From 624d4074ecad3209adf6d6c0ad4eb0b2ba46500d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 14 Apr 2021 14:30:11 +0200 Subject: [PATCH 001/821] Prepare next development iteration. See #2192 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dba4f5304d..aa268a6f17 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.5.0 + 2.6.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. From acf01b6c7140a56cd1c3caa67409b4d4f922937d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 14 Apr 2021 14:30:14 +0200 Subject: [PATCH 002/821] After release cleanups. See #2192 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index aa268a6f17..3755d72be0 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.5.0 + 2.6.0-SNAPSHOT @@ -26,7 +26,7 @@ 3.8.0 0.10.3 org.hibernate - 2.5.0 + 2.6.0-SNAPSHOT spring.data.jpa @@ -446,8 +446,8 @@ - spring-libs-release - https://repo.spring.io/libs-release + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 2ccd7c8280864b9cfc40e293aae4902325cf3ff6 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 15 Apr 2021 13:46:39 -0500 Subject: [PATCH 003/821] Migrate to main branch. See #2192. --- CI.adoc | 2 +- CONTRIBUTING.adoc | 2 +- Jenkinsfile | 10 +++++----- README.adoc | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CI.adoc b/CI.adoc index a9b227f192..ca9b406efe 100644 --- a/CI.adoc +++ b/CI.adoc @@ -1,6 +1,6 @@ = Continuous Integration -image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2Fmaster&subject=Moore%20(master)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] +image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2Fmain&subject=Moore%20(main)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2F2.1.x&subject=Lovelace%20(2.1.x)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2F1.11.x&subject=Ingalls%20(1.11.x)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index f007591467..740e8bd0bb 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -1,3 +1,3 @@ = Spring Data contribution guidelines -You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc[here]. +You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/main/CONTRIBUTING.adoc[here]. diff --git a/Jenkinsfile b/Jenkinsfile index f50adb2cc8..f2adf94a66 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,7 +3,7 @@ pipeline { triggers { pollSCM 'H/10 * * * *' - upstream(upstreamProjects: "spring-data-commons/master", threshold: hudson.model.Result.SUCCESS) + upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS) } options { @@ -15,7 +15,7 @@ pipeline { stage("test: baseline (jdk8)") { when { anyOf { - branch 'master' + branch 'main' not { triggeredBy 'UpstreamCause' } } } @@ -37,7 +37,7 @@ pipeline { stage("Test other configurations") { when { allOf { - branch 'master' + branch 'main' not { triggeredBy 'UpstreamCause' } } } @@ -79,7 +79,7 @@ pipeline { stage('Release to artifactory') { when { anyOf { - branch 'master' + branch 'main' not { triggeredBy 'UpstreamCause' } } } @@ -111,7 +111,7 @@ pipeline { } stage('Publish documentation') { when { - branch 'master' + branch 'main' } agent { label 'data' diff --git a/README.adoc b/README.adoc index e4d99c20e9..d1a568deb9 100644 --- a/README.adoc +++ b/README.adoc @@ -1,7 +1,7 @@ image:https://spring.io/badges/spring-data-jpa/ga.svg[Spring Data JPA,link=https://projects.spring.io/spring-data-jpa/#quick-start] image:https://spring.io/badges/spring-data-jpa/snapshot.svg[Spring Data JPA,link=https://projects.spring.io/spring-data-jpa/#quick-start] -= Spring Data JPA image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2Fmaster&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] += Spring Data JPA image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] Spring Data JPA, part of the larger https://projects.spring.io/spring-data[Spring Data] family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. From 9468e890dec374300ca3b66d5f0e389269623cfe Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 22 Apr 2021 11:02:45 +0200 Subject: [PATCH 004/821] Introduce template method for easier customization of fragments. We introduced getRepositoryFragments(RepositoryMetadata,EntityManager,EntityPathResolver,CrudMethodMetadata) to easier get hold of typical arguments required for customization of repository base fragments that aren't bound to a specific entity type such as QuerydslJpaPredicateExecutor. Closes #2202. --- .../support/JpaRepositoryFactory.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 50adefde93..2a1795bac8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository.support; +import static org.springframework.data.querydsl.QuerydslUtils.*; + import java.io.Serializable; import java.lang.reflect.Method; import java.util.Optional; @@ -23,6 +25,7 @@ import javax.persistence.EntityManager; import javax.persistence.Tuple; +import com.querydsl.core.types.EntityPath; import org.slf4j.Logger; import org.springframework.beans.factory.BeanFactory; @@ -45,9 +48,8 @@ import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.QueryCreationListener; -import org.springframework.data.repository.core.support.RepositoryComposition; +import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments; import org.springframework.data.repository.core.support.RepositoryFactorySupport; -import org.springframework.data.repository.core.support.RepositoryFragment; import org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; @@ -57,8 +59,6 @@ import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; -import static org.springframework.data.querydsl.QuerydslUtils.*; - /** * JPA specific generic repository factory. * @@ -234,9 +234,27 @@ public JpaEntityInformation getEntityInformation(Class domainC * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryFragments(org.springframework.data.repository.core.RepositoryMetadata) */ @Override - protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) { + protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) { + + return getRepositoryFragments(metadata, entityManager, entityPathResolver, + crudMethodMetadataPostProcessor.getCrudMethodMetadata()); + } - RepositoryComposition.RepositoryFragments fragments = RepositoryComposition.RepositoryFragments.empty(); + /** + * Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add JPA-specific extensions. Typically + * adds a {@link QuerydslJpaPredicateExecutor} if the repository interface uses Querydsl. + *

+ * Can be overridden by subclasses to customize {@link RepositoryFragments}. + * + * @param metadata repository metadata. + * @param entityManager the entity manager. + * @param resolver resolver to translate an plain domain class into a {@link EntityPath}. + * @param crudMethodMetadata metadata about the invoked CRUD methods. + * @return + * @since 2.5.1 + */ + protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata, EntityManager entityManager, + EntityPathResolver resolver, CrudMethodMetadata crudMethodMetadata) { boolean isQueryDslRepository = QUERY_DSL_PRESENT && QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface()); @@ -248,15 +266,11 @@ protected RepositoryComposition.RepositoryFragments getRepositoryFragments(Repos "Cannot combine Querydsl and reactive repository support in a single interface"); } - JpaEntityInformation entityInformation = getEntityInformation(metadata.getDomainType()); - - Object querydslFragment = getTargetRepositoryViaReflection(QuerydslJpaPredicateExecutor.class, entityInformation, - entityManager, entityPathResolver, crudMethodMetadataPostProcessor.getCrudMethodMetadata()); - - fragments = fragments.append(RepositoryFragment.implemented(querydslFragment)); + return RepositoryFragments.just(new QuerydslJpaPredicateExecutor<>(getEntityInformation(metadata.getDomainType()), + entityManager, resolver, crudMethodMetadata)); } - return fragments; + return RepositoryFragments.empty(); } private static boolean hasMethodReturningStream(Class repositoryClass) { From ea20270f24b06a78e02620d14413751f247fe0d2 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 22 Apr 2021 11:04:15 +0200 Subject: [PATCH 005/821] Polishing. Fix Javadoc references. See #2202. --- .../data/jpa/repository/support/JpaRepositoryFactory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 2a1795bac8..a10de113d0 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -140,8 +140,7 @@ public void setEscapeCharacter(EscapeCharacter escapeCharacter) { } /** - * Configures the {@link JpaQueryMethodFactory} to be used. Defaults to - * {@link JpaQueryMethod.DefaultJpaQueryMethodFactory#INSTANCE}. + * Configures the {@link JpaQueryMethodFactory} to be used. Defaults to {@link DefaultJpaQueryMethodFactory}. * * @param queryMethodFactory must not be {@literal null}. */ @@ -294,7 +293,7 @@ private static boolean hasMethodReturningStream(Class repositoryClass) { * * @author Oliver Gierke * @since 2.0.5 - * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 + * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 */ private static class EclipseLinkProjectionQueryCreationListener implements QueryCreationListener { From 68034e75bcb6e4f4eaa58c6aba38540a29e0e160 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 22 Apr 2021 12:19:28 -0500 Subject: [PATCH 006/821] Authenticate with artifactory. See #2192. --- Jenkinsfile | 19 +++-- LICENSE.txt | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++ pom.xml | 5 -- settings.xml | 29 ++++++++ 4 files changed, 245 insertions(+), 10 deletions(-) create mode 100644 LICENSE.txt create mode 100644 settings.xml diff --git a/Jenkinsfile b/Jenkinsfile index f2adf94a66..d1a864b649 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -23,11 +23,14 @@ pipeline { label 'data' } options { timeout(time: 30, unit: 'MINUTES') } + environment { + ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + } steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' } } } @@ -47,11 +50,14 @@ pipeline { label 'data' } options { timeout(time: 30, unit: 'MINUTES') } + environment { + ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + } steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { docker.image('adoptopenjdk/openjdk11:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pjava11 clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' } } } @@ -63,11 +69,14 @@ pipeline { label 'data' } options { timeout(time: 30, unit: 'MINUTES') } + environment { + ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + } steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { docker.image('adoptopenjdk/openjdk15:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pjava11 clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' } } } @@ -96,7 +105,7 @@ pipeline { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' + + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " + @@ -126,7 +135,7 @@ pipeline { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,distribute ' + + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..ff77379631 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/pom.xml b/pom.xml index 3755d72be0..148a2e73f5 100644 --- a/pom.xml +++ b/pom.xml @@ -460,11 +460,6 @@ spring-libs-milestone https://repo.spring.io/libs-milestone - - bintray-plugins - bintray-plugins - https://jcenter.bintray.com - diff --git a/settings.xml b/settings.xml new file mode 100644 index 0000000000..b3227cc110 --- /dev/null +++ b/settings.xml @@ -0,0 +1,29 @@ + + + + + spring-plugins-release + ${env.ARTIFACTORY_USR} + ${env.ARTIFACTORY_PSW} + + + spring-libs-snapshot + ${env.ARTIFACTORY_USR} + ${env.ARTIFACTORY_PSW} + + + spring-libs-milestone + ${env.ARTIFACTORY_USR} + ${env.ARTIFACTORY_PSW} + + + spring-libs-release + ${env.ARTIFACTORY_USR} + ${env.ARTIFACTORY_PSW} + + + + \ No newline at end of file From e7cac87c2e636a7ef9cc879c64c33e101daf060b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 29 Apr 2021 14:25:33 +0200 Subject: [PATCH 007/821] Provide anchors for fourth level sections. See #2129 --- src/main/asciidoc/faq.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/asciidoc/faq.adoc b/src/main/asciidoc/faq.adoc index a2056d484c..72bab22742 100644 --- a/src/main/asciidoc/faq.adoc +++ b/src/main/asciidoc/faq.adoc @@ -2,6 +2,7 @@ [appendix] = Frequently Asked Questions +[[faq.common]] == Common [qanda] @@ -21,6 +22,7 @@ I'd like to get more detailed logging information on what methods are called ins ---- +[[faq.infrastructure]] == Infrastructure [qanda] @@ -36,6 +38,7 @@ Currently I have implemented a repository layer based on `HibernateDaoSupport`. ---- ==== +[[faq.auditing]] == Auditing [qanda] From ad161f87c530936058ae7887f49b6189db4522dc Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 3 May 2021 08:58:23 +0200 Subject: [PATCH 008/821] Improve wording on transactional methods inherited from SimpleJpaRepository and repository fragments. Closes #2207 --- src/main/asciidoc/jpa.adoc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 718bb7c04d..b839eb31bd 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -869,7 +869,12 @@ include::query-by-example.adoc[leveloffset=+1] [[transactions]] == Transactionality -By default, CRUD methods on repository instances are transactional. For read operations, the transaction configuration `readOnly` flag is set to `true`. All others are configured with a plain `@Transactional` so that default transaction configuration applies. For details, see JavaDoc of link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/index.html?org/springframework/data/jpa/repository/support/SimpleJpaRepository.html$$[`SimpleJpaRepository`]. If you need to tweak transaction configuration for one of the methods declared in a repository, redeclare the method in your repository interface, as follows: +By default, CRUD methods on repository instances inherited from link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html$$[`SimpleJpaRepository`] are transactional. +For read operations, the transaction configuration `readOnly` flag is set to `true`. +All others are configured with a plain `@Transactional` so that default transaction configuration applies. +Repository methods that are backed by transactional repository fragments inherit the transactional attributes from the actual fragment method. + +If you need to tweak transaction configuration for one of the methods declared in a repository, redeclare the method in your repository interface, as follows: .Custom transaction configuration for CRUD ==== @@ -894,12 +899,11 @@ Another way to alter transactional behaviour is to use a facade or service imple [source, java] ---- @Service -class UserManagementImpl implements UserManagement { +public class UserManagementImpl implements UserManagement { private final UserRepository userRepository; private final RoleRepository roleRepository; - @Autowired public UserManagementImpl(UserRepository userRepository, RoleRepository roleRepository) { this.userRepository = userRepository; @@ -915,6 +919,7 @@ class UserManagementImpl implements UserManagement { user.addRole(role); userRepository.save(user); } + } } ---- This example causes call to `addRoleToAllUsers(…)` to run inside a transaction (participating in an existing one or creating a new one if none are already running). The transaction configuration at the repositories is then neglected, as the outer transaction configuration determines the actual one used. Note that you must activate `` or use `@EnableTransactionManagement` explicitly to get annotation-based configuration of facades to work. @@ -925,6 +930,7 @@ Note that the call to `save` is not strictly necessary from a JPA point of view, [[transactional-query-methods]] === Transactional query methods + To let your query methods be transactional, use `@Transactional` at the repository interface you define, as shown in the following example: .Using @Transactional at query methods @@ -932,7 +938,7 @@ To let your query methods be transactional, use `@Transactional` at the reposito [source, java] ---- @Transactional(readOnly = true) -public interface UserRepository extends JpaRepository { +interface UserRepository extends JpaRepository { List findByLastname(String lastname); From 1948f1a5e2b5ec05519d8e3029f23418c1b6db3d Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 7 May 2021 16:00:54 +0200 Subject: [PATCH 009/821] Adjust leveloffset for the JPA specific part. This moves the complete "JPA Repositories" section und "Reference Documentation". --- src/main/asciidoc/index.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index 8f0d07bc52..1a45e2757f 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -21,7 +21,7 @@ include::{spring-data-commons-docs}/repositories.adoc[leveloffset=+1] [[reference]] == Reference Documentation -include::jpa.adoc[leveloffset=+1] +include::jpa.adoc[leveloffset=+2] [[appendix]] == Appendix From 44239c30c9d7a80f797e3821d41f36c4c5c2b76e Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 13 May 2021 15:47:14 -0500 Subject: [PATCH 010/821] Update CI to JDK 16. See #2184. --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d1a864b649..0f34e477ba 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -64,7 +64,7 @@ pipeline { } } - stage("test: baseline (jdk15)") { + stage("test: baseline (jdk16)") { agent { label 'data' } @@ -75,7 +75,7 @@ pipeline { steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('adoptopenjdk/openjdk15:latest').inside('-v $HOME:/tmp/jenkins-home') { + docker.image('adoptopenjdk/openjdk16:latest').inside('-v $HOME:/tmp/jenkins-home') { sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pjava11 clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' } } From 8a0f0c27ab08ac9fb23a311134da56ee9d2d770e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 May 2021 11:51:46 +0200 Subject: [PATCH 011/821] Updated changelog. See #2197 --- src/main/resources/changelog.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index ba159c3e39..fa399111db 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,11 @@ Spring Data JPA Changelog ========================= +Changes in version 2.4.9 (2021-05-14) +------------------------------------- +* #2207 - Improve wording on transactional methods inherited from `SimpleJpaRepository` and repository fragments. + + Changes in version 2.5.0 (2021-04-14) ------------------------------------- * #2195 - Upgrade dependencies. @@ -2275,5 +2280,6 @@ Changes in version 1.0.0.M1 (2011-02-10) - https://jira.springsource.org/browse/ + From da9eddd83ffa74258a3d76306c7e5a63718a14ed Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 May 2021 12:23:15 +0200 Subject: [PATCH 012/821] Updated changelog. See #2199 --- src/main/resources/changelog.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index fa399111db..c620c8e9da 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,13 @@ Spring Data JPA Changelog ========================= +Changes in version 2.5.1 (2021-05-14) +------------------------------------- +* #2207 - Improve wording on transactional methods inherited from `SimpleJpaRepository` and repository fragments. +* #2202 - Introduce template method for easier customization of fragments. +* #2129 - Missing anchor on "Custom Namespace Attributes" causes TOC to break. + + Changes in version 2.4.9 (2021-05-14) ------------------------------------- * #2207 - Improve wording on transactional methods inherited from `SimpleJpaRepository` and repository fragments. @@ -2281,5 +2288,6 @@ Changes in version 1.0.0.M1 (2011-02-10) - https://jira.springsource.org/browse/ + From 087b61024b3d0d56f548b9796526f69eb1722074 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 8 Jun 2021 14:33:05 +0200 Subject: [PATCH 013/821] Remove AspectJ dependency. The dependency was seemingly introduced as a workaround to a Maven problem before this project even had the name Spring Data JPA. No problems are noticeable when building without the dependency or when using it in a simple Spring Boot app without it. Closes #2218 --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index 148a2e73f5..aefb6b09e1 100644 --- a/pom.xml +++ b/pom.xml @@ -165,11 +165,6 @@ - - org.aspectj - aspectjrt - ${aspectj} - org.aspectj aspectjweaver From aa10c46c7b8d5e7226a61739dea6f2be97546080 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 9 Jun 2021 10:59:05 +0200 Subject: [PATCH 014/821] Fix documenation of entity state-detection strategies. Closes #2230 --- src/main/asciidoc/jpa.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index b839eb31bd..f6f917e58b 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -150,7 +150,7 @@ Spring Data JPA offers the following strategies to detect whether an entity is n 2. Implementing `Persistable`: If an entity implements `Persistable`, Spring Data JPA delegates the new detection to the `isNew(…)` method of the entity. See the link:$$https://docs.spring.io/spring-data/data-commons/docs/current/api/index.html?org/springframework/data/domain/Persistable.html$$[JavaDoc] for details. 3. Implementing `EntityInformation`: You can customize the `EntityInformation` abstraction used in the `SimpleJpaRepository` implementation by creating a subclass of `JpaRepositoryFactory` and overriding the `getEntityInformation(…)` method accordingly. You then have to register the custom implementation of `JpaRepositoryFactory` as a Spring bean. Note that this should be rarely necessary. See the link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/index.html?org/springframework/data/jpa/repository/support/JpaRepositoryFactory.html$$[JavaDoc] for details. -Option 1 is not an option for entities that use manually assigned identifiers as with those the identifier will always be non-`null`. +Option 1 is not an option for entities that use manually assigned identifiers and no version attribute as with those the identifier will always be non-`null`. A common pattern in that scenario is to use a common base class with a transient flag defaulting to indicate a new instance and using JPA lifecycle callbacks to flip that flag on persistence operations: .A base class for entities with manually assigned identifiers From 39e72af2d57d4093c42a89703ca13d4a54b39082 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 9 Jun 2021 10:11:34 +0200 Subject: [PATCH 015/821] Fix build failures with Java 16. A few test that used reflection to access the actual SQL executed by a Query object failed with `java.lang.reflect.InaccessibleObjectException: Unable to make field protected java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.h accessible: module java.base does not "opens java.lang.reflect" to unnamed module @1b9e1916`. This change replaces the reflection used by a proper call to `unwrap`. Original pull request #2231 --- .../query/PartTreeJpaQueryIntegrationTests.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 336ca0d0a6..69afb6a598 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -33,10 +33,10 @@ import javax.persistence.TemporalType; import org.hibernate.Version; +import org.hibernate.query.internal.QueryImpl; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -107,11 +107,11 @@ void recreatesQueryIfNullValueIsGiven() throws Exception { Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) }))); - assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).endsWith("firstname=:param0"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).endsWith("firstname=:param0"); query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { null, PageRequest.of(0, 1) }))); - assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).endsWith("firstname is null"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).endsWith("firstname is null"); } @Test // DATAJPA-920 @@ -133,7 +133,7 @@ void shouldSelectAliasedIdForExistsProjectionQueries() throws Exception { Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews" }))); - assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).contains(".id from User as"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).contains(".id from User as"); } @Test // DATAJPA-1074 @@ -144,7 +144,7 @@ void isEmptyCollection() throws Exception { Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] {}))); - assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).endsWith("roles is empty"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).endsWith("roles is empty"); } @Test // DATAJPA-1074 @@ -155,7 +155,7 @@ void isNotEmptyCollection() throws Exception { Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] {}))); - assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY))).endsWith("roles is not empty"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).endsWith("roles is not empty"); } @Test // DATAJPA-1074 From 5a00192ecdbc3f2acd026c5c735dfe1b3f5025ac Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Tue, 8 Jun 2021 13:32:36 +0200 Subject: [PATCH 016/821] Escape JDBC styled parameters. Closes #2228 Original pull request #2229 --- .../query/ExpressionBasedStringQuery.java | 10 +++++----- .../ExpressionBasedStringQueryUnitTests.java | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index 5cca2d2bf9..a54030589e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -35,15 +35,15 @@ * @author Thomas Darimont * @author Oliver Gierke * @author Tom Hombergs + * @author Michael J. Simons */ class ExpressionBasedStringQuery extends StringQuery { - private static final String EXPRESSION_PARAMETER = "?#{"; - private static final String QUOTED_EXPRESSION_PARAMETER = "?__HASH__{"; + private static final String EXPRESSION_PARAMETER = "$1#{"; + private static final String QUOTED_EXPRESSION_PARAMETER = "$1__HASH__{"; - private static final Pattern EXPRESSION_PARAMETER_QUOTING = Pattern.compile(Pattern.quote(EXPRESSION_PARAMETER)); - private static final Pattern EXPRESSION_PARAMETER_UNQUOTING = Pattern.compile(Pattern - .quote(QUOTED_EXPRESSION_PARAMETER)); + private static final Pattern EXPRESSION_PARAMETER_QUOTING = Pattern.compile("([:\\?])#\\{"); + private static final Pattern EXPRESSION_PARAMETER_UNQUOTING = Pattern.compile("([:\\?])__HASH__\\{"); private static final String ENTITY_NAME = "entityName"; private static final String ENTITY_NAME_VARIABLE = "#" + ENTITY_NAME; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index d5db6d4dcc..a757fda895 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -34,6 +34,7 @@ * @author Oliver Gierke * @author Jens Schauder * @author Mark Paluch + * @author Michael J. Simons */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -66,7 +67,7 @@ void renderAliasInExpressionQueryCorrectly() { void shouldDetectBindParameterCountCorrectly() { StringQuery query = new ExpressionBasedStringQuery( - "select n from NetworkServer n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',:#{#networkRequest.name},'%')), '')) OR :#{#networkRequest.name} IS NULL )\"\n" + "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',:#{#networkRequest.name},'%')), '')) OR :#{#networkRequest.name} IS NULL )\"\n" + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',:#{#networkRequest.server},'%')), '')) OR :#{#networkRequest.server} IS NULL)\"\n" + "+ \"AND (n.createdAt >= :#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=:#{#networkRequest.createdTime.endDateTime})\"\n" + "+ \"AND (n.updatedAt >= :#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=:#{#networkRequest.updatedTime.endDateTime})", @@ -75,4 +76,17 @@ void shouldDetectBindParameterCountCorrectly() { assertThat(query.getParameterBindings()).hasSize(8); } + @Test // GH-2228 + void shouldDetectBindParameterCountCorrectlyWithJDBCStyleParameters() { + + StringQuery query = new ExpressionBasedStringQuery( + "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )\"\n" + + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)\"\n" + + "+ \"AND (n.createdAt >= ?#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=?#{#networkRequest.createdTime.endDateTime})\"\n" + + "+ \"AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", + metadata, SPEL_PARSER); + + assertThat(query.getParameterBindings()).hasSize(8); + } + } From dec2a437369989738c6ed939e73475b13830057a Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Tue, 8 Jun 2021 13:37:21 +0200 Subject: [PATCH 017/821] Polishing. Original pull request #2229 See #2228 --- .../data/jpa/repository/query/ExpressionBasedStringQuery.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index a54030589e..b907fdbf47 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -42,8 +42,8 @@ class ExpressionBasedStringQuery extends StringQuery { private static final String EXPRESSION_PARAMETER = "$1#{"; private static final String QUOTED_EXPRESSION_PARAMETER = "$1__HASH__{"; - private static final Pattern EXPRESSION_PARAMETER_QUOTING = Pattern.compile("([:\\?])#\\{"); - private static final Pattern EXPRESSION_PARAMETER_UNQUOTING = Pattern.compile("([:\\?])__HASH__\\{"); + private static final Pattern EXPRESSION_PARAMETER_QUOTING = Pattern.compile("([:?])#\\{"); + private static final Pattern EXPRESSION_PARAMETER_UNQUOTING = Pattern.compile("([:?])__HASH__\\{"); private static final String ENTITY_NAME = "entityName"; private static final String ENTITY_NAME_VARIABLE = "#" + ENTITY_NAME; From 316d0ce7e46de135f436cb95f1a6c4010d2337e9 Mon Sep 17 00:00:00 2001 From: Manuel Doninger Date: Tue, 15 Jun 2021 10:56:35 +0200 Subject: [PATCH 018/821] Remove superfluous @Repository annotation from examples. Remove the @Repository annotation from interfaces in the examples, since it is not necessary on Spring Data interfaces (and creates a warning log anyway). Closes #2234 Original pull request #2235 --- src/main/asciidoc/jpa.adoc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index f6f917e58b..1384ba825d 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -655,7 +655,6 @@ The following example shows how to reference a named entity graph on a repositor ==== [source, java] ---- -@Repository public interface GroupRepository extends CrudRepository { @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD) @@ -671,7 +670,6 @@ It is also possible to define ad hoc entity graphs by using `@EntityGraph`. The ==== [source, java] ---- -@Repository public interface GroupRepository extends CrudRepository { @EntityGraph(attributePaths = { "members" }) From dfdb528458642c01b630bc53db8c2d6d7de4e2d3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 17 Jun 2021 08:59:09 +0200 Subject: [PATCH 019/821] Use Spring nullability annotations instead of JSR305. Closes #2239. --- .../data/jpa/repository/query/NativeJpaQuery.java | 2 +- .../jpa/repository/support/QuerydslRepositorySupport.java | 2 +- .../data/jpa/repository/UserRepositoryTests.java | 6 +++--- .../data/jpa/repository/sample/UserRepository.java | 3 +-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index 54fd9b3141..da0d718976 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -15,7 +15,6 @@ */ package org.springframework.data.jpa.repository.query; -import javax.annotation.Nullable; import javax.persistence.EntityManager; import javax.persistence.Query; import javax.persistence.Tuple; @@ -25,6 +24,7 @@ import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ReturnedType; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; /** * {@link RepositoryQuery} implementation that inspects a {@link org.springframework.data.repository.query.QueryMethod} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java index 8b106cf134..a326470ee4 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.repository.support; -import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Repository; import org.springframework.util.Assert; diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index ce28bbfd3d..26ea26416e 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -30,6 +30,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Stream; @@ -46,6 +47,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; @@ -59,7 +61,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; -import org.springframework.data.domain.ExampleMatcher.*; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; @@ -67,14 +68,13 @@ import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; -import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.jpa.repository.sample.SampleEvaluationContextExtension.SampleSecurityContextHolder; +import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.jpa.repository.sample.UserRepository.NameOnly; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; -import com.google.common.base.Optional; /** * Base integration test class for {@code UserRepository}. Loads a basic (non-namespace) Spring configuration file as diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index fe90d7f9ad..ab03b79675 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Stream; @@ -42,8 +43,6 @@ import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; -import com.google.common.base.Optional; - /** * Repository interface for {@code User}s. * From fcc1d8b317fad011c077eede64e159ab738cc390 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 16 Jun 2021 09:19:12 +0200 Subject: [PATCH 020/821] Add a note that DTO projections are not supported with native queries. Closes #2009 Related ticket: spring-projects/spring-data-commons#2382 Original pull request: #2237 --- src/main/asciidoc/jpa.adoc | 3 +++ src/main/asciidoc/repository-projections-dto-limitations.adoc | 1 + 2 files changed, 4 insertions(+) create mode 100644 src/main/asciidoc/repository-projections-dto-limitations.adoc diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 1384ba825d..d30fbfc902 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -680,6 +680,9 @@ public interface GroupRepository extends CrudRepository { ==== +:repository-projections-dto-limitations-file: ../../../../spring-data-jpa/src/main/asciidoc/repository-projections-dto-limitations.adoc + + include::{spring-data-commons-docs}/repository-projections.adoc[leveloffset=+2] [[jpa.stored-procedures]] diff --git a/src/main/asciidoc/repository-projections-dto-limitations.adoc b/src/main/asciidoc/repository-projections-dto-limitations.adoc new file mode 100644 index 0000000000..44ab3a6bbf --- /dev/null +++ b/src/main/asciidoc/repository-projections-dto-limitations.adoc @@ -0,0 +1 @@ +NOTE: Class based projections do not work with native queries. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] From bf8e8a1eacb814f32673a67d59dfc196f5aaf0c7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 17 Jun 2021 12:01:10 +0200 Subject: [PATCH 021/821] Polishing. Update placeholder name to reflect its position instead of the content that is being added by using modules. Original pull request: #2237. See #2009 --- src/main/asciidoc/jpa.adoc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index d30fbfc902..1abb6f8d7c 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -679,9 +679,7 @@ public interface GroupRepository extends CrudRepository { ---- ==== - -:repository-projections-dto-limitations-file: ../../../../spring-data-jpa/src/main/asciidoc/repository-projections-dto-limitations.adoc - +:repository-projections-trailing-dto-fragment: ../../../../spring-data-jpa/src/main/asciidoc/repository-projections-dto-limitations.adoc include::{spring-data-commons-docs}/repository-projections.adoc[leveloffset=+2] From 1c79f5f0d9c1b43231a1444bf96993d4b88a0882 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 21 Jun 2021 17:50:00 +0200 Subject: [PATCH 022/821] Upgrade EclipseLink to 2.7.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aefb6b09e1..ad4d9e359e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ DATAJPA - 2.7.8 + 2.7.9 5.4.29.Final 3.8.0 0.10.3 From 92bbf1ac6f3c74d9205f001fb4fa5dd4ff2720ec Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 21 Jun 2021 17:51:28 +0200 Subject: [PATCH 023/821] Upgrade Mockito to 3.11.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad4d9e359e..969e6dc820 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 2.7.9 5.4.29.Final - 3.8.0 + 3.11.1 0.10.3 org.hibernate 2.6.0-SNAPSHOT From 26146eeede78e725e324eb85b1b007c1e4bd9ab6 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 21 Jun 2021 17:50:47 +0200 Subject: [PATCH 024/821] Upgrade Hibernate to 5.5.2.Final --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 969e6dc820..2c96e422ab 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ DATAJPA 2.7.9 - 5.4.29.Final + 5.5.2.Final 3.11.1 0.10.3 org.hibernate From e2e50ac63a22366759df4765b87d407fa3ca51a5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Jun 2021 15:17:50 +0200 Subject: [PATCH 025/821] Updated changelog. See #2214 --- src/main/resources/changelog.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index c620c8e9da..6dc160b323 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,14 @@ Spring Data JPA Changelog ========================= +Changes in version 2.4.10 (2021-06-22) +-------------------------------------- +* #2234 - Spring Data documentation examples have wrong annotations. +* #2230 - Error in documentation regarding manually assigned identifiers in "Entity State-detection Strategies" when there is a Version property. +* #2228 - ExpressionBasedStringQuery does not quote all parameter expressions. +* #2009 - Document that DTOs don't work with native queries [DATAJPA-1714]. + + Changes in version 2.5.1 (2021-05-14) ------------------------------------- * #2207 - Improve wording on transactional methods inherited from `SimpleJpaRepository` and repository fragments. @@ -2289,5 +2297,6 @@ Changes in version 1.0.0.M1 (2011-02-10) - https://jira.springsource.org/browse/ + From cde430e9fb23ecfca4cd41adf0512d67347f532e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Jun 2021 15:51:32 +0200 Subject: [PATCH 026/821] Updated changelog. See #2215 --- src/main/resources/changelog.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index 6dc160b323..8e7924fabd 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,14 @@ Spring Data JPA Changelog ========================= +Changes in version 2.5.2 (2021-06-22) +------------------------------------- +* #2234 - Spring Data documentation examples have wrong annotations. +* #2230 - Error in documentation regarding manually assigned identifiers in "Entity State-detection Strategies" when there is a Version property. +* #2228 - ExpressionBasedStringQuery does not quote all parameter expressions. +* #2009 - Document that DTOs don't work with native queries [DATAJPA-1714]. + + Changes in version 2.4.10 (2021-06-22) -------------------------------------- * #2234 - Spring Data documentation examples have wrong annotations. @@ -2298,5 +2306,6 @@ Changes in version 1.0.0.M1 (2011-02-10) - https://jira.springsource.org/browse/ + From 5754fa5d23b6923418d2109a52cff8772199c1e9 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 22 Jun 2021 16:33:58 +0200 Subject: [PATCH 027/821] Remove explicit version for Mockito, so we use the one of Spring Data Build. Closes #2247 See https://github.com/spring-projects/spring-data-build/issues/1469 --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2c96e422ab..b622598385 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,6 @@ 2.7.9 5.5.2.Final - 3.11.1 0.10.3 org.hibernate 2.6.0-SNAPSHOT @@ -238,7 +237,7 @@ ${querydsl} true - + org.jmolecules.integrations From 05cce6d1359f07b39224b0f17a11162d6e8e2713 Mon Sep 17 00:00:00 2001 From: DaniBa1 Date: Thu, 1 Jul 2021 16:59:09 +0200 Subject: [PATCH 028/821] Fixing small mistake in documentation Changed a wrong description. Closes #2251 --- .../org/springframework/data/jpa/repository/JpaRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index 7e09ff137f..cbc36e210b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -81,7 +81,7 @@ public interface JpaRepository extends PagingAndSortingRepository, /** * Saves all entities and flushes changes instantly. * - * @param entities entities to be deleted. Must not be {@literal null}. + * @param entities entities to be saved. Must not be {@literal null}. * @return the saved entities * @since 2.5 */ From 2e30a353c97c534910edce003f2a15bb2f478809 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 2 Jul 2021 14:36:54 -0500 Subject: [PATCH 029/821] Drop serialVersionUID from AbstractPersistable. Considering it's not serializable, there is no need to maintain serialVersionUID. See #2245. --- .../springframework/data/jpa/domain/AbstractPersistable.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java b/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java index f9ec3778b4..31bd12e93e 100644 --- a/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java +++ b/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java @@ -33,13 +33,12 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Mark Paluch + * @author Greg Turnquist * @param the type of the identifier. */ @MappedSuperclass public abstract class AbstractPersistable implements Persistable { - private static final long serialVersionUID = -5554308939380869754L; - @Id @GeneratedValue private @Nullable PK id; /* From 554bd3d6edb75d87a4a425f5c0515e9f81269c15 Mon Sep 17 00:00:00 2001 From: Gabriel Basilio Brito Date: Tue, 14 Jan 2020 15:30:15 -0400 Subject: [PATCH 030/821] Add REF_CURSOR support for stored procedures. When registered procedure output parameters, if the method annotated with @Procedure has a collection return type or an entity return type, then use ResultSet. Otherwise, handle either the array (Object[]) or the primitive types. * Throw a proper exception when using an @Procedure-annotated method and no transaction is active. * Introduce refCursor as a boolean flag to @Procedure denoting when to use REF_CURSORs for OUT parameters.. * Extract a ProcedureParameter command object to ease usage of a parameter's name, type and mode. (NOTE: javax.persistence already has "StoredProcedureParameter".) NOTE: Integration testing of stored procedures is very limited by lack of HSQL's support for REF CURSORS. See ##2256 to track future work for potential testing against MySQL, Oracle, Postgres, or SQL Server. See: #1959, #409. Related: #2014. --- .../repository/query/JpaQueryExecution.java | 22 +- .../data/jpa/repository/query/Procedure.java | 6 + .../repository/query/ProcedureParameter.java | 52 +++++ .../query/StoredProcedureAttributeSource.java | 27 ++- .../query/StoredProcedureAttributes.java | 71 +++--- .../query/StoredProcedureJpaQuery.java | 85 +++---- .../support/JpaRepositoryFactory.java | 14 +- .../StoredProcedureIntegrationTests.java | 3 +- ...oredProcedureAttributeSourceUnitTests.java | 209 +++++++++++++++--- .../StoredProcedureAttributesUnitTests.java | 7 +- 10 files changed, 364 insertions(+), 132 deletions(-) create mode 100644 src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index ed7473ecbe..ac09f871d8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -53,6 +53,7 @@ * @author Christoph Strobl * @author Nicolas Cirigliano * @author Jens Schauder + * @author Gabriel Basilio */ public abstract class JpaQueryExecution { @@ -301,6 +302,8 @@ protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccesso */ static class ProcedureExecution extends JpaQueryExecution { + private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a @Procedure method without a surrounding transaction that keeps the connection open so that the ResultSet can actually be consumed. Make sure the consumer code uses @Transactional or any other way of declaring a (read-only) transaction."; + /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) @@ -312,7 +315,24 @@ protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAcce StoredProcedureJpaQuery storedProcedureJpaQuery = (StoredProcedureJpaQuery) jpaQuery; StoredProcedureQuery storedProcedure = storedProcedureJpaQuery.createQuery(accessor); - storedProcedure.execute(); + + boolean returnsResultSet = storedProcedure.execute(); + + if (returnsResultSet) { + if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) + throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); + + List result = storedProcedure.getResultList(); + + if (!storedProcedureJpaQuery.getQueryMethod().isCollectionQuery()) { + if (result.isEmpty()) + return null; + if (result.size() == 1) + return result.get(0); + } + + return result; + } return storedProcedureJpaQuery.extractOutputValue(storedProcedure); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java b/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java index e60a6c6e1d..13007cc25a 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java @@ -26,6 +26,7 @@ * @author Thomas Darimont * @author Oliver Gierke * @author Christoph Strobl + * @author Gabriel Basilio * @since 1.6 */ @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @@ -51,4 +52,9 @@ * The name of the outputParameter, defaults to {@code ""}. */ String outputParameterName() default ""; + + /** + * Whether the procedure returns a Ref Cursor from the database {@code false}. + */ + boolean refCursor() default false; } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java b/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java new file mode 100644 index 0000000000..6d185e5025 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.jpa.repository.query; + +import org.springframework.lang.Nullable; + +import javax.persistence.ParameterMode; + +/** + * This class represents a Stored Procedure Parameter + * and an instance of the annotation {@link javax.persistence.StoredProcedureParameter}. + * + * @author Gabriel Basilio + */ +public class ProcedureParameter { + + private final String name; + private final ParameterMode mode; + private final Class type; + + public ProcedureParameter(@Nullable String name, ParameterMode mode, Class type) { + this.name = name; + this.mode = mode; + this.type = type; + } + + public String getName() { + return name; + } + + public ParameterMode getMode() { + return mode; + } + + public Class getType() { + return type; + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java index 34630e7efc..0160ad65e2 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java @@ -22,6 +22,7 @@ import javax.persistence.NamedStoredProcedureQueries; import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.ParameterMode; import javax.persistence.StoredProcedureParameter; import org.springframework.core.annotation.AnnotatedElementUtils; @@ -38,6 +39,7 @@ * @author Mark Paluch * @author Diego Diez * @author Jeff Sheets + * @author Gabriel Basilio * @since 1.6 */ enum StoredProcedureAttributeSource { @@ -72,7 +74,7 @@ public StoredProcedureAttributes createFrom(Method method, JpaEntityMetadata + method); } - return new StoredProcedureAttributes(procedureName, procedure.outputParameterName(), method.getReturnType()); + return new StoredProcedureAttributes(procedureName, createOutputProcedureParameterFrom(method, procedure)); } /** @@ -102,28 +104,29 @@ private String deriveProcedureNameFrom(Method method, Procedure procedure) { private StoredProcedureAttributes newProcedureAttributesFrom(Method method, NamedStoredProcedureQuery namedStoredProc, Procedure procedure) { - List outputParameterNames = new ArrayList<>(); - List> outputParameterTypes = new ArrayList<>(); + List outputParameters = new ArrayList<>(); if (!procedure.outputParameterName().isEmpty()) { // we give the output parameter definition from the @Procedure annotation precedence - outputParameterNames.add(procedure.outputParameterName()); + outputParameters.add(createOutputProcedureParameterFrom(method, procedure)); } else { // try to discover the output parameter - List outputParameters = extractOutputParametersFrom(namedStoredProc); + List namedProcedureOutputParameters = extractOutputParametersFrom(namedStoredProc); - for (StoredProcedureParameter outputParameter : outputParameters) { - outputParameterNames.add(outputParameter.name()); - outputParameterTypes.add(outputParameter.type()); + for (StoredProcedureParameter outputParameter : namedProcedureOutputParameters) { + outputParameters.add(new ProcedureParameter( + outputParameter.name(), outputParameter.mode(), outputParameter.type())); } } - if (outputParameterTypes.isEmpty()) { - outputParameterTypes.add(method.getReturnType()); - } + return new StoredProcedureAttributes(namedStoredProc.name(), outputParameters, true); + } - return new StoredProcedureAttributes(namedStoredProc.name(), outputParameterNames, outputParameterTypes, true); + private ProcedureParameter createOutputProcedureParameterFrom(Method method, Procedure procedure) { + return new ProcedureParameter(procedure.outputParameterName(), + procedure.refCursor() ? ParameterMode.REF_CURSOR : ParameterMode.OUT, + method.getReturnType()); } private List extractOutputParametersFrom(NamedStoredProcedureQuery namedStoredProc) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java index fbc891cb73..7c885fc135 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java @@ -33,6 +33,7 @@ * @author Mark Paluch * @author Jeff Sheets * @author Jens Schauder + * @author Gabriel Basilio * @since 1.6 */ class StoredProcedureAttributes { @@ -42,52 +43,51 @@ class StoredProcedureAttributes { private final boolean namedStoredProcedure; private final String procedureName; - private final List outputParameterNames; - private final List> outputParameterTypes; + private final List outputProcedureParameters; /** * Creates a new {@link StoredProcedureAttributes}. * * @param procedureName must not be {@literal null}. - * @param outputParameterName may be {@literal null}. - * @param outputParameterType must not be {@literal null}. */ - StoredProcedureAttributes(String procedureName, @Nullable String outputParameterName, - Class outputParameterType) { - this(procedureName, Collections.singletonList(outputParameterName), Collections.singletonList(outputParameterType), false); + StoredProcedureAttributes(String procedureName, ProcedureParameter parameter) { + this(procedureName, Collections.singletonList(parameter), false); } /** * Creates a new {@link StoredProcedureAttributes}. * * @param procedureName must not be {@literal null}. - * @param outputParameterNames may be empty, but not {@literal null}. - * @param outputParameterTypes must not be empty, and cannot be a single element of {@literal null}. * @param namedStoredProcedure flag signaling if the stored procedure has a name. */ - StoredProcedureAttributes(String procedureName, List outputParameterNames, - List> outputParameterTypes, boolean namedStoredProcedure) { + StoredProcedureAttributes(String procedureName, List outputProcedureParameters, boolean namedStoredProcedure) { Assert.notNull(procedureName, "ProcedureName must not be null!"); - Assert.notNull(outputParameterNames, "OutputParameterNames must not be null!"); - Assert.notEmpty(outputParameterTypes, "OutputParameterTypes must not be empty!"); - Assert.isTrue(outputParameterTypes.size() != 1 || outputParameterTypes.get(0) != null, "OutputParameterTypes must not have size 1 with a null value"); + Assert.notNull(outputProcedureParameters, "OutputProcedureParameters must not be null!"); + Assert.isTrue(outputProcedureParameters.size() != 1 || outputProcedureParameters.get(0) != null, "ProcedureParameters must not have size 1 with a null value"); this.procedureName = procedureName; - this.outputParameterNames = namedStoredProcedure - ? outputParameterNames - : completeOutputParameterNames(outputParameterNames); - this.outputParameterTypes = outputParameterTypes; this.namedStoredProcedure = namedStoredProcedure; - } - private List completeOutputParameterNames(List outputParameterNames) { + if (namedStoredProcedure) { + this.outputProcedureParameters = outputProcedureParameters; + } else { + this.outputProcedureParameters = getParametersWithCompletedNames(outputProcedureParameters); + } + } - return IntStream.range(0, outputParameterNames.size()) // - .mapToObj(i -> completeOutputParameterName(i, outputParameterNames.get(i))) // + private List getParametersWithCompletedNames(List procedureParameters) { + return IntStream.range(0, procedureParameters.size()) + .mapToObj(i -> getParameterWithCompletedName(procedureParameters.get(i), i)) .collect(Collectors.toList()); } + private ProcedureParameter getParameterWithCompletedName(ProcedureParameter parameter, int i) { + return new ProcedureParameter( + completeOutputParameterName(i, parameter.getName()), + parameter.getMode(), parameter.getType()); + } + private String completeOutputParameterName(int i, String paramName) { return StringUtils.hasText(paramName) // @@ -113,26 +113,15 @@ public String getProcedureName() { * * @return */ - public List getOutputParameterNames() { - return outputParameterNames; - } - - /** - * Returns the types of the output parameters. - * - * @return - */ - public List> getOutputParameterTypes() { - return outputParameterTypes; + public boolean isNamedStoredProcedure() { + return namedStoredProcedure; } /** - * Returns whether the stored procedure is a named one. - * - * @return + * @return Returns the stored procedure output parameter list */ - public boolean isNamedStoredProcedure() { - return namedStoredProcedure; + public List getOutputProcedureParameters() { + return outputProcedureParameters; } /** @@ -141,6 +130,10 @@ public boolean isNamedStoredProcedure() { * @return */ public boolean hasReturnValue() { - return !(outputParameterTypes.size() == 1 && (void.class.equals(outputParameterTypes.get(0)) || Void.class.equals(outputParameterTypes.get(0)))); + if (getOutputProcedureParameters().isEmpty()) + return false; + + Class outputType = getOutputProcedureParameters().get(0).getType(); + return !(void.class.equals(outputType) || Void.class.equals(outputType)); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index 0839fd0b7d..caacc7adcc 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -23,6 +23,7 @@ import javax.persistence.EntityManager; import javax.persistence.NamedStoredProcedureQuery; import javax.persistence.ParameterMode; +import javax.persistence.StoredProcedureParameter; import javax.persistence.StoredProcedureQuery; import javax.persistence.TypedQuery; @@ -132,42 +133,32 @@ Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) { return null; } - Map outputValues = new HashMap<>(); - List parameterNames = procedureAttributes.getOutputParameterNames(); - - for (int i = 0; i < parameterNames.size(); i++) { + List outputParameters = procedureAttributes.getOutputProcedureParameters(); - String name = parameterNames.get(i); - outputValues.put(name, extractOutputParameter(storedProcedureQuery, i)); + if (outputParameters.size() == 1) { + return extractOutputParameterValue(outputParameters.get(0), 0, storedProcedureQuery); } - return outputValues.size() == 1 ? outputValues.values().iterator().next() : outputValues; - } - - private Object extractOutputParameter(StoredProcedureQuery storedProcedureQuery, Integer index) { + Map outputValues = new HashMap<>(); - String outputParameterName = procedureAttributes.getOutputParameterNames().get(index); - JpaParameters parameters = getQueryMethod().getParameters(); + for (int i = 0; i < outputParameters.size(); i++) { + ProcedureParameter outputParameter = outputParameters.get(i); + outputValues.put(outputParameter.getName(), extractOutputParameterValue(outputParameter, i, storedProcedureQuery)); + } - return extractOutputParameterValue(storedProcedureQuery, outputParameterName, index, - parameters.getNumberOfParameters()); + return outputValues; } /** - * extract the value of an output parameter either by name or by index. - * - * @param storedProcedureQuery the query object of the stored procedure. - * @param name the name of the output parameter - * @param index index of the output parameter - * @param offset for index based access the index after which to find the output parameter values - * @return the value + * @return The value of an output parameter either by name or by index. */ - private Object extractOutputParameterValue(StoredProcedureQuery storedProcedureQuery, String name, Integer index, - int offset) { + private Object extractOutputParameterValue(ProcedureParameter outputParameter, Integer index, StoredProcedureQuery storedProcedureQuery) { + + JpaParameters methodParameters = getQueryMethod().getParameters(); - return useNamedParameters && StringUtils.hasText(name) ? // - storedProcedureQuery.getOutputParameterValue(name) - : storedProcedureQuery.getOutputParameterValue(offset + index + 1); + return useNamedParameters && StringUtils.hasText(outputParameter.getName()) ? + storedProcedureQuery.getOutputParameterValue(outputParameter.getName()) + : storedProcedureQuery.getOutputParameterValue(methodParameters.getNumberOfParameters() + index + 1); } /** @@ -192,9 +183,7 @@ private StoredProcedureQuery newNamedStoredProcedureQuery() { private StoredProcedureQuery newAdhocStoredProcedureQuery() { JpaParameters params = getQueryMethod().getParameters(); - String procedureName = procedureAttributes.getProcedureName(); - - StoredProcedureQuery procedureQuery = getEntityManager().createStoredProcedureQuery(procedureName); + StoredProcedureQuery procedureQuery = createAdhocStoredProcedureQuery(); for (JpaParameter param : params) { @@ -214,23 +203,41 @@ private StoredProcedureQuery newAdhocStoredProcedureQuery() { if (procedureAttributes.hasReturnValue()) { - ParameterMode mode = ParameterMode.OUT; + ProcedureParameter procedureOutput = procedureAttributes.getOutputProcedureParameters().get(0); - IntStream.range(0, procedureAttributes.getOutputParameterTypes().size()).forEach(i -> { - Class outputParameterType = procedureAttributes.getOutputParameterTypes().get(i); + /* If the stored procedure returns a ResultSet without using REF_CURSOR, + it is not necessary to declare an output parameter */ + if ((isResultSetProcedure() && procedureOutput.getMode() == ParameterMode.REF_CURSOR) || !isResultSetProcedure()) { if (useNamedParameters) { - - String outputParameterName = procedureAttributes.getOutputParameterNames().get(i); - procedureQuery.registerStoredProcedureParameter(outputParameterName, outputParameterType, mode); - + procedureQuery.registerStoredProcedureParameter(procedureOutput.getName(), procedureOutput.getType(), procedureOutput.getMode()); } else { - procedureQuery.registerStoredProcedureParameter(params.getNumberOfParameters() + i + 1, outputParameterType, - mode); + // Output parameter should be after the input parameters + int outputParameterIndex = params.getNumberOfParameters() + 1; + procedureQuery.registerStoredProcedureParameter(outputParameterIndex, procedureOutput.getType(), + procedureOutput.getMode()); } - }); + } } return procedureQuery; } + + private StoredProcedureQuery createAdhocStoredProcedureQuery() { + String procedureName = procedureAttributes.getProcedureName(); + + if (getQueryMethod().isQueryForEntity()) { + return getEntityManager().createStoredProcedureQuery(procedureName, + getQueryMethod().getEntityInformation().getJavaType()); + } + + return getEntityManager().createStoredProcedureQuery(procedureName); + } + + /** + * @return true if the stored procedure will use a ResultSet to return data and not output parameters + */ + private boolean isResultSetProcedure() { + return getQueryMethod().isCollectionQuery() || getQueryMethod().isQueryForEntity(); + } } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index a10de113d0..4147b18601 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -25,9 +25,7 @@ import javax.persistence.EntityManager; import javax.persistence.Tuple; -import com.querydsl.core.types.EntityPath; import org.slf4j.Logger; - import org.springframework.beans.factory.BeanFactory; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.jpa.projection.CollectionAwareProjectionFactory; @@ -40,6 +38,7 @@ import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy; import org.springframework.data.jpa.repository.query.JpaQueryMethod; import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory; +import org.springframework.data.jpa.repository.query.Procedure; import org.springframework.data.jpa.util.JpaMetamodel; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.querydsl.EntityPathResolver; @@ -59,6 +58,8 @@ import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; +import com.querydsl.core.types.EntityPath; + /** * JPA specific generic repository factory. * @@ -97,7 +98,7 @@ public JpaRepositoryFactory(EntityManager entityManager) { addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor); addRepositoryProxyPostProcessor((factory, repositoryInformation) -> { - if (hasMethodReturningStream(repositoryInformation.getRepositoryInterface())) { + if (isTransactionNeeded(repositoryInformation.getRepositoryInterface())) { factory.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE); } }); @@ -272,12 +273,12 @@ protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata return RepositoryFragments.empty(); } - private static boolean hasMethodReturningStream(Class repositoryClass) { + private static boolean isTransactionNeeded(Class repositoryClass) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(repositoryClass); for (Method method : methods) { - if (Stream.class.isAssignableFrom(method.getReturnType())) { + if (Stream.class.isAssignableFrom(method.getReturnType()) || method.isAnnotationPresent(Procedure.class)) { return true; } } @@ -293,7 +294,8 @@ private static boolean hasMethodReturningStream(Class repositoryClass) { * * @author Oliver Gierke * @since 2.0.5 - * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 + * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 */ private static class EclipseLinkProjectionQueryCreationListener implements QueryCreationListener { diff --git a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java index e0931eb2d6..c02dc7f1cf 100644 --- a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -47,6 +47,7 @@ * @author Thomas Darimont * @author Oliver Gierke * @author Jens Schauder + * @author Gabriel Basilio * @see scripts/schema-stored-procedures.sql for procedure definitions. */ @Transactional @@ -54,7 +55,7 @@ @ExtendWith(SpringExtension.class) public class StoredProcedureIntegrationTests { - private static final String NOT_SUPPORTED = "Stored procedures with ResultSets are currently not supported for any JPA provider"; + private static final String NOT_SUPPORTED = "Stored procedures with REF_CURSOR are currently not supported by HSQL dialect"; @PersistenceContext EntityManager em; @Autowired DummyRepository repository; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java index 73cc474bc0..caf306b160 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java @@ -21,9 +21,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; +import java.util.List; import java.util.Map; import javax.persistence.EntityManager; +import javax.persistence.ParameterMode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,8 +34,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.core.annotation.AliasFor; +import org.springframework.data.jpa.domain.sample.Dummy; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.repository.query.Param; import org.springframework.util.ReflectionUtils; @@ -47,6 +49,7 @@ * @author Diego Diez * @author Jeff Sheets * @author Jens Schauder + * @author Gabriel Basilio * @since 1.6 */ @ExtendWith(MockitoExtension.class) @@ -54,8 +57,7 @@ class StoredProcedureAttributeSourceUnitTests { private StoredProcedureAttributeSource creator; - @Mock - JpaEntityMetadata entityMetadata; + @Mock JpaEntityMetadata entityMetadata; @BeforeEach void setup() { @@ -70,10 +72,11 @@ void setup() { void shouldCreateStoredProcedureAttributesFromProcedureMethodWithImplicitProcedureName() { StoredProcedureAttributes attr = creator.createFrom(method("plus1inout", Integer.class), entityMetadata); - + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); } @Test // DATAJPA-455 @@ -82,9 +85,11 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictName() { StoredProcedureAttributes attr = creator.createFrom(method("explicitlyNamedPlus1inout", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); } @Test // DATAJPA-455 @@ -93,9 +98,11 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedur StoredProcedureAttributes attr = creator.createFrom(method("explicitlyNamedPlus1inout", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); } @Test // DATAJPA-455 @@ -104,9 +111,11 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedur StoredProcedureAttributes attr = creator .createFrom(method("explicitPlus1inoutViaProcedureNameAlias", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); } @Test // DATAJPA-1297 @@ -115,9 +124,11 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedur StoredProcedureAttributes attr = creator.createFrom( method("explicitPlus1inoutViaProcedureNameAliasAndOutputParameterName", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo("res"); } @Test // DATAJPA-455 @@ -126,9 +137,11 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitl StoredProcedureAttributes attr = creator .createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("User.plus1IO"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo("res"); } @Test // DATAJPA-707 @@ -137,9 +150,11 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitl StoredProcedureAttributes attr = creator .createFrom(method("entityAnnotatedCustomNamedProcedureOutputParamNamePlus1IO", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("User.plus1IO"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("override"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo("override"); } @Test // DATAJPA-707 @@ -148,11 +163,18 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitl StoredProcedureAttributes attr = creator .createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO2", Integer.class), entityMetadata); + ProcedureParameter firstOutputParameter = attr.getOutputProcedureParameters().get(0); + ProcedureParameter secondOutputParameter = attr.getOutputProcedureParameters().get(1); + assertThat(attr.getProcedureName()).isEqualTo("User.plus1IO2"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res"); - assertThat(attr.getOutputParameterTypes().get(1)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(1)).isEqualTo("res2"); + + assertThat(firstOutputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(firstOutputParameter.getType()).isEqualTo(Integer.class); + assertThat(firstOutputParameter.getName()).isEqualTo("res"); + + assertThat(secondOutputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(secondOutputParameter.getType()).isEqualTo(Integer.class); + assertThat(secondOutputParameter.getName()).isEqualTo("res2"); } @Test // DATAJPA-455 @@ -160,9 +182,11 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithImplicitl StoredProcedureAttributes attr = creator.createFrom(method("plus1", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("User.plus1"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo("res"); } @Test // DATAJPA-871 @@ -171,9 +195,11 @@ void aliasedStoredProcedure() { StoredProcedureAttributes attr = creator .createFrom(method("plus1inoutWithComposedAnnotationOverridingProcedureName", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); } @Test // DATAJPA-871 @@ -182,9 +208,102 @@ void aliasedStoredProcedure2() { StoredProcedureAttributes attr = creator .createFrom(method("plus1inoutWithComposedAnnotationOverridingName", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); assertThat(attr.getProcedureName()).isEqualTo("User.plus1"); - assertThat(attr.getOutputParameterTypes().get(0)).isEqualTo(Integer.class); - assertThat(attr.getOutputParameterNames().get(0)).isEqualTo("res"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Integer.class); + assertThat(outputParameter.getName()).isEqualTo("res"); + } + + @Test // DATAJPA-1657 + public void testSingleEntityFrom1RowResultSetAndNoInput() { + + StoredProcedureAttributes attr = creator.createFrom(method("singleEntityFrom1RowResultSetAndNoInput"), + entityMetadata); + + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("0_input_1_row_resultset"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Dummy.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + } + + @Test // DATAJPA-1657 + public void testSingleEntityFrom1RowResultSetWithInput() { + + StoredProcedureAttributes attr = creator.createFrom(method("singleEntityFrom1RowResultSetWithInput", Integer.class), + entityMetadata); + + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_row_resultset"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(Dummy.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + } + + @Test // DATAJPA-1657 + public void testEntityListFromResultSetWithNoInput() { + + StoredProcedureAttributes attr = creator.createFrom(method("entityListFromResultSetWithNoInput"), entityMetadata); + + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("0_input_1_resultset"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(List.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + } + + // + @Test // DATAJPA-1657 + public void testEntityListFromResultSetWithInput() { + + StoredProcedureAttributes attr = creator.createFrom(method("entityListFromResultSetWithInput", Integer.class), + entityMetadata); + + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(List.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + } + + @Test // DATAJPA-1657 + public void testGenericObjectListFromResultSetWithInput() { + + StoredProcedureAttributes attr = creator + .createFrom(method("genericObjectListFromResultSetWithInput", Integer.class), entityMetadata); + + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(List.class); + assertThat(outputParameter.getName()).isEqualTo(StoredProcedureAttributes.SYNTHETIC_OUTPUT_PARAMETER_NAME); + } + + @Test // DATAJPA-1657 + public void testEntityListFromResultSetWithInputAndNamedOutput() { + + StoredProcedureAttributes attr = creator + .createFrom(method("entityListFromResultSetWithInputAndNamedOutput", Integer.class), entityMetadata); + + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); + assertThat(outputParameter.getType()).isEqualTo(List.class); + assertThat(outputParameter.getName()).isEqualTo("dummies"); + } + + @Test // DATAJPA-1657 + public void testEntityListFromResultSetWithInputAndNamedOutputAndCursor() { + + StoredProcedureAttributes attr = creator + .createFrom(method("entityListFromResultSetWithInputAndNamedOutputAndCursor", Integer.class), entityMetadata); + + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset"); + assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.REF_CURSOR); + assertThat(outputParameter.getType()).isEqualTo(List.class); + assertThat(outputParameter.getName()).isEqualTo("dummies"); } private static Method method(String name, Class... paramTypes) { @@ -195,8 +314,7 @@ private static Method method(String name, Class... paramTypes) { * @author Thomas Darimont */ @SuppressWarnings("unused") - private - interface DummyRepository { + private interface DummyRepository { /** * Explicitly mapped to a procedure with name "plus1inout" in database. @@ -235,8 +353,7 @@ interface DummyRepository { Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg); /** - * Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}. - * With a outputParameterName + * Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}. With a outputParameterName */ @Procedure(name = "User.plus1IO", outputParameterName = "override") // DATAJPA-707 @@ -261,6 +378,34 @@ interface DummyRepository { @ComposedProcedureUsingAliasFor(emProcedureName = "User.plus1") Integer plus1inoutWithComposedAnnotationOverridingName(Integer arg); + + @Procedure("0_input_1_row_resultset") + // DATAJPA-1657 + Dummy singleEntityFrom1RowResultSetAndNoInput(); + + @Procedure("1_input_1_row_resultset") + // DATAJPA-1657 + Dummy singleEntityFrom1RowResultSetWithInput(Integer arg); + + @Procedure("0_input_1_resultset") + // DATAJPA-1657 + List entityListFromResultSetWithNoInput(); + + @Procedure("1_input_1_resultset") + // DATAJPA-1657 + List entityListFromResultSetWithInput(Integer arg); + + @Procedure("1_input_1_resultset") + // DATAJPA-1657 + List genericObjectListFromResultSetWithInput(Integer arg); + + @Procedure(value = "1_input_1_resultset", outputParameterName = "dummies") + // DATAJPA-1657 + List entityListFromResultSetWithInputAndNamedOutput(Integer arg); + + @Procedure(value = "1_input_1_resultset", outputParameterName = "dummies", refCursor = true) + // DATAJPA-1657 + List entityListFromResultSetWithInputAndNamedOutputAndCursor(Integer arg); } @SuppressWarnings("unused") diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java index c63e22b22b..3d1bedbbeb 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java @@ -20,6 +20,8 @@ import org.junit.jupiter.api.Test; +import javax.persistence.ParameterMode; + /** * Unit tests for {@link StoredProcedureAttributes}. * @@ -31,7 +33,8 @@ class StoredProcedureAttributesUnitTests { @Test // DATAJPA-681 void usesSyntheticOutputParameterNameForAdhocProcedureWithoutOutputName() { - StoredProcedureAttributes attributes = new StoredProcedureAttributes("procedure", null, Long.class); - assertThat(attributes.getOutputParameterNames().get(0)).isEqualTo(SYNTHETIC_OUTPUT_PARAMETER_NAME); + ProcedureParameter outputParameter = new ProcedureParameter(null, ParameterMode.OUT, Long.class); + StoredProcedureAttributes attributes = new StoredProcedureAttributes("procedure", outputParameter); + assertThat(attributes.getOutputProcedureParameters().get(0).getName()).isEqualTo(SYNTHETIC_OUTPUT_PARAMETER_NAME); } } From 0a81bc6a5dafc2f0efc41c7f6fc6d12c67409cb5 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 9 Jul 2021 11:05:25 -0500 Subject: [PATCH 031/821] Polishing. See: #1959, #409. --- pom.xml | 18 +++-- .../repository/query/JpaQueryExecution.java | 18 +++-- .../data/jpa/repository/query/Procedure.java | 2 +- .../repository/query/ProcedureParameter.java | 43 ++++++++++-- .../query/StoredProcedureAttributeSource.java | 62 +++++++++++------ .../query/StoredProcedureAttributes.java | 32 +++++---- .../query/StoredProcedureJpaQuery.java | 47 ++++++++----- .../support/JpaRepositoryFactory.java | 3 + .../StoredProcedureIntegrationTests.java | 1 - ...oredProcedureAttributeSourceUnitTests.java | 68 ++++++++++--------- .../StoredProcedureAttributesUnitTests.java | 8 +-- 11 files changed, 193 insertions(+), 109 deletions(-) diff --git a/pom.xml b/pom.xml index b622598385..56389ab45f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -101,7 +102,9 @@ - ${project.build.directory}/${project.artifactId}-${project.version}.zip + + ${project.build.directory}/${project.artifactId}-${project.version}.zip + zip @@ -358,7 +361,10 @@ **/OpenJpa* **/EclipseLink* - -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} + + -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar + -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} + @@ -371,7 +377,11 @@ **/EclipseLink*Tests.java - -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar + + -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} + -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar + -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar + diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index ac09f871d8..cb2bc61b4d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -54,6 +54,7 @@ * @author Nicolas Cirigliano * @author Jens Schauder * @author Gabriel Basilio + * @author Greg Turnquist */ public abstract class JpaQueryExecution { @@ -319,19 +320,16 @@ protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAcce boolean returnsResultSet = storedProcedure.execute(); if (returnsResultSet) { - if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) - throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); - - List result = storedProcedure.getResultList(); - if (!storedProcedureJpaQuery.getQueryMethod().isCollectionQuery()) { - if (result.isEmpty()) - return null; - if (result.size() == 1) - return result.get(0); + if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) { + throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); } - return result; + if (storedProcedureJpaQuery.getQueryMethod().isCollectionQuery()) { + return storedProcedure.getResultList(); + } else { + return storedProcedure.getSingleResult(); + } } return storedProcedureJpaQuery.extractOutputValue(storedProcedure); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java b/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java index 13007cc25a..7b7835152c 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java @@ -54,7 +54,7 @@ String outputParameterName() default ""; /** - * Whether the procedure returns a Ref Cursor from the database {@code false}. + * Whether the procedure returns a Ref Cursor from the database - defaults to {@code false}. */ boolean refCursor() default false; } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java b/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java index 6d185e5025..77577fc43e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2020 the original author or authors. + * Copyright 2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,23 +16,27 @@ package org.springframework.data.jpa.repository.query; -import org.springframework.lang.Nullable; +import java.util.Objects; import javax.persistence.ParameterMode; +import org.springframework.lang.Nullable; + /** - * This class represents a Stored Procedure Parameter - * and an instance of the annotation {@link javax.persistence.StoredProcedureParameter}. + * This class represents a Stored Procedure Parameter and an instance of the annotation + * {@link javax.persistence.StoredProcedureParameter}. * * @author Gabriel Basilio + * @author Greg Turnquist */ -public class ProcedureParameter { +class ProcedureParameter { private final String name; private final ParameterMode mode; private final Class type; - public ProcedureParameter(@Nullable String name, ParameterMode mode, Class type) { + ProcedureParameter(@Nullable String name, ParameterMode mode, Class type) { + this.name = name; this.mode = mode; this.type = type; @@ -49,4 +53,29 @@ public ParameterMode getMode() { public Class getType() { return type; } -} \ No newline at end of file + + @Override + public boolean equals(Object o) { + + if (this == o) { + return true; + } + + if (!(o instanceof ProcedureParameter)) { + return false; + } + + ProcedureParameter that = (ProcedureParameter) o; + return Objects.equals(name, that.name) && mode == that.mode && Objects.equals(type, that.type); + } + + @Override + public int hashCode() { + return Objects.hash(name, mode, type); + } + + @Override + public String toString() { + return "ProcedureParameter{" + "name='" + name + '\'' + ", mode=" + mode + ", type=" + type + '}'; + } +} diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java index 0160ad65e2..010ceb38fd 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java @@ -18,7 +18,9 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import javax.persistence.NamedStoredProcedureQueries; import javax.persistence.NamedStoredProcedureQuery; @@ -28,6 +30,7 @@ import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -40,6 +43,7 @@ * @author Diego Diez * @author Jeff Sheets * @author Gabriel Basilio + * @author Greg Turnquist * @since 1.6 */ enum StoredProcedureAttributeSource { @@ -69,9 +73,9 @@ public StoredProcedureAttributes createFrom(Method method, JpaEntityMetadata } String procedureName = deriveProcedureNameFrom(method, procedure); - if (StringUtils.isEmpty(procedureName)) { - throw new IllegalArgumentException("Could not determine name of procedure for @Procedure annotated method: " - + method); + if (ObjectUtils.isEmpty(procedureName)) { + throw new IllegalArgumentException( + "Could not determine name of procedure for @Procedure annotated method: " + method); } return new StoredProcedureAttributes(procedureName, createOutputProcedureParameterFrom(method, procedure)); @@ -96,42 +100,57 @@ private String deriveProcedureNameFrom(Method method, Procedure procedure) { } /** + * Extract procedure attributes from method and procedure. + * * @param method * @param namedStoredProc * @param procedure * @return */ - private StoredProcedureAttributes newProcedureAttributesFrom(Method method, - NamedStoredProcedureQuery namedStoredProc, Procedure procedure) { + private StoredProcedureAttributes newProcedureAttributesFrom(Method method, NamedStoredProcedureQuery namedStoredProc, + Procedure procedure) { - List outputParameters = new ArrayList<>(); + List outputParameters; if (!procedure.outputParameterName().isEmpty()) { + // we give the output parameter definition from the @Procedure annotation precedence - outputParameters.add(createOutputProcedureParameterFrom(method, procedure)); + outputParameters = Collections.singletonList(createOutputProcedureParameterFrom(method, procedure)); } else { // try to discover the output parameter - List namedProcedureOutputParameters = extractOutputParametersFrom(namedStoredProc); - - for (StoredProcedureParameter outputParameter : namedProcedureOutputParameters) { - outputParameters.add(new ProcedureParameter( - outputParameter.name(), outputParameter.mode(), outputParameter.type())); - } + outputParameters = extractOutputParametersFrom(namedStoredProc).stream() // + .map(namedParameter -> new ProcedureParameter(namedParameter.name(), namedParameter.mode(), + namedParameter.type())) // + .collect(Collectors.toList()); } return new StoredProcedureAttributes(namedStoredProc.name(), outputParameters, true); } + /** + * Create a {@link ProcedureParameter} from relevant {@link Method} and {@link Procedure}. + * + * @param method + * @param procedure + * @return + */ private ProcedureParameter createOutputProcedureParameterFrom(Method method, Procedure procedure) { + return new ProcedureParameter(procedure.outputParameterName(), - procedure.refCursor() ? ParameterMode.REF_CURSOR : ParameterMode.OUT, - method.getReturnType()); + procedure.refCursor() ? ParameterMode.REF_CURSOR : ParameterMode.OUT, method.getReturnType()); } + /** + * Translate all the {@Link NamedStoredProcedureQuery} parameters into a {@link List} of + * {@link StoredProcedureParameter}s. + * + * @param namedStoredProc + * @return + */ private List extractOutputParametersFrom(NamedStoredProcedureQuery namedStoredProc) { - List outputParameters = new ArrayList(); + List outputParameters = new ArrayList<>(); for (StoredProcedureParameter param : namedStoredProc.parameters()) { @@ -190,9 +209,12 @@ private NamedStoredProcedureQuery tryFindAnnotatedNamedStoredProcedureQuery(Meth * @param procedure * @return */ - private String derivedNamedProcedureNameFrom(Method method, JpaEntityMetadata entityMetadata, Procedure procedure) { - return StringUtils.hasText(procedure.name()) ? procedure.name() : entityMetadata.getEntityName() + "." - + method.getName(); + private String derivedNamedProcedureNameFrom(Method method, JpaEntityMetadata entityMetadata, + Procedure procedure) { + + return StringUtils.hasText(procedure.name()) // + ? procedure.name() // + : entityMetadata.getEntityName() + "." + method.getName(); } /** @@ -201,7 +223,7 @@ private String derivedNamedProcedureNameFrom(Method method, JpaEntityMetadata */ private List collectNamedStoredProcedureQueriesFrom(Class entityType) { - List queries = new ArrayList(); + List queries = new ArrayList<>(); NamedStoredProcedureQueries namedQueriesAnnotation = AnnotatedElementUtils.findMergedAnnotation(entityType, NamedStoredProcedureQueries.class); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java index 7c885fc135..fc89d2dc5e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java @@ -15,16 +15,16 @@ */ package org.springframework.data.jpa.repository.query; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -import javax.persistence.StoredProcedureQuery; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; +import javax.persistence.StoredProcedureQuery; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + /** * Stored procedure configuration for JPA 2.1 {@link StoredProcedureQuery}s. * @@ -48,7 +48,7 @@ class StoredProcedureAttributes { /** * Creates a new {@link StoredProcedureAttributes}. * - * @param procedureName must not be {@literal null}. + * @param procedureName must not be {@literal null}. */ StoredProcedureAttributes(String procedureName, ProcedureParameter parameter) { this(procedureName, Collections.singletonList(parameter), false); @@ -57,14 +57,16 @@ class StoredProcedureAttributes { /** * Creates a new {@link StoredProcedureAttributes}. * - * @param procedureName must not be {@literal null}. + * @param procedureName must not be {@literal null}. * @param namedStoredProcedure flag signaling if the stored procedure has a name. */ - StoredProcedureAttributes(String procedureName, List outputProcedureParameters, boolean namedStoredProcedure) { + StoredProcedureAttributes(String procedureName, List outputProcedureParameters, + boolean namedStoredProcedure) { Assert.notNull(procedureName, "ProcedureName must not be null!"); Assert.notNull(outputProcedureParameters, "OutputProcedureParameters must not be null!"); - Assert.isTrue(outputProcedureParameters.size() != 1 || outputProcedureParameters.get(0) != null, "ProcedureParameters must not have size 1 with a null value"); + Assert.isTrue(outputProcedureParameters.size() != 1 || outputProcedureParameters.get(0) != null, + "ProcedureParameters must not have size 1 with a null value"); this.procedureName = procedureName; this.namedStoredProcedure = namedStoredProcedure; @@ -77,15 +79,16 @@ class StoredProcedureAttributes { } private List getParametersWithCompletedNames(List procedureParameters) { - return IntStream.range(0, procedureParameters.size()) - .mapToObj(i -> getParameterWithCompletedName(procedureParameters.get(i), i)) + + return IntStream.range(0, procedureParameters.size()) // + .mapToObj(i -> getParameterWithCompletedName(procedureParameters.get(i), i)) // .collect(Collectors.toList()); } private ProcedureParameter getParameterWithCompletedName(ProcedureParameter parameter, int i) { - return new ProcedureParameter( - completeOutputParameterName(i, parameter.getName()), - parameter.getMode(), parameter.getType()); + + return new ProcedureParameter(completeOutputParameterName(i, parameter.getName()), parameter.getMode(), + parameter.getType()); } private String completeOutputParameterName(int i, String paramName) { @@ -130,6 +133,7 @@ public List getOutputProcedureParameters() { * @return */ public boolean hasReturnValue() { + if (getOutputProcedureParameters().isEmpty()) return false; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index caacc7adcc..9aadb5c4c1 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -18,20 +18,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.IntStream; import javax.persistence.EntityManager; import javax.persistence.NamedStoredProcedureQuery; import javax.persistence.ParameterMode; -import javax.persistence.StoredProcedureParameter; import javax.persistence.StoredProcedureQuery; import javax.persistence.TypedQuery; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.Parameter; -import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.ResultProcessor; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -143,7 +139,8 @@ Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) { for (int i = 0; i < outputParameters.size(); i++) { ProcedureParameter outputParameter = outputParameters.get(i); - outputValues.put(outputParameter.getName(), extractOutputParameterValue(outputParameter, i, storedProcedureQuery)); + outputValues.put(outputParameter.getName(), + extractOutputParameterValue(outputParameter, i, storedProcedureQuery)); } return outputValues; @@ -152,12 +149,13 @@ Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) { /** * @return The value of an output parameter either by name or by index. */ - private Object extractOutputParameterValue(ProcedureParameter outputParameter, Integer index, StoredProcedureQuery storedProcedureQuery) { + private Object extractOutputParameterValue(ProcedureParameter outputParameter, Integer index, + StoredProcedureQuery storedProcedureQuery) { JpaParameters methodParameters = getQueryMethod().getParameters(); - return useNamedParameters && StringUtils.hasText(outputParameter.getName()) ? - storedProcedureQuery.getOutputParameterValue(outputParameter.getName()) + return useNamedParameters && StringUtils.hasText(outputParameter.getName()) + ? storedProcedureQuery.getOutputParameterValue(outputParameter.getName()) : storedProcedureQuery.getOutputParameterValue(methodParameters.getNumberOfParameters() + index + 1); } @@ -166,7 +164,8 @@ private Object extractOutputParameterValue(ProcedureParameter outputParameter, I */ private StoredProcedureQuery createStoredProcedure() { - return procedureAttributes.isNamedStoredProcedure() ? newNamedStoredProcedureQuery() + return procedureAttributes.isNamedStoredProcedure() // + ? newNamedStoredProcedureQuery() : newAdhocStoredProcedureQuery(); } @@ -205,15 +204,20 @@ private StoredProcedureQuery newAdhocStoredProcedureQuery() { ProcedureParameter procedureOutput = procedureAttributes.getOutputProcedureParameters().get(0); - /* If the stored procedure returns a ResultSet without using REF_CURSOR, - it is not necessary to declare an output parameter */ - if ((isResultSetProcedure() && procedureOutput.getMode() == ParameterMode.REF_CURSOR) || !isResultSetProcedure()) { + /** + * If there is a {@link java.sql.ResultSet} with a {@link ParameterMode#REF_CURSOR}, find the output parameter. + * Otherwise, no need, there is no need to find an output parameter. + */ + if (storedProcedureHasResultSetUsingRefCursor(procedureOutput) || !isResultSetProcedure()) { if (useNamedParameters) { - procedureQuery.registerStoredProcedureParameter(procedureOutput.getName(), procedureOutput.getType(), procedureOutput.getMode()); + procedureQuery.registerStoredProcedureParameter(procedureOutput.getName(), procedureOutput.getType(), + procedureOutput.getMode()); } else { + // Output parameter should be after the input parameters int outputParameterIndex = params.getNumberOfParameters() + 1; + procedureQuery.registerStoredProcedureParameter(outputParameterIndex, procedureOutput.getType(), procedureOutput.getMode()); } @@ -223,18 +227,29 @@ private StoredProcedureQuery newAdhocStoredProcedureQuery() { return procedureQuery; } + /** + * Does this stored procedure has a {@link java.sql.ResultSet} using {@link ParameterMode#REF_CURSOR}? + * + * @param procedureOutput + * @return + */ + private boolean storedProcedureHasResultSetUsingRefCursor(ProcedureParameter procedureOutput) { + return isResultSetProcedure() && procedureOutput.getMode() == ParameterMode.REF_CURSOR; + } + private StoredProcedureQuery createAdhocStoredProcedureQuery() { - String procedureName = procedureAttributes.getProcedureName(); if (getQueryMethod().isQueryForEntity()) { - return getEntityManager().createStoredProcedureQuery(procedureName, + + return getEntityManager().createStoredProcedureQuery(procedureAttributes.getProcedureName(), getQueryMethod().getEntityInformation().getJavaType()); } - return getEntityManager().createStoredProcedureQuery(procedureName); + return getEntityManager().createStoredProcedureQuery(procedureAttributes.getProcedureName()); } /** + * * @return true if the stored procedure will use a ResultSet to return data and not output parameters */ private boolean isResultSetProcedure() { diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 4147b18601..d4773baab2 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -69,6 +69,8 @@ * @author Jens Schauder * @author Stefan Fussenegger * @author Réda Housni Alaoui + * @author Gabriel Basilio + * @author Greg Turnquist */ public class JpaRepositoryFactory extends RepositoryFactorySupport { @@ -214,6 +216,7 @@ protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFa @Override protected Optional getQueryLookupStrategy(@Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider) { + return Optional.of(JpaQueryLookupStrategy.create(entityManager, queryMethodFactory, key, evaluationContextProvider, escapeCharacter)); } diff --git a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java index c02dc7f1cf..11bf21f1e3 100644 --- a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -28,7 +28,6 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java index caf306b160..d4388f0256 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java @@ -50,6 +50,7 @@ * @author Jeff Sheets * @author Jens Schauder * @author Gabriel Basilio + * @author Greg Turnquist * @since 1.6 */ @ExtendWith(MockitoExtension.class) @@ -72,7 +73,9 @@ void setup() { void shouldCreateStoredProcedureAttributesFromProcedureMethodWithImplicitProcedureName() { StoredProcedureAttributes attr = creator.createFrom(method("plus1inout", Integer.class), entityMetadata); + ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -86,6 +89,7 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictName() { entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -99,6 +103,7 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedur entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -112,6 +117,7 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedur .createFrom(method("explicitPlus1inoutViaProcedureNameAlias", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -125,6 +131,7 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodWithExplictProcedur method("explicitPlus1inoutViaProcedureNameAliasAndOutputParameterName", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -138,6 +145,7 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitl .createFrom(method("entityAnnotatedCustomNamedProcedurePlus1IO", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("User.plus1IO"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -151,6 +159,7 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithExplicitl .createFrom(method("entityAnnotatedCustomNamedProcedureOutputParamNamePlus1IO", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("User.plus1IO"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -183,6 +192,7 @@ void shouldCreateStoredProcedureAttributesFromProcedureMethodBackedWithImplicitl StoredProcedureAttributes attr = creator.createFrom(method("plus1", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("User.plus1"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -196,6 +206,7 @@ void aliasedStoredProcedure() { .createFrom(method("plus1inoutWithComposedAnnotationOverridingProcedureName", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("plus1inout"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -209,6 +220,7 @@ void aliasedStoredProcedure2() { .createFrom(method("plus1inoutWithComposedAnnotationOverridingName", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("User.plus1"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Integer.class); @@ -222,6 +234,7 @@ public void testSingleEntityFrom1RowResultSetAndNoInput() { entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("0_input_1_row_resultset"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Dummy.class); @@ -235,6 +248,7 @@ public void testSingleEntityFrom1RowResultSetWithInput() { entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_row_resultset"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(Dummy.class); @@ -247,6 +261,7 @@ public void testEntityListFromResultSetWithNoInput() { StoredProcedureAttributes attr = creator.createFrom(method("entityListFromResultSetWithNoInput"), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("0_input_1_resultset"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(List.class); @@ -261,6 +276,7 @@ public void testEntityListFromResultSetWithInput() { entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(List.class); @@ -274,6 +290,7 @@ public void testGenericObjectListFromResultSetWithInput() { .createFrom(method("genericObjectListFromResultSetWithInput", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(List.class); @@ -287,6 +304,7 @@ public void testEntityListFromResultSetWithInputAndNamedOutput() { .createFrom(method("entityListFromResultSetWithInputAndNamedOutput", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.OUT); assertThat(outputParameter.getType()).isEqualTo(List.class); @@ -300,6 +318,7 @@ public void testEntityListFromResultSetWithInputAndNamedOutputAndCursor() { .createFrom(method("entityListFromResultSetWithInputAndNamedOutputAndCursor", Integer.class), entityMetadata); ProcedureParameter outputParameter = attr.getOutputProcedureParameters().get(0); + assertThat(attr.getProcedureName()).isEqualTo("1_input_1_resultset"); assertThat(outputParameter.getMode()).isEqualTo(ParameterMode.REF_CURSOR); assertThat(outputParameter.getType()).isEqualTo(List.class); @@ -319,58 +338,50 @@ private interface DummyRepository { /** * Explicitly mapped to a procedure with name "plus1inout" in database. */ - @Procedure("plus1inout") - // DATAJPA-455 + @Procedure("plus1inout") // DATAJPA-455 Integer explicitlyNamedPlus1inout(Integer arg); /** * Explicitly mapped to a procedure with name "plus1inout" in database via alias. */ - @Procedure(procedureName = "plus1inout") - // DATAJPA-455 + @Procedure(procedureName = "plus1inout") // DATAJPA-455 Integer explicitPlus1inoutViaProcedureNameAlias(Integer arg); /** - * Explicitly mapped to a procedure with name "plus1inout" in database via alias and explicitly named ouput + * Explicitly mapped to a procedure with name "plus1inout" in database via alias and explicitly named output * parameter. */ - @Procedure(procedureName = "plus1inout", outputParameterName = "res") - // DATAJPA-1297 + @Procedure(procedureName = "plus1inout", outputParameterName = "res") // DATAJPA-1297 Integer explicitPlus1inoutViaProcedureNameAliasAndOutputParameterName(Integer arg); /** * Implicitly mapped to a procedure with name "plus1inout" in database via alias. */ - @Procedure - // DATAJPA-455 + @Procedure // DATAJPA-455 Integer plus1inout(Integer arg); /** * Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}. */ - @Procedure(name = "User.plus1IO") - // DATAJPA-455 + @Procedure(name = "User.plus1IO") // DATAJPA-455 Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg); /** - * Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager}. With a outputParameterName + * Explicitly mapped to named stored procedure "User.plus1IO" in {@link EntityManager} with an outputParameterName. */ - @Procedure(name = "User.plus1IO", outputParameterName = "override") - // DATAJPA-707 + @Procedure(name = "User.plus1IO", outputParameterName = "override") // DATAJPA-707 Integer entityAnnotatedCustomNamedProcedureOutputParamNamePlus1IO(@Param("arg") Integer arg); /** * Explicitly mapped to named stored procedure "User.plus1IO2" in {@link EntityManager}. */ - @Procedure(name = "User.plus1IO2") - // DATAJPA-707 + @Procedure(name = "User.plus1IO2") // DATAJPA-707 Map entityAnnotatedCustomNamedProcedurePlus1IO2(@Param("arg") Integer arg); /** * Implicitly mapped to named stored procedure "User.plus1" in {@link EntityManager}. */ - @Procedure - // DATAJPA-455 + @Procedure // DATAJPA-455 Integer plus1(@Param("arg") Integer arg); @ComposedProcedureUsingAliasFor(explicitProcedureName = "plus1inout") @@ -379,32 +390,25 @@ private interface DummyRepository { @ComposedProcedureUsingAliasFor(emProcedureName = "User.plus1") Integer plus1inoutWithComposedAnnotationOverridingName(Integer arg); - @Procedure("0_input_1_row_resultset") - // DATAJPA-1657 + @Procedure("0_input_1_row_resultset") // DATAJPA-1657 Dummy singleEntityFrom1RowResultSetAndNoInput(); - @Procedure("1_input_1_row_resultset") - // DATAJPA-1657 + @Procedure("1_input_1_row_resultset") // DATAJPA-1657 Dummy singleEntityFrom1RowResultSetWithInput(Integer arg); - @Procedure("0_input_1_resultset") - // DATAJPA-1657 + @Procedure("0_input_1_resultset") // DATAJPA-1657 List entityListFromResultSetWithNoInput(); - @Procedure("1_input_1_resultset") - // DATAJPA-1657 + @Procedure("1_input_1_resultset") // DATAJPA-1657 List entityListFromResultSetWithInput(Integer arg); - @Procedure("1_input_1_resultset") - // DATAJPA-1657 + @Procedure("1_input_1_resultset") // DATAJPA-1657 List genericObjectListFromResultSetWithInput(Integer arg); - @Procedure(value = "1_input_1_resultset", outputParameterName = "dummies") - // DATAJPA-1657 + @Procedure(value = "1_input_1_resultset", outputParameterName = "dummies") // DATAJPA-1657 List entityListFromResultSetWithInputAndNamedOutput(Integer arg); - @Procedure(value = "1_input_1_resultset", outputParameterName = "dummies", refCursor = true) - // DATAJPA-1657 + @Procedure(value = "1_input_1_resultset", outputParameterName = "dummies", refCursor = true) // DATAJPA-1657 List entityListFromResultSetWithInputAndNamedOutputAndCursor(Integer arg); } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java index 3d1bedbbeb..73a35ba0a4 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java @@ -18,10 +18,10 @@ import static org.assertj.core.api.Assertions.*; import static org.springframework.data.jpa.repository.query.StoredProcedureAttributes.*; -import org.junit.jupiter.api.Test; - import javax.persistence.ParameterMode; +import org.junit.jupiter.api.Test; + /** * Unit tests for {@link StoredProcedureAttributes}. * @@ -33,8 +33,8 @@ class StoredProcedureAttributesUnitTests { @Test // DATAJPA-681 void usesSyntheticOutputParameterNameForAdhocProcedureWithoutOutputName() { - ProcedureParameter outputParameter = new ProcedureParameter(null, ParameterMode.OUT, Long.class); - StoredProcedureAttributes attributes = new StoredProcedureAttributes("procedure", outputParameter); + StoredProcedureAttributes attributes = new StoredProcedureAttributes("procedure", + new ProcedureParameter(null, ParameterMode.OUT, Long.class)); assertThat(attributes.getOutputProcedureParameters().get(0).getName()).isEqualTo(SYNTHETIC_OUTPUT_PARAMETER_NAME); } } From 8d6634430fb5958d4dfbc0bd64db4011368b64f3 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 9 Jul 2021 11:32:21 -0500 Subject: [PATCH 032/821] Polishing. See: #1959, #409. --- .../data/jpa/repository/query/StoredProcedureJpaQuery.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index 9aadb5c4c1..7b5c39ef9f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -228,7 +228,7 @@ private StoredProcedureQuery newAdhocStoredProcedureQuery() { } /** - * Does this stored procedure has a {@link java.sql.ResultSet} using {@link ParameterMode#REF_CURSOR}? + * Does this stored procedure have a {@link java.sql.ResultSet} using {@link ParameterMode#REF_CURSOR}? * * @param procedureOutput * @return From 400aa0e042920341917d2705e79cdb2ff18bd9d7 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Fri, 9 Jul 2021 15:23:23 +0200 Subject: [PATCH 033/821] Adapt to API consolidation in Spring Data Commons' PersistentProperty. Closes: #2254 Original Pull Request: #2255 Related to: spring-projects/spring-data-commons#2408 --- .../mapping/JpaPersistentPropertyImpl.java | 23 +++++++++++++------ .../JpaPersistentPropertyImplUnitTests.java | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index a60b24eedf..a0ac87b813 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -39,7 +39,7 @@ import org.springframework.util.Assert; /** - * {@link JpaPersistentProperty} implementation usind a JPA {@link Metamodel}. + * {@link JpaPersistentProperty} implementation using a JPA {@link Metamodel}. * * @author Oliver Gierke * @author Thomas Darimont @@ -130,10 +130,19 @@ public Class getActualType() { */ @Override public Iterable> getPersistentEntityTypes() { + return getPersistentEntityTypeInformation(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.model.AbstractPersistentProperty#getPersistentEntityTypeInformation() + */ + @Override + public Iterable> getPersistentEntityTypeInformation() { return associationTargetType != null // ? Collections.singleton(associationTargetType) // - : super.getPersistentEntityTypes(); + : super.getPersistentEntityTypeInformation(); } /* @@ -219,22 +228,22 @@ public boolean isEmbeddable() { /* * (non-Javadoc) - * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#getAssociationTargetType() + * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#getAssociationTargetTypeInformation() */ @Override - public Class getAssociationTargetType() { + public TypeInformation getAssociationTargetTypeInformation() { if (!isAssociation()) { return null; } if (associationTargetType != null) { - return associationTargetType.getType(); + return associationTargetType; } - Class targetType = super.getAssociationTargetType(); + TypeInformation targetType = super.getAssociationTargetTypeInformation(); - return targetType != null ? targetType : getActualType(); + return targetType != null ? targetType : getActualTypeInformation(); } /** diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index 1a4f219c83..2c98720dce 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -144,7 +144,7 @@ void considersTargetEntityTypeForPropertyType() { assertThat(property.getType()).isEqualTo(Api.class); assertThat(property.getActualType()).isEqualTo(Implementation.class); - Iterable> entityType = property.getPersistentEntityTypes(); + Iterable> entityType = property.getPersistentEntityTypeInformation(); assertThat(entityType.iterator().hasNext()).isTrue(); assertThat(entityType.iterator().next()) .isEqualTo(ClassTypeInformation.from(Implementation.class)); From 59bf6783ab0bae6f5bfbc8464afcbaf95c1fcd2d Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 12 Jul 2021 15:12:11 +0200 Subject: [PATCH 034/821] Upgrade Hibernate to 5.5.3.Final Closes #2257 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56389ab45f..09a335f485 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ DATAJPA 2.7.9 - 5.5.2.Final + 5.5.3.Final 0.10.3 org.hibernate 2.6.0-SNAPSHOT From 02157824900c68d38c470ebd88f5d139157aeea1 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 16 Jul 2021 10:18:49 +0200 Subject: [PATCH 035/821] Updated changelog. See #2246 --- src/main/resources/changelog.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index 8e7924fabd..0515c7a873 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,11 @@ Spring Data JPA Changelog ========================= +Changes in version 2.4.11 (2021-07-16) +-------------------------------------- +* #2245 - Drop serialVersionUID from AbstractPersistable. + + Changes in version 2.5.2 (2021-06-22) ------------------------------------- * #2234 - Spring Data documentation examples have wrong annotations. @@ -2307,5 +2312,6 @@ Changes in version 1.0.0.M1 (2011-02-10) - https://jira.springsource.org/browse/ + From 2e1f1a45f4bc4dc484796ddc00da7d22120b3040 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 16 Jul 2021 14:08:34 +0200 Subject: [PATCH 036/821] Updated changelog. See #2201 --- src/main/resources/changelog.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index 0515c7a873..c50cd70408 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,27 @@ Spring Data JPA Changelog ========================= +Changes in version 2.6.0-M1 (2021-07-16) +---------------------------------------- +* #2257 - Upgrade to Hibernate 5.5.3.Final. +* #2254 - Adapt to consolidated PersistentEntity API. +* #2251 - Small mistake in Documentation. +* #2247 - Remove explicit version for Mockito, so we use the one of Spring Data Build. +* #2245 - Drop serialVersionUID from AbstractPersistable. +* #2239 - Use Spring nullability annotations instead of JSR305. +* #2234 - Spring Data documentation examples have wrong annotations. +* #2231 - Fix build failures with Java 16. +* #2230 - Error in documentation regarding manually assigned identifiers in "Entity State-detection Strategies" when there is a Version property. +* #2228 - ExpressionBasedStringQuery does not quote all parameter expressions. +* #2218 - Unused org.aspectj:aspectjrt compile time dependency. +* #2207 - Improve wording on transactional methods inherited from `SimpleJpaRepository` and repository fragments. +* #2202 - Introduce template method for easier customization of fragments. +* #2184 - Update CI to Java 16. +* #2129 - Missing anchor on "Custom Namespace Attributes" causes TOC to break. +* #2009 - Document that DTOs don't work with native queries [DATAJPA-1714]. +* #1959 - @Procedure annotation doesn't work with cursors (NULL when using REF_CURSOR) and ResultSets that don't come from cursors [DATAJPA-1657]. + + Changes in version 2.4.11 (2021-07-16) -------------------------------------- * #2245 - Drop serialVersionUID from AbstractPersistable. @@ -2313,5 +2334,6 @@ Changes in version 1.0.0.M1 (2011-02-10) - https://jira.springsource.org/browse/ + From 9f210e0ee1a6d667f0f57e9451ab476dacb08a5a Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 16 Jul 2021 14:08:58 +0200 Subject: [PATCH 037/821] Prepare 2.6 M1 (2021.1.0). See #2201 --- pom.xml | 11 +++++------ src/main/resources/notice.txt | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 09a335f485..774df9c09a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 @@ -15,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-SNAPSHOT + 2.6.0-M1 @@ -26,7 +25,7 @@ 5.5.3.Final 0.10.3 org.hibernate - 2.6.0-SNAPSHOT + 2.6.0-M1 spring.data.jpa @@ -450,8 +449,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index e9af3a4595..e1e5caba6c 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data JPA 2.5 GA (2021.0.0) +Spring Data JPA 2.6 M1 (2021.1.0) Copyright (c) [2011-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -27,3 +27,4 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From bfdc68d1f89883a8e85a312b3f74bed3cc8f4e5f Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 16 Jul 2021 14:09:25 +0200 Subject: [PATCH 038/821] Release version 2.6 M1 (2021.1.0). See #2201 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 774df9c09a..dd29e6e343 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-SNAPSHOT + 2.6.0-M1 Spring Data JPA Spring Data module for JPA repositories. From 003d78b0e916c31c5fc637125ce7d3e2e9b57224 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 16 Jul 2021 14:19:55 +0200 Subject: [PATCH 039/821] Prepare next development iteration. See #2201 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dd29e6e343..774df9c09a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-M1 + 2.6.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. From 183e3e5b622ebce89b976e857a13a37d1f95388b Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 16 Jul 2021 14:19:57 +0200 Subject: [PATCH 040/821] After release cleanups. See #2201 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 774df9c09a..c5902fb8fc 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-M1 + 2.6.0-SNAPSHOT @@ -25,7 +25,7 @@ 5.5.3.Final 0.10.3 org.hibernate - 2.6.0-M1 + 2.6.0-SNAPSHOT spring.data.jpa @@ -449,8 +449,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From a4ef56d83cb3caab4a5cafce9198400a082eb1d5 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 22 Jul 2021 16:57:58 -0500 Subject: [PATCH 041/821] Add support for MySQL and Postgres testing. * Introduce Testcontainers as the mechanism to test against real data stores. * Alter CI to handle running Testcontainers in reduced privilege mode. * Run the data store specific test cases ONLY for the baseline Java 8 test execution. See #2256. --- Jenkinsfile | 7 +- ci/clean.sh | 6 + ci/test.sh | 10 + pom.xml | 97 ++++++- .../MySqlStoredProcedureIntegrationTests.java | 252 ++++++++++++++++++ ...stgresStoredProcedureIntegrationTests.java | 252 ++++++++++++++++++ src/test/resources/logback.xml | 9 +- .../scripts/mysql-stored-procedures.sql | 27 ++ .../scripts/postgres-stored-procedures.sql | 45 ++++ 9 files changed, 697 insertions(+), 8 deletions(-) create mode 100755 ci/clean.sh create mode 100755 ci/test.sh create mode 100644 src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java create mode 100644 src/test/resources/scripts/mysql-stored-procedures.sql create mode 100644 src/test/resources/scripts/postgres-stored-procedures.sql diff --git a/Jenkinsfile b/Jenkinsfile index 0f34e477ba..906d2af6c8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,13 +24,16 @@ pipeline { } options { timeout(time: 30, unit: 'MINUTES') } environment { + DOCKER_HUB = credentials('hub.docker.com-springbuildmaster') ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') } steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' + docker.image('adoptopenjdk/openjdk8:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') { + sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}" + sh 'PROFILE=all-dbs ci/test.sh' + sh "ci/clean.sh" } } } diff --git a/ci/clean.sh b/ci/clean.sh new file mode 100755 index 0000000000..7b38a05c9c --- /dev/null +++ b/ci/clean.sh @@ -0,0 +1,6 @@ +#!/bin/bash -x + +set -euo pipefail + +MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \ + ./mvnw -s settings.xml clean -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa diff --git a/ci/test.sh b/ci/test.sh new file mode 100755 index 0000000000..ffb33bccf5 --- /dev/null +++ b/ci/test.sh @@ -0,0 +1,10 @@ +#!/bin/bash -x + +set -euo pipefail + +mkdir -p /tmp/jenkins-home/.m2/spring-data-jpa +chown -R 1001:1001 . + +MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \ + ./mvnw -s settings.xml \ + -P${PROFILE} clean dependency:list test -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa \ No newline at end of file diff --git a/pom.xml b/pom.xml index c5902fb8fc..1c15621445 100644 --- a/pom.xml +++ b/pom.xml @@ -23,9 +23,12 @@ 2.7.9 5.5.3.Final + 8.0.23 + 42.2.19 + 2.6.0-SNAPSHOT 0.10.3 + org.hibernate - 2.6.0-SNAPSHOT spring.data.jpa @@ -34,6 +37,43 @@ + + all-dbs + + + + org.apache.maven.plugins + maven-surefire-plugin + + + mysql-test + test + + test + + + + **/MySql*IntegrationTests.java + + + + + postgres-test + test + + test + + + + **/Postgres*IntegrationTests.java + + + + + + + + hibernate-next @@ -116,6 +156,18 @@ + + + + org.testcontainers + testcontainers-bom + ${testcontainers} + pom + import + + + + @@ -187,6 +239,40 @@ test + + + mysql + mysql-connector-java + ${mysql-connector-java} + test + + + + org.testcontainers + mysql + test + + + org.slf4j + jcl-over-slf4j + + + + + + + org.postgresql + postgresql + ${postgresql} + test + + + + org.testcontainers + postgresql + test + + org.threeten threetenbp @@ -325,6 +411,7 @@ + default-test @@ -333,7 +420,7 @@ - unit-tests + unit-test test @@ -345,7 +432,7 @@ - integration-tests + integration-test test @@ -359,6 +446,8 @@ **/*UnitTests.java **/OpenJpa* **/EclipseLink* + **/MySql* + **/Postgres* -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar @@ -367,7 +456,7 @@ - eclipselink-tests + eclipselink-test test diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java new file mode 100644 index 0000000000..2bde920383 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -0,0 +1,252 @@ +/* + * Copyright 2015-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.jpa.repository.procedures; + +import static org.assertj.core.api.Assertions.*; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Properties; + +import javax.persistence.Entity; +import javax.persistence.EntityManagerFactory; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.NamedStoredProcedureQuery; +import javax.sql.DataSource; +import javax.transaction.Transactional; + +import org.hibernate.dialect.MySQL8Dialect; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.jdbc.datasource.init.DataSourceInitializer; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.testcontainers.containers.MySQLContainer; + +import com.mysql.cj.jdbc.MysqlDataSource; + +/** + * Testcase to verify {@link org.springframework.jdbc.object.StoredProcedure}s work with MySQL. + * + * @author Gabriel Basilio + * @author Greg Turnquist + */ +@Transactional +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = MySqlStoredProcedureIntegrationTests.Config.class) +public class MySqlStoredProcedureIntegrationTests { + + @Autowired EmployeeRepositoryWithNoCursor repository; + + @Test // #2256 + void testGenericSingleObjectFromResultSet() { + + Object[] employee = repository.genericSingleObjectFromResultSet(); + + assertThat(employee).containsExactly( // + new Object[] { 3, "Fanny" }, // + new Object[] { 4, "Gabriel" }); + } + + @Test // #2256 + void testGenericObjectsFromResultSet() { + + List employees = repository.genericObjectsFromResultSet(); + + assertThat(employees).containsExactly( // + new Object[] { 3, "Fanny" }, // + new Object[] { 4, "Gabriel" }); + } + + @Test // #2256 + void testEntityListFromResultSet() { + + List employees = repository.entityListFromResultSet(); + + assertThat(employees).containsExactly( // + new Employee(3, "Fanny"), // + new Employee(4, "Gabriel")); + } + + @Test // #2256 + void testNamedOutputParameter() { + + List employees = repository.namedOutputParameter(); + + assertThat(employees).containsExactly( // + new Employee(3, "Fanny"), // + new Employee(4, "Gabriel")); + } + + @Test // #2256 + void testSingleEntityFromResultSet() { + + Employee employee = repository.singleEntityFromResultSet(); + + assertThat(employee).isEqualTo(new Employee(3, "Fanny")); + } + + @Test // #2256 + void testEntityListFromSingleRowResultSet() { + + List employees = repository.entityListFromSingleRowResultSet(); + + assertThat(employees).containsExactly(new Employee(3, "Fanny")); + } + + @Test // #2256 + void testNoResultSet() { + + int count = repository.noResultSet(); + + assertThat(count).isEqualTo(2); + } + + @Test // #2256 + void testEntityListFromNamedProcedure() { + + List employees = repository.entityListFromNamedProcedure(); + + assertThat(employees).containsExactly( // + new Employee(3, "Fanny"), // + new Employee(4, "Gabriel")); + } + + @Data + @Entity + @AllArgsConstructor + @NoArgsConstructor + @NamedStoredProcedureQuery(name = "get_employees_mysql", procedureName = "get_employees", + resultClasses = Employee.class) + public static class Employee { + + @Id @GeneratedValue private Integer id; + private String name; + } + + @Transactional + public interface EmployeeRepositoryWithNoCursor extends JpaRepository { + + @Procedure(value = "get_employees") + Object[] genericSingleObjectFromResultSet(); + + @Procedure(value = "get_employees") + List genericObjectsFromResultSet(); + + @Procedure(value = "get_employees") + List entityListFromResultSet(); + + @Procedure(value = "get_employees", outputParameterName = "p_employees") + List namedOutputParameter(); + + @Procedure(value = "get_single_employee") + Employee singleEntityFromResultSet(); + + @Procedure(value = "get_single_employee") + List entityListFromSingleRowResultSet(); + + @Procedure(value = "get_employees_count") + Integer noResultSet(); + + @Procedure(name = "get_employees_mysql") + List entityListFromNamedProcedure(); + } + + @EnableJpaRepositories(considerNestedRepositories = true, basePackageClasses = Config.class, + includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = EmployeeRepositoryWithNoCursor.class)) + @EnableTransactionManagement + static class Config { + + private MySQLContainer MYSQL_CONTAINER; + + @Bean + public DataSource dataSource() { + + if (MYSQL_CONTAINER == null) { + + MYSQL_CONTAINER = new MySQLContainer<>("mysql:8.0.24") // + .withUsername("test") // + .withPassword("test") // + .withConfigurationOverride(""); + MYSQL_CONTAINER.start(); + } + + MysqlDataSource dataSource = new MysqlDataSource(); + dataSource.setUrl(MYSQL_CONTAINER.getJdbcUrl()); + dataSource.setUser("root"); + dataSource.setPassword(MYSQL_CONTAINER.getPassword()); + dataSource.setDatabaseName(MYSQL_CONTAINER.getDatabaseName()); + + return dataSource; + } + + @Bean + public AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { + + LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setPersistenceUnitRootLocation("simple-persistence"); + factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + factoryBean.setPackagesToScan(this.getClass().getPackage().getName()); + + Properties properties = new Properties(); + properties.setProperty("hibernate.hbm2ddl.auto", "create"); + properties.setProperty("hibernate.dialect", MySQL8Dialect.class.getCanonicalName()); + factoryBean.setJpaProperties(properties); + + return factoryBean; + } + + @Bean + PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + return new JpaTransactionManager(entityManagerFactory); + } + + @Bean + DataSourceInitializer initializer(DataSource dataSource) { + + DataSourceInitializer initializer = new DataSourceInitializer(); + initializer.setDataSource(dataSource); + + ClassPathResource script = new ClassPathResource("scripts/mysql-stored-procedures.sql"); + ResourceDatabasePopulator populator = new ResourceDatabasePopulator(script); + populator.setSeparator(";;"); + initializer.setDatabasePopulator(populator); + + return initializer; + } + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java new file mode 100644 index 0000000000..ae6b42366e --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -0,0 +1,252 @@ +/* + * Copyright 2015-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.jpa.repository.procedures; + +import static org.assertj.core.api.Assertions.*; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Properties; + +import javax.persistence.Entity; +import javax.persistence.EntityManagerFactory; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.ParameterMode; +import javax.persistence.StoredProcedureParameter; +import javax.sql.DataSource; +import javax.transaction.Transactional; + +import org.hibernate.dialect.PostgreSQL91Dialect; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.postgresql.ds.PGSimpleDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.jdbc.datasource.init.DataSourceInitializer; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.testcontainers.containers.PostgreSQLContainer; + +/** + * Testcase to verify {@link org.springframework.jdbc.object.StoredProcedure}s work with Postgres. + * + * @author Gabriel Basilio + * @author Greg Turnquist + */ +@Transactional +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = PostgresStoredProcedureIntegrationTests.Config.class) +public class PostgresStoredProcedureIntegrationTests { + + @Autowired EmployeeRepositoryWithRefCursor repository; + + @Test // 2256 + void testGenericSingleObjectFromResultSet() { + + Object[] employee = repository.genericSingleObjectFromResultSet(); + + assertThat(employee).containsExactly( // + new Object[] { new BigDecimal("3"), "Fanny" }, // + new Object[] { new BigDecimal("4"), "Gabriel" }); + } + + @Test // 2256 + void testGenericObjectsFromResultSet() { + + List employees = repository.genericObjectsFromResultSet(); + + assertThat(employees).containsExactly( // + new Object[] { new BigDecimal("3"), "Fanny" }, // + new Object[] { new BigDecimal("4"), "Gabriel" }); + } + + @Test // 2256 + void testEntityListFromResultSet() { + + List employees = repository.entityListFromResultSet(); + + assertThat(employees).containsExactly( // + new Employee(3, "Fanny"), // + new Employee(4, "Gabriel")); + } + + @Test // 2256 + void testNamedOutputParameter() { + + List employees = repository.namedOutputParameter(); + + assertThat(employees).containsExactly( // + new Employee(3, "Fanny"), // + new Employee(4, "Gabriel")); + } + + @Test // 2256 + void testSingleEntityFromResultSet() { + + Employee employee = repository.singleEntityFromResultSet(); + + assertThat(employee).isEqualTo(new Employee(3, "Fanny")); + } + + @Test // 2256 + void testEntityListFromSingleRowResultSet() { + + List employees = repository.entityListFromSingleRowResultSet(); + + assertThat(employees).containsExactly(new Employee(3, "Fanny")); + } + + @Test // 2256 + void testNoResultSet() { + + int count = repository.noResultSet(); + + assertThat(count).isEqualTo(2); + } + + @Test // 2256 + void testEntityListFromNamedProcedure() { + + List employees = repository.entityListFromNamedProcedure(); + + assertThat(employees).containsExactly( // + new Employee(3, "Fanny"), // + new Employee(4, "Gabriel")); + } + + @Data + @Entity + @AllArgsConstructor + @NoArgsConstructor + @NamedStoredProcedureQuery(name = "get_employees_postgres", procedureName = "get_employees", + parameters = { @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class) }, + resultClasses = Employee.class) + public static class Employee { + + @Id @GeneratedValue private Integer id; + private String name; + } + + @Transactional + public interface EmployeeRepositoryWithRefCursor extends JpaRepository { + + @Procedure(value = "get_employees", refCursor = true) + Object[] genericSingleObjectFromResultSet(); + + @Procedure(value = "get_employees", refCursor = true) + List genericObjectsFromResultSet(); + + @Procedure(value = "get_employees", refCursor = true) + List entityListFromResultSet(); + + @Procedure(value = "get_employees", outputParameterName = "p_employees", refCursor = true) + List namedOutputParameter(); + + @Procedure(value = "get_single_employee", refCursor = true) + Employee singleEntityFromResultSet(); + + @Procedure(value = "get_single_employee", refCursor = true) + List entityListFromSingleRowResultSet(); + + @Procedure(value = "get_employees_count") + Integer noResultSet(); + + @Procedure(name = "get_employees_postgres", refCursor = true) + List entityListFromNamedProcedure(); + } + + @EnableJpaRepositories(considerNestedRepositories = true, + includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = EmployeeRepositoryWithRefCursor.class)) + @EnableTransactionManagement + static class Config { + + private PostgreSQLContainer POSTGRESQL_CONTAINER; + + @Bean + public DataSource dataSource() { + + if (POSTGRESQL_CONTAINER == null) { + + POSTGRESQL_CONTAINER = new PostgreSQLContainer<>("postgres:9.6.12") // + .withUsername("postgres"); + POSTGRESQL_CONTAINER.start(); + } + + PGSimpleDataSource dataSource = new PGSimpleDataSource(); + dataSource.setUrl(POSTGRESQL_CONTAINER.getJdbcUrl()); + dataSource.setUser(POSTGRESQL_CONTAINER.getUsername()); + dataSource.setPassword(POSTGRESQL_CONTAINER.getPassword()); + + return dataSource; + } + + @Bean + public AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { + + LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setPersistenceUnitRootLocation("simple-persistence"); + factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + factoryBean.setPackagesToScan(this.getClass().getPackage().getName()); + + Properties properties = new Properties(); + properties.setProperty("hibernate.hbm2ddl.auto", "create"); + properties.setProperty("hibernate.dialect", PostgreSQL91Dialect.class.getCanonicalName()); + factoryBean.setJpaProperties(properties); + + return factoryBean; + } + + @Bean + PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + return new JpaTransactionManager(entityManagerFactory); + } + + @Bean + DataSourceInitializer initializer(DataSource dataSource) { + + DataSourceInitializer initializer = new DataSourceInitializer(); + initializer.setDataSource(dataSource); + + ClassPathResource script = new ClassPathResource("scripts/postgres-stored-procedures.sql"); + ResourceDatabasePopulator populator = new ResourceDatabasePopulator(script); + populator.setSeparator(";;"); + initializer.setDatabasePopulator(populator); + + return initializer; + } + } +} diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml index ad5cbef50a..2bb9893fdc 100644 --- a/src/test/resources/logback.xml +++ b/src/test/resources/logback.xml @@ -7,10 +7,15 @@ - + + + + + + - + \ No newline at end of file diff --git a/src/test/resources/scripts/mysql-stored-procedures.sql b/src/test/resources/scripts/mysql-stored-procedures.sql new file mode 100644 index 0000000000..b696e3772b --- /dev/null +++ b/src/test/resources/scripts/mysql-stored-procedures.sql @@ -0,0 +1,27 @@ +CREATE TABLE employee +( + ID int NOT NULL, + name varchar(50) NOT NULL, + primary key (ID) +);; + +INSERT INTO employee (ID, NAME) VALUES (3, 'Fanny');; +INSERT INTO employee (ID, NAME) VALUES (4, 'Gabriel');; + +DROP PROCEDURE IF EXISTS get_employees;; +CREATE PROCEDURE get_employees() +BEGIN + SELECT * FROM employee; +END;; + +DROP PROCEDURE IF EXISTS get_employees_count;; +CREATE PROCEDURE get_employees_count(OUT employees INT) +BEGIN + SELECT COUNT(*) into employees FROM employee; +END;; + +DROP PROCEDURE IF EXISTS get_single_employee;; +CREATE PROCEDURE get_single_employee() +BEGIN + SELECT * FROM employee WHERE employee.ID = 3; +END;; diff --git a/src/test/resources/scripts/postgres-stored-procedures.sql b/src/test/resources/scripts/postgres-stored-procedures.sql new file mode 100644 index 0000000000..9d8226ee75 --- /dev/null +++ b/src/test/resources/scripts/postgres-stored-procedures.sql @@ -0,0 +1,45 @@ +CREATE TABLE employee +( + ID numeric NOT NULL, + name text COLLATE pg_catalog."default" NOT NULL, + CONSTRAINT employee_pkey PRIMARY KEY (ID) +);; + +INSERT INTO employee (ID, NAME) VALUES (3, 'Fanny');; +INSERT INTO employee (ID, NAME) VALUES (4, 'Gabriel');; + +CREATE OR REPLACE FUNCTION get_employees() + RETURNS refcursor + LANGUAGE 'plpgsql' +AS +$BODY$ +DECLARE + ref refcursor; +BEGIN + OPEN ref FOR SELECT * FROM employee; + RETURN ref; +END; +$BODY$;; + +CREATE OR REPLACE FUNCTION get_employees_count() + RETURNS integer + LANGUAGE 'plpgsql' +AS +$BODY$ +BEGIN + RETURN (SELECT COUNT(*) FROM employee); +END; +$BODY$;; + +CREATE OR REPLACE FUNCTION get_single_employee() + RETURNS refcursor + LANGUAGE 'plpgsql' +AS +$BODY$ +DECLARE + ref refcursor; +BEGIN + OPEN ref FOR SELECT * FROM employee WHERE employee.ID = 3; + RETURN ref; +END; +$BODY$;; From e9c118506fcc7756a7876d1f0f912e76801ba2e2 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 23 Jul 2021 16:40:59 -0500 Subject: [PATCH 042/821] Remove changelog. Closes #2267. --- src/main/resources/changelog.txt | 2339 ------------------------------ 1 file changed, 2339 deletions(-) delete mode 100644 src/main/resources/changelog.txt diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt deleted file mode 100644 index c50cd70408..0000000000 --- a/src/main/resources/changelog.txt +++ /dev/null @@ -1,2339 +0,0 @@ -Spring Data JPA Changelog -========================= - -Changes in version 2.6.0-M1 (2021-07-16) ----------------------------------------- -* #2257 - Upgrade to Hibernate 5.5.3.Final. -* #2254 - Adapt to consolidated PersistentEntity API. -* #2251 - Small mistake in Documentation. -* #2247 - Remove explicit version for Mockito, so we use the one of Spring Data Build. -* #2245 - Drop serialVersionUID from AbstractPersistable. -* #2239 - Use Spring nullability annotations instead of JSR305. -* #2234 - Spring Data documentation examples have wrong annotations. -* #2231 - Fix build failures with Java 16. -* #2230 - Error in documentation regarding manually assigned identifiers in "Entity State-detection Strategies" when there is a Version property. -* #2228 - ExpressionBasedStringQuery does not quote all parameter expressions. -* #2218 - Unused org.aspectj:aspectjrt compile time dependency. -* #2207 - Improve wording on transactional methods inherited from `SimpleJpaRepository` and repository fragments. -* #2202 - Introduce template method for easier customization of fragments. -* #2184 - Update CI to Java 16. -* #2129 - Missing anchor on "Custom Namespace Attributes" causes TOC to break. -* #2009 - Document that DTOs don't work with native queries [DATAJPA-1714]. -* #1959 - @Procedure annotation doesn't work with cursors (NULL when using REF_CURSOR) and ResultSets that don't come from cursors [DATAJPA-1657]. - - -Changes in version 2.4.11 (2021-07-16) --------------------------------------- -* #2245 - Drop serialVersionUID from AbstractPersistable. - - -Changes in version 2.5.2 (2021-06-22) -------------------------------------- -* #2234 - Spring Data documentation examples have wrong annotations. -* #2230 - Error in documentation regarding manually assigned identifiers in "Entity State-detection Strategies" when there is a Version property. -* #2228 - ExpressionBasedStringQuery does not quote all parameter expressions. -* #2009 - Document that DTOs don't work with native queries [DATAJPA-1714]. - - -Changes in version 2.4.10 (2021-06-22) --------------------------------------- -* #2234 - Spring Data documentation examples have wrong annotations. -* #2230 - Error in documentation regarding manually assigned identifiers in "Entity State-detection Strategies" when there is a Version property. -* #2228 - ExpressionBasedStringQuery does not quote all parameter expressions. -* #2009 - Document that DTOs don't work with native queries [DATAJPA-1714]. - - -Changes in version 2.5.1 (2021-05-14) -------------------------------------- -* #2207 - Improve wording on transactional methods inherited from `SimpleJpaRepository` and repository fragments. -* #2202 - Introduce template method for easier customization of fragments. -* #2129 - Missing anchor on "Custom Namespace Attributes" causes TOC to break. - - -Changes in version 2.4.9 (2021-05-14) -------------------------------------- -* #2207 - Improve wording on transactional methods inherited from `SimpleJpaRepository` and repository fragments. - - -Changes in version 2.5.0 (2021-04-14) -------------------------------------- -* #2195 - Upgrade dependencies. - - -Changes in version 2.4.8 (2021-04-14) -------------------------------------- -* #2195 - Upgrade dependencies. - - -Changes in version 2.3.9.RELEASE (2021-04-14) ---------------------------------------------- -* #2195 - Upgrade dependencies. - - -Changes in version 2.4.7 (2021-03-31) -------------------------------------- - - -Changes in version 2.5.0-RC1 (2021-03-31) ------------------------------------------ -* #2191 - `JpaPersistentPropertyImpl` now considers super types association and target type. - - -Changes in version 2.5.0-M5 (2021-03-17) ----------------------------------------- -* #2173 - Improve JavaDoc. -* #2169 - DATAJPA-1377 added JpaRepository.getById, deprecated JpaRepository.ge…. -* #2159 - Fix raw use of JpaSpecificationExecutor in docs. -* #2148 - DATAJPA-1574 - Add support for saveAllAndFlush. -* #2111 - Sorting on nested optional relations produces inner joins instead of outer joins [DATAJPA-1822]. -* #1883 - saveAndFlush could accept iterable as well [DATAJPA-1574]. -* #1850 - Seems BeanDefinitionUtils class should be abstract [DATAJPA-1540]. -* #1697 - JpaRepository.getOne(ID id) method name is inconsistent with other xById(ID id) method names [DATAJPA-1377]. -* #436 - DATAJPA-1822 - Ensure left outer join for nested optional attributes. - - -Changes in version 2.4.6 (2021-03-17) -------------------------------------- -* #2173 - Improve JavaDoc. -* #2159 - Fix raw use of JpaSpecificationExecutor in docs. -* #2111 - Sorting on nested optional relations produces inner joins instead of outer joins [DATAJPA-1822]. -* #436 - DATAJPA-1822 - Ensure left outer join for nested optional attributes. - - -Changes in version 2.3.8.RELEASE (2021-03-17) ---------------------------------------------- -* #2173 - Improve JavaDoc. -* #2159 - Fix raw use of JpaSpecificationExecutor in docs. -* #2111 - Sorting on nested optional relations produces inner joins instead of outer joins [DATAJPA-1822]. -* #436 - DATAJPA-1822 - Ensure left outer join for nested optional attributes. - - -Changes in version 2.5.0-M4 (2021-02-18) ----------------------------------------- - - -Changes in version 2.4.5 (2021-02-18) -------------------------------------- - - -Changes in version 2.5.0-M3 (2021-02-17) ----------------------------------------- -* #2146 - SpecificationComposition reversed predicates. -* #2127 - Update repository after GitHub issues migration. - - -Changes in version 2.4.4 (2021-02-17) -------------------------------------- -* #2146 - SpecificationComposition reversed predicates. - - -Changes in version 2.3.7.RELEASE (2021-02-17) ---------------------------------------------- -* DATAJPA-1828 - Update CI jobs with Docker Login. -* DATAJPA-1827 - @Modifing not supported by vavr integration. -* #2149 - The 2.3.x build is broken. -* #2146 - SpecificationComposition reversed predicates. -* #2133 - Update copyright year to 2021. -* #2129 - Missing anchor on "Custom Namespace Attributes" causes TOC to break. -* #2127 - Update repository after GitHub issues migration. - - -Changes in version 2.2.13.RELEASE (2021-02-17) ----------------------------------------------- -* #2133 - Update copyright year to 2021. -* #2129 - Missing anchor on "Custom Namespace Attributes" causes TOC to break. -* #2127 - Update repository after GitHub issues migration. - - -Changes in version 2.5.0-M2 (2021-01-13) ----------------------------------------- -* DATAJPA-1828 - Update CI jobs with Docker Login. -* DATAJPA-1827 - @Modifing not supported by vavr integration. -* #2133 - Update copyright year to 2021. -* #2129 - Missing anchor on "Custom Namespace Attributes" causes TOC to break. -* #2127 - Update repository after GitHub issues migration. - - -Changes in version 2.4.3 (2021-01-13) -------------------------------------- -* DATAJPA-1828 - Update CI jobs with Docker Login. -* DATAJPA-1827 - @Modifing not supported by vavr integration. -* #2133 - Update copyright year to 2021. -* #2129 - Missing anchor on "Custom Namespace Attributes" causes TOC to break. -* #2127 - Update repository after GitHub issues migration. - - -Changes in version 2.4.2 (2020-12-09) -------------------------------------- -* DATAJPA-1819 - Release 2.4.2 (2020.0.2). - - -Changes in version 2.5.0-M1 (2020-12-09) ----------------------------------------- -* DATAJPA-1818 - Implement CrudRepository.delete(Iterable ids). -* DATAJPA-1813 - Remove dependency to joda.time. -* DATAJPA-1812 - Release 2.5 M1 (2021.0.0). - - -Changes in version 2.3.6.RELEASE (2020-12-09) ---------------------------------------------- -* DATAJPA-1810 - Release 2.3.6 (Neumann SR6). - - -Changes in version 2.2.12.RELEASE (2020-12-09) ----------------------------------------------- -* DATAJPA-1809 - Release 2.2.12 (Moore SR12). - - -Changes in version 2.4.1 (2020-11-11) -------------------------------------- -* DATAJPA-1811 - Release 2.4.1 (2020.0.1). - - -Changes in version 2.4.0 (2020-10-28) -------------------------------------- -* DATAJPA-1808 - Deprecate ThreeTenBackPortJpaConverters. -* DATAJPA-1807 - Small edit of reference doc. -* DATAJPA-1803 - EnableJpaAuditing: JavaDoc references joda.time instead of java.time. -* DATAJPA-1800 - Avoid language in documentation that might be considered insensitive. -* DATAJPA-1798 - Editing pass for the documentation. -* DATAJPA-1794 - Release 2.4 GA (2020.0.0). -* DATAJPA-1793 - 5.3.8. Modifying Queries: unclear reference to "custom methods". -* DATAJPA-1792 - 5.3.5. Using Sort: typo "be either". -* DATAJPA-1789 - "Distinct" not listed in supported query keywords. -* DATAJPA-1787 - Update documentation to use Java 8 lambdas. -* DATAJPA-1622 - Nullable annotations used for non-null returning Specification methods. - - -Changes in version 2.3.5.RELEASE (2020-10-28) ---------------------------------------------- -* DATAJPA-1807 - Small edit of reference doc. -* DATAJPA-1803 - EnableJpaAuditing: JavaDoc references joda.time instead of java.time. -* DATAJPA-1800 - Avoid language in documentation that might be considered insensitive. -* DATAJPA-1798 - Editing pass for the documentation. -* DATAJPA-1793 - 5.3.8. Modifying Queries: unclear reference to "custom methods". -* DATAJPA-1792 - 5.3.5. Using Sort: typo "be either". -* DATAJPA-1789 - "Distinct" not listed in supported query keywords. -* DATAJPA-1787 - Update documentation to use Java 8 lambdas. -* DATAJPA-1776 - Release 2.3.5 (Neumann SR5). -* DATAJPA-1622 - Nullable annotations used for non-null returning Specification methods. -* DATAJPA-1198 - Querydsl Creates toLowerCase Expression for all Types. - - -Changes in version 2.2.11.RELEASE (2020-10-28) ----------------------------------------------- -* DATAJPA-1807 - Small edit of reference doc. -* DATAJPA-1803 - EnableJpaAuditing: JavaDoc references joda.time instead of java.time. -* DATAJPA-1801 - Upgrade Eclipselink dependencies on maintenance branches. -* DATAJPA-1800 - Avoid language in documentation that might be considered insensitive. -* DATAJPA-1798 - Editing pass for the documentation. -* DATAJPA-1793 - 5.3.8. Modifying Queries: unclear reference to "custom methods". -* DATAJPA-1792 - 5.3.5. Using Sort: typo "be either". -* DATAJPA-1789 - "Distinct" not listed in supported query keywords. -* DATAJPA-1787 - Update documentation to use Java 8 lambdas. -* DATAJPA-1775 - Release 2.2.11 (Moore SR11). - - -Changes in version 2.1.21.RELEASE (2020-10-28) ----------------------------------------------- -* DATAJPA-1806 - Release 2.1.21 (Lovelace SR21). - - -Changes in version 2.4.0-RC2 (2020-10-14) ------------------------------------------ -* DATAJPA-1783 - Migrate tests to JUnit 5. -* DATAJPA-1782 - Adopt to changes in Spring Data Commons. -* DATAJPA-1777 - Release 2.4 RC2 (2020.0.0). -* DATAJPA-1198 - Querydsl Creates toLowerCase Expression for all Types. - - -Changes in version 2.4.0-RC1 (2020-09-16) ------------------------------------------ -* DATAJPA-1768 - Release 2.4 RC1 (2020.0.0). - - -Changes in version 2.3.4.RELEASE (2020-09-16) ---------------------------------------------- -* DATAJPA-1769 - Release 2.3.4 (Neumann SR4). - - -Changes in version 2.2.10.RELEASE (2020-09-16) ----------------------------------------------- -* DATAJPA-1761 - Sample transactional code having unnecessary save operation. -* DATAJPA-1758 - Release 2.2.10 (Moore SR10). - - -Changes in version 2.1.20.RELEASE (2020-09-16) ----------------------------------------------- -* DATAJPA-1761 - Sample transactional code having unnecessary save operation. -* DATAJPA-1757 - Release 2.1.20 (Lovelace SR20). - - -Changes in version 2.3.3.RELEASE (2020-08-12) ---------------------------------------------- -* DATAJPA-1761 - Sample transactional code having unnecessary save operation. -* DATAJPA-1759 - Release 2.3.3 (Neumann SR3). - - -Changes in version 2.4.0-M2 (2020-08-12) ----------------------------------------- -* DATAJPA-1761 - Sample transactional code having unnecessary save operation. -* DATAJPA-1754 - assert the entity is null in SimpleJpaRepository.save. -* DATAJPA-1749 - Release 2.4 M2 (2020.0.0). -* DATAJPA-872 - Query hints on repository methods do not support hints that allow duplicate values for the same name. - - -Changes in version 2.3.2.RELEASE (2020-07-22) ---------------------------------------------- -* DATAJPA-1743 - Release 2.3.2 (Neumann SR2). - - -Changes in version 2.2.9.RELEASE (2020-07-22) ---------------------------------------------- -* DATAJPA-1742 - Release 2.2.9 (Moore SR9). - - -Changes in version 2.1.19.RELEASE (2020-07-22) ----------------------------------------------- -* DATAJPA-1741 - Release 2.1.19 (Lovelace SR19). - - -Changes in version 2.4.0-M1 (2020-06-25) ----------------------------------------- -* DATAJPA-1740 - Use standard Spring code of conduct. -* DATAJPA-1728 - Delombok source files. -* DATAJPA-1727 - Release 2.4 M1 (2020.0.0). - - -Changes in version 2.3.1.RELEASE (2020-06-10) ---------------------------------------------- -* DATAJPA-1726 - Release 2.3.1 (Neumann SR1). - - -Changes in version 2.2.8.RELEASE (2020-06-10) ---------------------------------------------- -* DATAJPA-1724 - Remove superfluous parenthesis from Javadoc. -* DATAJPA-1720 - Remove Travis CI. -* DATAJPA-1716 - Release 2.2.8 (Moore SR8). -* DATAJPA-1576 - JpaMetamodelEntityInformation.getId(…) uses field access for proxied entities. - - -Changes in version 2.1.18.RELEASE (2020-06-10) ----------------------------------------------- -* DATAJPA-1724 - Remove superfluous parenthesis from Javadoc. -* DATAJPA-1720 - Remove Travis CI. -* DATAJPA-1715 - Release 2.1.18 (Lovelace SR18). -* DATAJPA-1576 - JpaMetamodelEntityInformation.getId(…) uses field access for proxied entities. - - -Changes in version 2.3.0.RELEASE (2020-05-12) ---------------------------------------------- -* DATAJPA-1724 - Remove superfluous parenthesis from Javadoc. -* DATAJPA-1720 - Remove Travis CI. -* DATAJPA-1717 - Release 2.3 GA (Neumann). -* DATAJPA-1576 - JpaMetamodelEntityInformation.getId(…) uses field access for proxied entities. - - -Changes in version 2.3.0.RC2 (2020-04-28) ------------------------------------------ -* DATAJPA-1712 - Fix minor documentation typo. -* DATAJPA-1708 - Use JDK 14 for Java.NEXT CI testing. -* DATAJPA-1706 - Release 2.3 RC2 (Neumann). - - -Changes in version 2.2.7.RELEASE (2020-04-28) ---------------------------------------------- -* DATAJPA-1712 - Fix minor documentation typo. -* DATAJPA-1702 - Release 2.2.7 (Moore SR7). - - -Changes in version 2.1.17.RELEASE (2020-04-28) ----------------------------------------------- -* DATAJPA-1712 - Fix minor documentation typo. -* DATAJPA-1696 - Reluctant parsing introduced in DATAJPA-1679 causes problems. -* DATAJPA-1691 - Javadoc spelling errors. -* DATAJPA-1688 - Release 2.1.17 (Lovelace SR17). -* DATAJPA-1679 - QueryUtils.getProjection dies not treat DISTINCT queries well. -* DATAJPA-1664 - Typo errors in documentation of @EntityGraph. - - -Changes in version 2.3.0.RC1 (2020-03-31) ------------------------------------------ -* DATAJPA-1696 - Reluctant parsing introduced in DATAJPA-1679 causes problems. -* DATAJPA-1695 - Unbound parameters for queries using SpEL and quotation. -* DATAJPA-1693 - Release 2.3 RC1 (Neumann). -* DATAJPA-1660 - Add JpaSort.of(…) factory methods. - - -Changes in version 2.2.6.RELEASE (2020-03-25) ---------------------------------------------- -* DATAJPA-1696 - Reluctant parsing introduced in DATAJPA-1679 causes problems. -* DATAJPA-1691 - Javadoc spelling errors. -* DATAJPA-1689 - Release 2.2.6 (Moore SR6). -* DATAJPA-1679 - QueryUtils.getProjection dies not treat DISTINCT queries well. -* DATAJPA-1664 - Typo errors in documentation of @EntityGraph. - - -Changes in version 2.3.0.M4 (2020-03-11) ----------------------------------------- -* DATAJPA-1691 - Javadoc spelling errors. -* DATAJPA-1680 - Release 2.3 M4 (Neumann). -* DATAJPA-1679 - QueryUtils.getProjection dies not treat DISTINCT queries well. -* DATAJPA-1664 - Typo errors in documentation of @EntityGraph. - - -Changes in version 2.2.5.RELEASE (2020-02-26) ---------------------------------------------- -* DATAJPA-1676 - Typo in some comments. -* DATAJPA-1673 - Bulk delete query is invalid. -* DATAJPA-1666 - Release 2.2.5 (Moore SR5). - - -Changes in version 2.1.16.RELEASE (2020-02-26) ----------------------------------------------- -* DATAJPA-1676 - Typo in some comments. -* DATAJPA-1673 - Bulk delete query is invalid. -* DATAJPA-1665 - Release 2.1.16 (Lovelace SR16). - - -Changes in version 2.3.0.M3 (2020-02-12) ----------------------------------------- -* DATAJPA-1676 - Typo in some comments. -* DATAJPA-1673 - Bulk delete query is invalid. -* DATAJPA-1669 - Release 2.3 M3 (Neumann). - - -Changes in version 2.3.0.M2 (2020-01-17) ----------------------------------------- -* DATAJPA-1668 - Release 2.3 M2 (Neumann). - - -Changes in version 2.3.0.M1 (2020-01-16) ----------------------------------------- -* DATAJPA-1663 - Remove PersistenceProvider.potentiallyConvertEmptyCollection(…). -* DATAJPA-1656 - Update copyright years to 2020. -* DATAJPA-1652 - Using || (pipes) along with named parameters in custom queries raises an exception. -* DATAJPA-1647 - JpaRepository memory leak with Pageable argument. -* DATAJPA-1639 - Fix position of _ in MetaModel classes in the documentation. -* DATAJPA-1632 - Fix documentation for countQuery. -* DATAJPA-1631 - Enable building with JDK11+. -* DATAJPA-1630 - Disable Hibernate 6 build until it is more mature. -* DATAJPA-1620 - Update versions of Hibernate and Eclipselink. -* DATAJPA-1619 - Allow repository method parameters of type Iterable for derived IN-queries. -* DATAJPA-1618 - Fix repository for EclipseLink. -* DATAJPA-1617 - Fix quotation marks in documentation. -* DATAJPA-1607 - Release 2.3 M1 (Neumann). -* DATAJPA-1497 - Allow to add a special parameter type from a Spring Data JPA extension. - - -Changes in version 2.2.4.RELEASE (2020-01-15) ---------------------------------------------- -* DATAJPA-1663 - Remove PersistenceProvider.potentiallyConvertEmptyCollection(…). -* DATAJPA-1656 - Update copyright years to 2020. -* DATAJPA-1652 - Using || (pipes) along with named parameters in custom queries raises an exception. -* DATAJPA-1647 - JpaRepository memory leak with Pageable argument. -* DATAJPA-1641 - Release 2.2.4 (Moore SR4). - - -Changes in version 2.1.15.RELEASE (2020-01-15) ----------------------------------------------- -* DATAJPA-1656 - Update copyright years to 2020. -* DATAJPA-1652 - Using || (pipes) along with named parameters in custom queries raises an exception. -* DATAJPA-1640 - Release 2.1.15 (Lovelace SR15). - - -Changes in version 2.2.3.RELEASE (2019-12-04) ---------------------------------------------- -* DATAJPA-1639 - Fix position of _ in MetaModel classes in the documentation. -* DATAJPA-1634 - Release 2.2.3 (Moore SR3). - - -Changes in version 2.1.14.RELEASE (2019-12-04) ----------------------------------------------- -* DATAJPA-1639 - Fix position of _ in MetaModel classes in the documentation. -* DATAJPA-1633 - Release 2.1.14 (Lovelace SR14). - - -Changes in version 2.2.2.RELEASE (2019-11-18) ---------------------------------------------- -* DATAJPA-1632 - Fix documentation for countQuery. -* DATAJPA-1623 - Release 2.2.2 (Moore SR2). -* DATAJPA-1619 - Allow repository method parameters of type Iterable for derived IN-queries. - - -Changes in version 2.1.13.RELEASE (2019-11-18) ----------------------------------------------- -* DATAJPA-1632 - Fix documentation for countQuery. -* DATAJPA-1621 - Release 2.1.13 (Lovelace SR13). - - -Changes in version 2.2.1.RELEASE (2019-11-04) ---------------------------------------------- -* DATAJPA-1620 - Update versions of Hibernate and Eclipselink. -* DATAJPA-1618 - Fix repository for EclipseLink. -* DATAJPA-1617 - Fix quotation marks in documentation. -* DATAJPA-1606 - Release 2.2.1 (Moore SR1). - - -Changes in version 2.1.12.RELEASE (2019-11-04) ----------------------------------------------- -* DATAJPA-1620 - Update versions of Hibernate and Eclipselink. -* DATAJPA-1618 - Fix repository for EclipseLink. -* DATAJPA-1617 - Fix quotation marks in documentation. -* DATAJPA-1605 - Release 2.1.12 (Lovelace SR12). - - -Changes in version 2.2.0.RELEASE (2019-09-30) ---------------------------------------------- -* DATAJPA-1604 - Move off deprecated QPageRequest constructors. -* DATAJPA-1583 - Release 2.2 GA (Moore). - - -Changes in version 2.1.11.RELEASE (2019-09-30) ----------------------------------------------- -* DATAJPA-1600 - Document recommended way to work with entities with manually assigned identifiers. -* DATAJPA-1594 - Tighten nullability constraints in Querydsl implementation. -* DATAJPA-1582 - Local schema resolution fails for spring-repository.xsd. -* DATAJPA-1581 - Release 2.1.11 (Lovelace SR11). - - -Changes in version 2.2.0.RC3 (2019-09-06) ------------------------------------------ -* DATAJPA-1600 - Document recommended way to work with entities with manually assigned identifiers. -* DATAJPA-1594 - Tighten nullability constraints in Querydsl implementation. -* DATAJPA-1584 - Release 2.2 RC3 (Moore). -* DATAJPA-1582 - Local schema resolution fails for spring-repository.xsd. - - -Changes in version 2.2.0.RC2 (2019-08-05) ------------------------------------------ -* DATAJPA-1580 - Adapt EntityManagerBeanDefinitionRegistrarPostProcessor to latest changes in Spring Framework 5.2. -* DATAJPA-1579 - Support multiple out parameters with nullable output data ad hoc stored procedure. -* DATAJPA-1575 - Performance improvements in repository invocation handling. -* DATAJPA-1571 - Migrate from Hamcrest to AssertJ. -* DATAJPA-1569 - Revise readme for a consistent structure. -* DATAJPA-1564 - Count query is faulty created when query have multiline select clause. -* DATAJPA-1560 - CPU consumption of IllegalArgumentException during id derivation for entities with composite keys. -* DATAJPA-1559 - Release 2.2 RC2 (Moore). -* DATAJPA-1545 - Introduce Jenkins CI. -* DATAJPA-1372 - Error while using ExampleMatcher with @embeded entity in spring data jpa. -* DATAJPA-1368 - QueryDslJpaRepository.createQuery() throws NPE on WebSphere. -* DATAJPA-1303 - Supports findByFieldIgnoreCaseIn(Collection<> fieldValues). -* DATAJPA-1061 - Sorting doesn't work for field alias. -* DATAJPA-433 - findAll(Iterable ids) not working on EclipseLink and Oracle. - - -Changes in version 2.1.10.RELEASE (2019-08-05) ----------------------------------------------- -* DATAJPA-1580 - Adapt EntityManagerBeanDefinitionRegistrarPostProcessor to latest changes in Spring Framework 5.2. -* DATAJPA-1571 - Migrate from Hamcrest to AssertJ. -* DATAJPA-1564 - Count query is faulty created when query have multiline select clause. -* DATAJPA-1563 - Fix NoHTTP errors. -* DATAJPA-1560 - CPU consumption of IllegalArgumentException during id derivation for entities with composite keys. -* DATAJPA-1558 - Release 2.1.10 (Lovelace SR10). -* DATAJPA-1545 - Introduce Jenkins CI. -* DATAJPA-1372 - Error while using ExampleMatcher with @embeded entity in spring data jpa. -* DATAJPA-1368 - QueryDslJpaRepository.createQuery() throws NPE on WebSphere. -* DATAJPA-1230 - Make JpaRepositoryFactory agnostic of JpaRepository implementation. -* DATAJPA-1061 - Sorting doesn't work for field alias. -* DATAJPA-433 - findAll(Iterable ids) not working on EclipseLink and Oracle. - - -Changes in version 1.11.23.RELEASE (2019-08-05) ------------------------------------------------ -* DATAJPA-1564 - Count query is faulty created when query have multiline select clause. -* DATAJPA-1562 - Got IndexOutOfBoundsException when projecting data on maps list. -* DATAJPA-1556 - Update faq.adoc: typo fix. -* DATAJPA-1545 - Introduce Jenkins CI. -* DATAJPA-1543 - Release 1.11.23 (Ingalls SR23). -* DATAJPA-1535 - Simple Repository delete of unexisting entity generates insert + delete SQL. -* DATAJPA-1500 - order by clause is not removed from derived count query when newline after the order by clause. -* DATAJPA-1368 - QueryDslJpaRepository.createQuery() throws NPE on WebSphere. -* DATAJPA-1061 - Sorting doesn't work for field alias. - - -Changes in version 2.2.0.RC1 (2019-06-14) ------------------------------------------ -* DATAJPA-1557 - Build fails with new Hibernate versions due to changed errror messages. -* DATAJPA-1556 - Update faq.adoc: typo fix. -* DATAJPA-1554 - JpaQueryExecution.SlicedExecution fails on Unpaged pageabled. -* DATAJPA-1552 - Create security policy readme. -* DATAJPA-1549 - Various forms of polishing from the community. -* DATAJPA-1535 - Simple Repository delete of unexisting entity generates insert + delete SQL. -* DATAJPA-1529 - Release 2.2 RC1 (Moore). -* DATAJPA-1500 - order by clause is not removed from derived count query when newline after the order by clause. -* DATAJPA-1451 - Inconsistent Assert messages. -* DATAJPA-1182 - Validate Collection/Scalar parameter declarations in PartTree query. -* DATAJPA-707 - Support multiple out parameters ad hoc stored procedure. - - -Changes in version 2.1.9.RELEASE (2019-06-14) ---------------------------------------------- -* DATAJPA-1556 - Update faq.adoc: typo fix. -* DATAJPA-1554 - JpaQueryExecution.SlicedExecution fails on Unpaged pageabled. -* DATAJPA-1542 - Release 2.1.9 (Lovelace SR9). -* DATAJPA-1535 - Simple Repository delete of unexisting entity generates insert + delete SQL. -* DATAJPA-1500 - order by clause is not removed from derived count query when newline after the order by clause. -* DATAJPA-1451 - Inconsistent Assert messages. - - -Changes in version 1.11.22.RELEASE (2019-05-13) ------------------------------------------------ -* DATAJPA-1539 - Release 1.11.22 (Ingalls SR22). -* DATAJPA-1534 - Wildcard character needs to be escaped in Spring data JPA. - - -Changes in version 2.1.8.RELEASE (2019-05-13) ---------------------------------------------- -* DATAJPA-1538 - Release 2.1.8 (Lovelace SR8). -* DATAJPA-1534 - Wildcard character needs to be escaped in Spring data JPA. - - -Changes in version 2.2.0.M4 (2019-05-13) ----------------------------------------- -* DATAJPA-1534 - Wildcard character needs to be escaped in Spring data JPA. -* DATAJPA-1533 - Builds are broken due to an outdated Mockito version. -* DATAJPA-1531 - Release 2.2 M4 (Moore). -* DATAJPA-1530 - JpaMetamodelEntityInformation does not unwrap ByteBuddy proxies. -* DATAJPA-1506 - Wrong alias detection for native query. - - -Changes in version 1.11.21.RELEASE (2019-05-10) ------------------------------------------------ -* DATAJPA-1523 - Release 1.11.21 (Ingalls SR21). -* DATAJPA-1506 - Wrong alias detection for native query. - - -Changes in version 2.1.7.RELEASE (2019-05-10) ---------------------------------------------- -* DATAJPA-1533 - Builds are broken due to an outdated Mockito version. -* DATAJPA-1530 - JpaMetamodelEntityInformation does not unwrap ByteBuddy proxies. -* DATAJPA-1525 - Release 2.1.7 (Lovelace SR7). -* DATAJPA-1506 - Wrong alias detection for native query. - - -Changes in version 2.2.0.M3 (2019-04-11) ----------------------------------------- -* DATAJPA-1522 - LIKE escape character needs to be escaped ins source value to be handled. -* DATAJPA-1519 - Query derivation keywords implemented using like expressions do not properly escape underscores and percentages. -* DATAJPA-1515 - Move off deprecations in Spring Data Commons. -* DATAJPA-1513 - Release 2.2 M3 (Moore). - - -Changes in version 2.1.6.RELEASE (2019-04-01) ---------------------------------------------- -* DATAJPA-1522 - LIKE escape character needs to be escaped ins source value to be handled. -* DATAJPA-1519 - Query derivation keywords implemented using like expressions do not properly escape underscores and percentages. -* DATAJPA-1504 - Release 2.1.6 (Lovelace SR6). -* DATAJPA-1498 - Upgrade Hibernate 5.4 build profile to 5.4.1. -* DATAJPA-1404 - Is Null query does not return entities. -* DATAJPA-1156 - Query hints are applied to count queries for QueryDSL even though forCounting is set to false. - - -Changes in version 2.0.14.RELEASE (2019-04-01) ----------------------------------------------- -* DATAJPA-1522 - LIKE escape character needs to be escaped ins source value to be handled. -* DATAJPA-1519 - Query derivation keywords implemented using like expressions do not properly escape underscores and percentages. -* DATAJPA-1502 - Typo in reference docs on Specification. -* DATAJPA-1498 - Upgrade Hibernate 5.4 build profile to 5.4.1. -* DATAJPA-1494 - Release 2.0.14 (Kay SR14). -* DATAJPA-1404 - Is Null query does not return entities. -* DATAJPA-1321 - JpaRepository.getOne(…) JavaDoc is misleading. -* DATAJPA-1297 - outputParameterName in @Procedure is not being used to create the StoredProcedureAttributes. - - -Changes in version 1.11.20.RELEASE (2019-04-01) ------------------------------------------------ -* DATAJPA-1522 - LIKE escape character needs to be escaped ins source value to be handled. -* DATAJPA-1521 - Release 1.11.20 (Ingalls SR20). - - -Changes in version 1.11.19.RELEASE (2019-04-01) ------------------------------------------------ -* DATAJPA-1519 - Query derivation keywords implemented using like expressions do not properly escape underscores and percentages. -* DATAJPA-1502 - Typo in reference docs on Specification. -* DATAJPA-1493 - Release 1.11.19 (Ingalls SR19). -* DATAJPA-1297 - outputParameterName in @Procedure is not being used to create the StoredProcedureAttributes. - - -Changes in version 2.2.0.M2 (2019-03-07) ----------------------------------------- -* DATAJPA-1502 - Typo in reference docs on Specification. -* DATAJPA-1492 - Introduce Concourse CI. -* DATAJPA-1490 - Update copyright years to 2019. -* DATAJPA-1487 - JpaMetamodelCacheCleanup must not be defined as lazy bean. -* DATAJPA-1485 - Ambiguity around @Modifying within documentation. -* DATAJPA-1484 - Add build profile for Hibernate 6 snapshots. -* DATAJPA-1483 - Upgrade Hibernate 5.4 build profile after GA release. -* DATAJPA-1481 - Release 2.2 M2 (Moore). -* DATAJPA-1404 - Is Null query does not return entities. -* DATAJPA-1321 - JpaRepository.getOne(…) JavaDoc is misleading. -* DATAJPA-1297 - outputParameterName in @Procedure is not being used to create the StoredProcedureAttributes. -* DATAJPA-1156 - Query hints are applied to count queries for QueryDSL even though forCounting is set to false. - - -Changes in version 2.1.5.RELEASE (2019-02-13) ---------------------------------------------- -* DATAJPA-1502 - Typo in reference docs on Specification. -* DATAJPA-1495 - Release 2.1.5 (Lovelace SR5). -* DATAJPA-1321 - JpaRepository.getOne(…) JavaDoc is misleading. -* DATAJPA-1297 - outputParameterName in @Procedure is not being used to create the StoredProcedureAttributes. - - -Changes in version 2.1.4.RELEASE (2019-01-10) ---------------------------------------------- -* DATAJPA-1490 - Update copyright years to 2019. -* DATAJPA-1487 - JpaMetamodelCacheCleanup must not be defined as lazy bean. -* DATAJPA-1485 - Ambiguity around @Modifying within documentation. -* DATAJPA-1484 - Add build profile for Hibernate 6 snapshots. -* DATAJPA-1483 - Upgrade Hibernate 5.4 build profile after GA release. -* DATAJPA-1473 - Add build profile for released Hibernate 5.4. -* DATAJPA-1472 - Fix Travis build failing due to moved JDK8. -* DATAJPA-1468 - Release 2.1.4 (Lovelace SR4). - - -Changes in version 2.0.13.RELEASE (2019-01-10) ----------------------------------------------- -* DATAJPA-1490 - Update copyright years to 2019. -* DATAJPA-1485 - Ambiguity around @Modifying within documentation. -* DATAJPA-1484 - Add build profile for Hibernate 6 snapshots. -* DATAJPA-1483 - Upgrade Hibernate 5.4 build profile after GA release. -* DATAJPA-1473 - Add build profile for released Hibernate 5.4. -* DATAJPA-1472 - Fix Travis build failing due to moved JDK8. -* DATAJPA-1467 - Release 2.0.13 (Kay SR13). - - -Changes in version 1.11.18.RELEASE (2019-01-10) ------------------------------------------------ -* DATAJPA-1490 - Update copyright years to 2019. -* DATAJPA-1485 - Ambiguity around @Modifying within documentation. -* DATAJPA-1483 - Upgrade Hibernate 5.4 build profile after GA release. -* DATAJPA-1473 - Add build profile for released Hibernate 5.4. -* DATAJPA-1472 - Fix Travis build failing due to moved JDK8. -* DATAJPA-1465 - Release 1.11.18 (Ingalls SR18). - - -Changes in version 2.2.0.M1 (2018-12-11) ----------------------------------------- -* DATAJPA-1479 - Simplify reference documentation setup. -* DATAJPA-1478 - Release 2.2 M1 (Moore). -* DATAJPA-1473 - Add build profile for released Hibernate 5.4. -* DATAJPA-1472 - Fix Travis build failing due to moved JDK8. -* DATAJPA-1455 - Build fails with a ClassNotFoundException. -* DATAJPA-1450 - Remove `sudo` from .travis.yml. -* DATAJPA-1449 - Remove Specifications from code and documentation. -* DATAJPA-1448 - JpaAuditing duplicate bean definitions. -* DATAJPA-1446 - Memory leak in class JpaMetamodel. -* DATAJPA-1443 - Small corrections in JavaDoc. -* DATAJPA-1418 - Interface-based Projections - Generate inner join instead of left join. -* DATAJPA-1335 - add missing @override annotations. -* DATAJPA-767 - Interoperation between @Version and entity state detection strategies is not documented. - - -Changes in version 2.1.3.RELEASE (2018-11-27) ---------------------------------------------- -* DATAJPA-1450 - Remove `sudo` from .travis.yml. -* DATAJPA-1448 - JpaAuditing duplicate bean definitions. -* DATAJPA-1447 - Release 2.1.3 (Lovelace SR3). - - -Changes in version 2.0.12.RELEASE (2018-11-27) ----------------------------------------------- -* DATAJPA-1450 - Remove `sudo` from .travis.yml. -* DATAJPA-1448 - JpaAuditing duplicate bean definitions. -* DATAJPA-1446 - Memory leak in class JpaMetamodel. -* DATAJPA-1443 - Small corrections in JavaDoc. -* DATAJPA-1440 - Release 2.0.12 (Kay SR12). -* DATAJPA-1418 - Interface-based Projections - Generate inner join instead of left join. - - -Changes in version 1.11.17.RELEASE (2018-11-27) ------------------------------------------------ -* DATAJPA-1450 - Remove `sudo` from .travis.yml. -* DATAJPA-1448 - JpaAuditing duplicate bean definitions. -* DATAJPA-1441 - Release 1.11.17 (Ingalls SR17). - - -Changes in version 2.1.2.RELEASE (2018-10-29) ---------------------------------------------- -* DATAJPA-1446 - Memory leak in class JpaMetamodel. -* DATAJPA-1443 - Small corrections in JavaDoc. -* DATAJPA-1439 - Release 2.1.2 (Lovelace SR2). -* DATAJPA-1418 - Interface-based Projections - Generate inner join instead of left join. - - -Changes in version 1.11.16.RELEASE (2018-10-15) ------------------------------------------------ -* DATAJPA-1419 - Release 1.11.16 (Ingalls SR16). - - -Changes in version 2.0.11.RELEASE (2018-10-15) ----------------------------------------------- -* DATAJPA-1420 - Release 2.0.11 (Kay SR11). - - -Changes in version 2.1.1.RELEASE (2018-10-15) ---------------------------------------------- -* DATAJPA-1424 - Release 2.1.1 (Lovelace SR1). - - -Changes in version 2.1.0.RELEASE (2018-09-21) ---------------------------------------------- -* DATAJPA-1416 - Saving lazy-loaded entity with IdClass throws TypeMismatchException. -* DATAJPA-1407 - Release 2.1 GA (Lovelace). -* DATAJPA-1333 - Minor performance improvements. - - -Changes in version 2.0.10.RELEASE (2018-09-10) ----------------------------------------------- -* DATAJPA-1416 - Saving lazy-loaded entity with IdClass throws TypeMismatchException. -* DATAJPA-1405 - Assumption in EclipsLinkNamespaceUserRepositoryTests need to include 2.7.3. -* DATAJPA-1402 - Ubgrade Hibernate 5.3 build profile to 5.3.5. -* DATAJPA-1400 - Upgrade Eclipselink 2.7 build profiles. -* DATAJPA-1399 - Add Travis build executions for Spring 5.1. -* DATAJPA-1398 - Upgrade to Hibernate 5.2.17. -* DATAJPA-1387 - Release 2.0.10 (Kay SR10). -* DATAJPA-1379 - NamedQueries with "SELECT NEW" constructor expression cause QueryException. -* DATAJPA-1333 - Minor performance improvements. - - -Changes in version 1.11.15.RELEASE (2018-09-10) ------------------------------------------------ -* DATAJPA-1405 - Assumption in EclipsLinkNamespaceUserRepositoryTests need to include 2.7.3. -* DATAJPA-1403 - 1.11.x Builds are broken for Hibernate 5.x. -* DATAJPA-1402 - Ubgrade Hibernate 5.3 build profile to 5.3.5. -* DATAJPA-1400 - Upgrade Eclipselink 2.7 build profiles. -* DATAJPA-1399 - Add Travis build executions for Spring 5.1. -* DATAJPA-1398 - Upgrade to Hibernate 5.2.17. -* DATAJPA-1388 - Release 1.11.15 (Ingalls SR15). -* DATAJPA-1333 - Minor performance improvements. - - -Changes in version 2.1.0.RC2 (2018-08-20) ------------------------------------------ -* DATAJPA-1405 - Assumption in EclipsLinkNamespaceUserRepositoryTests need to include 2.7.3. -* DATAJPA-1402 - Ubgrade Hibernate 5.3 build profile to 5.3.5. -* DATAJPA-1400 - Upgrade Eclipselink 2.7 build profiles. -* DATAJPA-1398 - Upgrade to Hibernate 5.2.17. -* DATAJPA-1397 - Add support for deferred repository initialization. -* DATAJPA-1394 - Avoid repeated lookups of EntityPathResolver from JpaRepositoryFactory. -* DATAJPA-1386 - Release 2.1 RC2 (Lovelace). -* DATAJPA-1379 - NamedQueries with "SELECT NEW" constructor expression cause QueryException. - - -Changes in version 1.11.14.RELEASE (2018-07-27) ------------------------------------------------ -* DATAJPA-1374 - Upgrade Hibernate 5.3 build profile to 5.3.2. -* DATAJPA-1363 - Alias for complex Function. -* DATAJPA-1361 - Release 1.11.14 (Ingalls SR14). - - -Changes in version 2.0.9.RELEASE (2018-07-26) ---------------------------------------------- -* DATAJPA-1382 - Remove superfluous call to getVersionProperty in JpaPersistentEntityImpl. -* DATAJPA-1375 - JpaRepositoryFactory.getTargetRepository(…) still requires ID to extend Serializable. -* DATAJPA-1374 - Upgrade Hibernate 5.3 build profile to 5.3.2. -* DATAJPA-1363 - Alias for complex Function. -* DATAJPA-1362 - Release 2.0.9 (Kay SR9). -* DATAJPA-1281 - Improve exception messages for invalid parameter definitions in repository query methods. -* DATAJPA-1163 - spring-data fails when #{#entityName} SpEL is using in countQuery definition. - - -Changes in version 2.1.0.RC1 (2018-07-26) ------------------------------------------ -* DATAJPA-1382 - Remove superfluous call to getVersionProperty in JpaPersistentEntityImpl. -* DATAJPA-1374 - Upgrade Hibernate 5.3 build profile to 5.3.2. -* DATAJPA-1363 - Alias for complex Function. -* DATAJPA-1359 - Move to org.hibernate.query.Query to avoid reflection overhead in HibernateUtils. -* DATAJPA-1348 - Upgrade build to Hibernate 5.3 GA. -* DATAJPA-1347 - Improve proxy detection to properly detect Hibernate 5.3 proxies. -* DATAJPA-1346 - Release 2.1 RC1 (Lovelace). -* DATAJPA-1336 - remove unused imports. -* DATAJPA-1281 - Improve exception messages for invalid parameter definitions in repository query methods. -* DATAJPA-1267 - Integrate newly created API to detect SpEL expressions in repository queries. -* DATAJPA-1238 - Unnecessary LEFT OUTER JOIN when a WHERE could have been used. -* DATAJPA-1163 - spring-data fails when #{#entityName} SpEL is using in countQuery definition. - - -Changes in version 2.0.8.RELEASE (2018-06-13) ---------------------------------------------- -* DATAJPA-1359 - Move to org.hibernate.query.Query to avoid reflection overhead in HibernateUtils. -* DATAJPA-1348 - Upgrade build to Hibernate 5.3 GA. -* DATAJPA-1347 - Improve proxy detection to properly detect Hibernate 5.3 proxies. -* DATAJPA-1343 - Add coverpage for epub documentation output. -* DATAJPA-1341 - Fix typo in AbstractAuditable. -* DATAJPA-1340 - Release 2.0.8 (Kay SR8). -* DATAJPA-1336 - remove unused imports. -* DATAJPA-1334 - Named queries using a constructor expression not working properly. -* DATAJPA-1280 - Using manual SqlResultSetMapping with named native queries fails. -* DATAJPA-1238 - Unnecessary LEFT OUTER JOIN when a WHERE could have been used. - - -Changes in version 1.11.13.RELEASE (2018-06-13) ------------------------------------------------ -* DATAJPA-1348 - Upgrade build to Hibernate 5.3 GA. -* DATAJPA-1347 - Improve proxy detection to properly detect Hibernate 5.3 proxies. -* DATAJPA-1343 - Add coverpage for epub documentation output. -* DATAJPA-1341 - Fix typo in AbstractAuditable. -* DATAJPA-1339 - Release 1.11.13 (Ingalls SR13). -* DATAJPA-1334 - Named queries using a constructor expression not working properly. -* DATAJPA-1280 - Using manual SqlResultSetMapping with named native queries fails. -* DATAJPA-1238 - Unnecessary LEFT OUTER JOIN when a WHERE could have been used. - - -Changes in version 2.1.0.M3 (2018-05-17) ----------------------------------------- -* DATAJPA-1344 - Adapt to SpEL extension API changes in Spring Data Commons. -* DATAJPA-1343 - Add coverpage for epub documentation output. -* DATAJPA-1341 - Fix typo in AbstractAuditable. -* DATAJPA-1334 - Named queries using a constructor expression not working properly. -* DATAJPA-1325 - Release 2.1 M3 (Lovelace). -* DATAJPA-1314 - Build Failure on Travis with profile eclipselink-27-next. -* DATAJPA-1282 - Meta-model classes no longer contained in binaries. -* DATAJPA-1280 - Using manual SqlResultSetMapping with named native queries fails. -* DATAJPA-1265 - Adapt to new EvaluationContextProvider API. - - -Changes in version 2.0.7.RELEASE (2018-05-08) ---------------------------------------------- -* DATAJPA-1323 - Documentation seems to be wrong about construction of the transaction manager. -* DATAJPA-1322 - Editing pass on documentation. -* DATAJPA-1318 - Native Query Projection is case sensitive. -* DATAJPA-1316 - Release 2.0.7 (Kay SR7). -* DATAJPA-1314 - Build Failure on Travis with profile eclipselink-27-next. -* DATAJPA-1282 - Meta-model classes no longer contained in binaries. - - -Changes in version 1.11.12.RELEASE (2018-05-08) ------------------------------------------------ -* DATAJPA-1323 - Documentation seems to be wrong about construction of the transaction manager. -* DATAJPA-1322 - Editing pass on documentation. -* DATAJPA-1315 - Release 1.11.12 (Ingalls SR12). -* DATAJPA-1314 - Build Failure on Travis with profile eclipselink-27-next. -* DATAJPA-1282 - Meta-model classes no longer contained in binaries. - - -Changes in version 2.1.0.M2 (2018-04-13) ----------------------------------------- -* DATAJPA-1323 - Documentation seems to be wrong about construction of the transaction manager. -* DATAJPA-1322 - Editing pass on documentation. -* DATAJPA-1320 - Adapt to API changes in Spring Data Commons. -* DATAJPA-1318 - Native Query Projection is case sensitive. -* DATAJPA-1310 - Upgrade to Hibernate 5.2.16. -* DATAJPA-1307 - Parameters without index nor name don't get detected as such anymore. -* DATAJPA-1301 - Map instances returned by JpaRepository queries do not handle null map values correctly. -* DATAJPA-1300 - Parameters are not passed to proxied native Query instance on EclipseLink. -* DATAJPA-1295 - Minor polishing in QueryUtils and ClasspathScanningPersistenceUnitPostProcessor. -* DATAJPA-1287 - Export composable repositories via CDI. -* DATAJPA-1284 - Use generated database names in tests. -* DATAJPA-1274 - Upgrade to Hibernate 5.2.14. -* DATAJPA-1273 - Named query execution doesn't use Tuple execution for projections. -* DATAJPA-1269 - Upgrade Eclipselink to 2.7.1. -* DATAJPA-1268 - Upgrade to Hibernate 5.2.13. -* DATAJPA-1263 - Release 2.1 M2 (Lovelace). -* DATAJPA-1255 - Native query with SpEL creates invalid SQL. -* DATAJPA-1250 - Entity classes loaded before EclipseLink LTW is initialized. -* DATAJPA-1248 - Add build profiles for Hibernate 5.3. -* DATAJPA-1241 - "object is not an instance of declaring class" in JpaQueryExecution.StreamExecution. -* DATAJPA-1233 - Count query fails when missing any defined parameter. -* DATAJPA-1105 - @IdClass doesn't work when we use nested composit key with String element. -* DATAJPA-928 - NativeQuery with Pagination validation error at startup. -* DATAJPA-658 - Improve identifier metadata detection for XML based entity mappings. - - -Changes in version 2.0.6.RELEASE (2018-04-04) ---------------------------------------------- -* DATAJPA-1310 - Upgrade to Hibernate 5.2.16. -* DATAJPA-1309 - Projection data is not mapped correctly on Hibernate 5.0. -* DATAJPA-1307 - Parameters without index nor name don't get detected as such anymore. -* DATAJPA-1301 - Map instances returned by JpaRepository queries do not handle null map values correctly. -* DATAJPA-1300 - Parameters are not passed to proxied native Query instance on EclipseLink. -* DATAJPA-1295 - Minor polishing in QueryUtils and ClasspathScanningPersistenceUnitPostProcessor. -* DATAJPA-1284 - Use generated database names in tests. -* DATAJPA-1276 - Release 2.0.6 (Kay SR6). -* DATAJPA-1269 - Upgrade Eclipselink to 2.7.1. -* DATAJPA-1255 - Native query with SpEL creates invalid SQL. -* DATAJPA-1250 - Entity classes loaded before EclipseLink LTW is initialized. - - -Changes in version 1.11.11.RELEASE (2018-04-04) ------------------------------------------------ -* DATAJPA-1313 - JpaPersistentPropertyImpl.tryResolveEntityType fails with NPE. -* DATAJPA-1310 - Upgrade to Hibernate 5.2.16. -* DATAJPA-1309 - Projection data is not mapped correctly on Hibernate 5.0. -* DATAJPA-1301 - Map instances returned by JpaRepository queries do not handle null map values correctly. -* DATAJPA-1284 - Use generated database names in tests. -* DATAJPA-1274 - Upgrade to Hibernate 5.2.14. -* DATAJPA-1273 - Named query execution doesn't use Tuple execution for projections. -* DATAJPA-1269 - Upgrade Eclipselink to 2.7.1. -* DATAJPA-1268 - Upgrade to Hibernate 5.2.13. -* DATAJPA-1252 - Fix line endings. -* DATAJPA-1251 - Release 1.11.11 (Ingalls SR11). -* DATAJPA-1248 - Add build profiles for Hibernate 5.3. -* DATAJPA-1241 - "object is not an instance of declaring class" in JpaQueryExecution.StreamExecution. -* DATAJPA-1105 - @IdClass doesn't work when we use nested composit key with String element. -* DATAJPA-806 - add flushAutomatically attribute to @Modifying annotation. -* DATAJPA-658 - Improve identifier metadata detection for XML based entity mappings. - - -Changes in version 2.0.5.RELEASE (2018-02-28) ---------------------------------------------- -* DATAJPA-1274 - Upgrade to Hibernate 5.2.14. -* DATAJPA-1273 - Named query execution doesn't use Tuple execution for projections. -* DATAJPA-1270 - Release 2.0.5 (Kay SR5). -* DATAJPA-1105 - @IdClass doesn't work when we use nested composit key with String element. - - -Changes in version 2.0.4.RELEASE (2018-02-19) ---------------------------------------------- -* DATAJPA-1268 - Upgrade to Hibernate 5.2.13. -* DATAJPA-1261 - Revert optimizations made for existing entities in implementation of CrudRepository.save(…). -* DATAJPA-1253 - Release 2.0.4 (Kay SR4). -* DATAJPA-1248 - Add build profiles for Hibernate 5.3. -* DATAJPA-1241 - "object is not an instance of declaring class" in JpaQueryExecution.StreamExecution. -* DATAJPA-1233 - Count query fails when missing any defined parameter. -* DATAJPA-928 - NativeQuery with Pagination validation error at startup. -* DATAJPA-806 - add flushAutomatically attribute to @Modifying annotation. -* DATAJPA-658 - Improve identifier metadata detection for XML based entity mappings. - - -Changes in version 2.1.0.M1 (2018-02-06) ----------------------------------------- -* DATAJPA-1261 - Revert optimizations made for existing entities in implementation of CrudRepository.save(…). -* DATAJPA-1252 - Fix line endings. -* DATAJPA-1243 - Update copyright years to 2018. -* DATAJPA-1235 - CrudRepository Exception - Required name for ParameterBinding not available. -* DATAJPA-1234 - querydsl.packageSuffix messes with QueryDslJpaRepository. -* DATAJPA-1220 - Mention snapshot repository in readme. -* DATAJPA-1213 - Upgrade to Hibernate 5.2.12. -* DATAJPA-1212 - Temporarily add milestone repository to plugin repositories. -* DATAJPA-1208 - Improve code readability. -* DATAJPA-1205 - BeanCreationException for custom repository after upgrading to Ingalls-SR8. -* DATAJPA-1200 - Colon database table separator confused with named parameter. -* DATAJPA-1197 - Release 2.1 M1 (Lovelace). -* DATAJPA-1195 - Fix sporadic hanging test in build. -* DATAJPA-1173 - Spring Data Projection with OneToMany returns too many results. -* DATAJPA-949 - Add support for JPA 2.2 Query.getResultStream(). -* DATAJPA-931 - Optimize calls to SimpleJpaRepository.save(…) if the entity is still attached. -* DATAJPA-863 - Missing parameters in findAll* Repository method should throw a descriptive exception. -* DATAJPA-806 - add flushAutomatically attribute to @Modifying annotation. - - -Changes in version 2.0.3.RELEASE (2018-01-24) ---------------------------------------------- -* DATAJPA-1252 - Fix line endings. -* DATAJPA-1229 - Release 2.0.3 (Kay SR3). -* DATAJPA-1213 - Upgrade to Hibernate 5.2.12. -* DATAJPA-931 - Optimize calls to SimpleJpaRepository.save(…) if the entity is still attached. - - -Changes in version 1.11.10.RELEASE (2018-01-24) ------------------------------------------------ -* DATAJPA-1228 - Release 1.11.10 (Ingalls SR10). -* DATAJPA-1213 - Upgrade to Hibernate 5.2.12. - - -Changes in version 2.0.2.RELEASE (2017-11-27) ---------------------------------------------- -* DATAJPA-1215 - Release 2.0.2 (Kay SR2). -* DATAJPA-863 - Missing parameters in findAll* Repository method should throw a descriptive exception. - - -Changes in version 1.11.9.RELEASE (2017-11-27) ----------------------------------------------- -* DATAJPA-1209 - Compatibility with Hibernate < 5.2.11 broken for projections on native queries. -* DATAJPA-1205 - BeanCreationException for custom repository after upgrading to Ingalls-SR8. -* DATAJPA-1203 - Release 1.11.9 (Ingalls SR9). -* DATAJPA-1185 - Query methods with dynamic projections which return Optional, Stream or Page throw NoSuchElementException. -* DATAJPA-1173 - Spring Data Projection with OneToMany returns too many results. -* DATAJPA-863 - Missing parameters in findAll* Repository method should throw a descriptive exception. - - -Changes in version 2.0.1.RELEASE (2017-10-27) ---------------------------------------------- -* DATAJPA-1208 - Improve code readability. -* DATAJPA-1205 - BeanCreationException for custom repository after upgrading to Ingalls-SR8. -* DATAJPA-1200 - Colon database table separator confused with named parameter. -* DATAJPA-1196 - Release 2.0.1 (Kay SR1). -* DATAJPA-1195 - Fix sporadic hanging test in build. -* DATAJPA-1173 - Spring Data Projection with OneToMany returns too many results. -* DATAJPA-949 - Add support for JPA 2.2 Query.getResultStream(). - - -Changes in version 1.11.8.RELEASE (2017-10-11) ----------------------------------------------- -* DATAJPA-1192 - Some JavaDocs (@params) are not up-to-date. -* DATAJPA-1191 - Upgrade to EclipseLink 2.6.5. -* DATAJPA-1190 - Add build profile for EclipseLink 2.7. -* DATAJPA-1181 - Upgrade to Hibernate 5.2.11. -* DATAJPA-1179 - Duplicate SpEL expressions create only one parameter name but multiple bindings. -* DATAJPA-1176 - Release 1.11.8 (Ingalls SR8). -* DATAJPA-1175 - Unable to deploy a web application on Oracle WebLogic Server 12.2.1.1.0. -* DATAJPA-1067 - Fix typo in JPQL keyword mapping. -* DATAJPA-1057 - Fix reference to SimpleJpaRepository in reference docs. -* DATAJPA-980 - Projections with native queries don't work as expected. -* DATAJPA-949 - Add support for JPA 2.2 Query.getResultStream(). - - -Changes in version 2.0.0.RELEASE (2017-10-02) ---------------------------------------------- -* DATAJPA-1194 - Add @Nullable annotation to methods in Jsr310JpaConverters which may return null. -* DATAJPA-1192 - Some JavaDocs (@params) are not up-to-date. -* DATAJPA-1191 - Upgrade to EclipseLink 2.6.5. -* DATAJPA-1190 - Add build profile for EclipseLink 2.7. -* DATAJPA-1188 - Adapt to changed Spring Framework 5 documentation structure. -* DATAJPA-1183 - Add explicit automatic module name for Java 9. -* DATAJPA-1181 - Upgrade to Hibernate 5.2.11. -* DATAJPA-1180 - Upgrade to OpenWebBeans 2.0.1. -* DATAJPA-1179 - Duplicate SpEL expressions create only one parameter name but multiple bindings. -* DATAJPA-1177 - Release 2.0 GA (Kay). -* DATAJPA-1175 - Unable to deploy a web application on Oracle WebLogic Server 12.2.1.1.0. -* DATAJPA-1067 - Fix typo in JPQL keyword mapping. -* DATAJPA-1057 - Fix reference to SimpleJpaRepository in reference docs. -* DATAJPA-980 - Projections with native queries don't work as expected. - - -Changes in version 2.0.0.RC3 (2017-09-11) ------------------------------------------ -* DATAJPA-1174 - Jsr310JpaConverters don't handle null values properly. -* DATAJPA-1172 - Improve error reporting for invalid type being used for SpEL parameter binding. -* DATAJPA-1171 - Query created for CrudRepository.existsById(…) shouldn't contain static '1 = 1'. -* DATAJPA-1170 - Unify Specification and Specifications API. -* DATAJPA-1168 - Introduce nullable annotations for API validation. -* DATAJPA-1159 - Upgrade to Hibernate 5.2.10. -* DATAJPA-1158 - Remove references to OpenJPA from codebase. -* DATAJPA-1155 - Release 2.0 RC3 (Kay). -* DATAJPA-1058 - Better exception when named method parameter is required but missing. - - -Changes in version 1.11.7.RELEASE (2017-09-11) ----------------------------------------------- -* DATAJPA-1172 - Improve error reporting for invalid type being used for SpEL parameter binding. -* DATAJPA-1171 - Query created for CrudRepository.existsById(…) shouldn't contain static '1 = 1'. -* DATAJPA-1157 - Release 1.11.7 (Ingalls SR7). -* DATAJPA-1080 - Use From instead of Root for order in QueryUtils. - - -Changes in version 1.11.6.RELEASE (2017-07-26) ----------------------------------------------- -* DATAJPA-1152 - Release 1.11.6 (Ingalls SR6). - - -Changes in version 2.0.0.RC2 (2017-07-25) ------------------------------------------ -* DATAJPA-1153 - Release 2.0 RC2 (Kay). - - -Changes in version 2.0.0.RC1 (2017-07-25) ------------------------------------------ -* DATAJPA-1150 - JpaQueryCreator.QueryPreparer.getDynamicSort(…) must not return null. -* DATAJPA-1148 - Incorrect behavior of SimpleJpaRepository.existsById(…) due to changes with Optional. -* DATAJPA-1143 - Adapt to API changes in mapping subsystem. -* DATAJPA-1140 - Mixing SpEL and String @Param is not possible if not all parameters are used as SpEL expression in @Query on Repository. -* DATAJPA-1135 - Release 2.0 RC1 (Kay). - - -Changes in version 1.11.5.RELEASE (2017-07-24) ----------------------------------------------- -* DATAJPA-1132 - Release 1.11.5 (Ingalls SR5). - - -Changes in version 2.0.0.M4 (2017-06-14) ----------------------------------------- -* DATAJPA-1134 - Adopt tests to composable repositories. -* DATAJPA-1131 - Adopt to changed AnnotationUtils.getValue(…) behavior. -* DATAJPA-1116 - JpaSpecificationExecutor.findOne should return Optional. -* DATAJPA-1115 - Adapt to QuerydslPredicateExecutor API changes. -* DATAJPA-1114 - Release 2.0 M4 (Kay). -* DATAJPA-1113 - Aggregate Root Domain Events and saveAndFlush. -* DATAJPA-1110 - Adapt QueryByExampleExecutor API changes. -* DATAJPA-1086 - Improve exception message for missing parameter names on JDK 8. - - -Changes in version 1.11.4.RELEASE (2017-06-08) ----------------------------------------------- -* DATAJPA-1113 - Aggregate Root Domain Events and saveAndFlush. -* DATAJPA-1102 - Upgrade to Eclipselink 2.6.4. -* DATAJPA-1099 - Make sure Travis builds resolve dependencies only on repositories defined by our POM. -* DATAJPA-1098 - Javadoc on Specification. toPredicate should state that it may return null. -* DATAJPA-1097 - Release 1.11.4 (Ingalls SR4). -* DATAJPA-1086 - Improve exception message for missing parameter names on JDK 8. - - -Changes in version 1.10.11.RELEASE (2017-06-07) ------------------------------------------------ -* DATAJPA-1102 - Upgrade to Eclipselink 2.6.4. -* DATAJPA-1099 - Make sure Travis builds resolve dependencies only on repositories defined by our POM. -* DATAJPA-1098 - Javadoc on Specification. toPredicate should state that it may return null. -* DATAJPA-1096 - Release 1.10.11 (Hopper SR11). -* DATAJPA-1086 - Improve exception message for missing parameter names on JDK 8. - - -Changes in version 2.0.0.M3 (2017-05-09) ----------------------------------------- -* DATAJPA-1104 - Adapt to API changes in repository interfaces. -* DATAJPA-1102 - Upgrade to Eclipselink 2.6.4. -* DATAJPA-1099 - Make sure Travis builds resolve dependencies only on repositories defined by our POM. -* DATAJPA-1098 - Javadoc on Specification. toPredicate should state that it may return null. -* DATAJPA-1090 - Release 2.0 M3 (Kay). -* DATAJPA-1087 - Regression: query hints not applied to count queries for QueryDSL. -* DATAJPA-1075 - Dynamic entity graphs may still omit subgraphs. - - -Changes in version 1.10.10.RELEASE (2017-04-19) ------------------------------------------------ -* DATAJPA-1095 - Release 1.10.10 (Hopper SR10). - - -Changes in version 1.11.3.RELEASE (2017-04-19) ----------------------------------------------- -* DATAJPA-1094 - Release 1.11.3 (Ingalls SR3). - - -Changes in version 1.10.9.RELEASE (2017-04-19) ----------------------------------------------- -* DATAJPA-1087 - Regression: query hints not applied to count queries for QueryDSL. -* DATAJPA-1084 - Document rationale behind the execution behavior of derived deleteBy…(…) queries. -* DATAJPA-1075 - Dynamic entity graphs may still omit subgraphs. -* DATAJPA-1072 - Release 1.10.9 (Hopper SR9). - - -Changes in version 1.11.2.RELEASE (2017-04-19) ----------------------------------------------- -* DATAJPA-1087 - Regression: query hints not applied to count queries for QueryDSL. -* DATAJPA-1084 - Document rationale behind the execution behavior of derived deleteBy…(…) queries. -* DATAJPA-1075 - Dynamic entity graphs may still omit subgraphs. -* DATAJPA-1071 - Release 1.11.2 (Ingalls SR2). - - -Changes in version 2.0.0.M2 (2017-04-04) ----------------------------------------- -* DATAJPA-1085 - Upgrade Hibernate baseline to 5.2. -* DATAJPA-1084 - Document rationale behind the execution behavior of derived deleteBy…(…) queries. -* DATAJPA-1079 - Adapt to API changes in RepositoryConfigurationExtensionSupport. -* DATAJPA-1074 - Add support for IsEmpty and IsNotEmpty keyword in query derivation. -* DATAJPA-1054 - Remove references to single-argument assertion methods of Spring. -* DATAJPA-1049 - Update "what’s new" section in reference documentation. -* DATAJPA-1048 - Upgrade Hibernate 5.2 build profile to 5.2.7. -* DATAJPA-1044 - Bug in JpaCountQueryCreator when doing Paged execution of a Query that uses the Distinct Keyword. -* DATAJPA-1043 - Update project documentation with the CLA tool integration. -* DATAJPA-1042 - Migrate ticket references in test code to Spring Framework style. -* DATAJPA-1041 - Dynamic entity graphs may omit subgraphs. -* DATAJPA-1029 - JpaQueryCreator should deduplicate multi-select selections. -* DATAJPA-1027 - Upgrade to a newer JDK version on TravisCI. -* DATAJPA-1026 - Adapt API in RepositoryFactoryBeanSupport implementation. -* DATAJPA-1024 - Querrying empty table for non-standard JPA type results in IllegalStateExcepion with the message "No aliases found in result tuple". -* DATAJPA-1023 - Reject stream executions if not executed within transaction. -* DATAJPA-1019 - Make sure build runs on Hibernate 5. -* DATAJPA-1018 - Upgrade Hibernate build profile to latest releases. -* DATAJPA-1016 - Bump infrastructure requirement to JPA 2.1. -* DATAJPA-1014 - Register repository factory in spring.factories for multi-store support. -* DATAJPA-1009 - Release 2.0 M2 (Kay). -* DATAJPA-1005 - Error bootstrapping Spring Data JPA with EntityManagerFactory already semi-initialized. -* DATAJPA-920 - Add support for exists projection in repository query derivation. -* DATAJPA-914 - findAll with Iterable parameter gives JpaSystemException when using Hibernate 5.2. -* DATAJPA-911 - Assert compatibility with Hibernate 5.2. - - -Changes in version 1.10.8.RELEASE (2017-03-02) ----------------------------------------------- -* DATAJPA-1054 - Remove references to single-argument assertion methods of Spring. -* DATAJPA-1050 - Release 1.10.8 (Hopper SR8). - -Changes in version 1.11.1.RELEASE (2017-03-02) ----------------------------------------------- -* DATAJPA-1054 - Remove references to single-argument assertion methods of Spring. -* DATAJPA-1051 - Release 1.11.1 (Ingalls SR1). - - -Changes in version 1.10.7.RELEASE (2017-01-26) ----------------------------------------------- -* DATAJPA-1048 - Upgrade Hibernate 5.2 build profile to 5.2.7. -* DATAJPA-1044 - Bug in JpaCountQueryCreator when doing Paged execution of a Query that uses the Distinct Keyword. -* DATAJPA-1041 - Dynamic entity graphs may omit subgraphs. -* DATAJPA-1030 - Release 1.10.7 (Hopper SR7). -* DATAJPA-1005 - Error bootstrapping Spring Data JPA with EntityManagerFactory already semi-initialized. - - -Changes in version 1.11.0.RELEASE (2017-01-26) ----------------------------------------------- -* DATAJPA-1049 - Update "what’s new" section in reference documentation. -* DATAJPA-1048 - Upgrade Hibernate 5.2 build profile to 5.2.7. -* DATAJPA-1044 - Bug in JpaCountQueryCreator when doing Paged execution of a Query that uses the Distinct Keyword. -* DATAJPA-1043 - Update project documentation with the CLA tool integration. -* DATAJPA-1042 - Migrate ticket references in test code to Spring Framework style. -* DATAJPA-1041 - Dynamic entity graphs may omit subgraphs. -* DATAJPA-1031 - Release 1.11 GA (Ingalls). -* DATAJPA-1005 - Error bootstrapping Spring Data JPA with EntityManagerFactory already semi-initialized. - - -Changes in version 1.10.6.RELEASE (2016-12-21) ----------------------------------------------- -* DATAJPA-1029 - JpaQueryCreator should deduplicate multi-select selections. -* DATAJPA-1024 - Querrying empty table for non-standard JPA type results in IllegalStateExcepion with the message "No aliases found in result tuple". -* DATAJPA-1022 - Drop Travis CI build profile for Spring 5 for Hopper. -* DATAJPA-1021 - Make sure builds against Spring 5 skip tests for OpenJPA. -* DATAJPA-1020 - Tweak Travis build against Hibernate 5.2 to run on Spring 4.3. -* DATAJPA-1019 - Make sure build runs on Hibernate 5. -* DATAJPA-1018 - Upgrade Hibernate build profile to latest releases. -* DATAJPA-1000 - Pageable With Fetch not returning the right alias Name. -* DATAJPA-994 - Release 1.10.6 (Hopper SR6). -* DATAJPA-914 - findAll with Iterable parameter gives JpaSystemException when using Hibernate 5.2. -* DATAJPA-911 - Assert compatibility with Hibernate 5.2. - - -Changes in version 1.11.0.RC1 (2016-12-21) ------------------------------------------- -* DATAJPA-1029 - JpaQueryCreator should deduplicate multi-select selections. -* DATAJPA-1027 - Upgrade to a newer JDK version on TravisCI. -* DATAJPA-1026 - Adapt API in RepositoryFactoryBeanSupport implementation. -* DATAJPA-1023 - Reject stream executions if not executed within transaction. -* DATAJPA-1021 - Make sure builds against Spring 5 skip tests for OpenJPA. -* DATAJPA-1019 - Make sure build runs on Hibernate 5. -* DATAJPA-1018 - Upgrade Hibernate build profile to latest releases. -* DATAJPA-1017 - Make sure tests work on Spring 5. -* DATAJPA-1014 - Register repository factory in spring.factories for multi-store support. -* DATAJPA-1000 - Pageable With Fetch not returning the right alias Name. -* DATAJPA-993 - Improve infrastructure setup example in reference documentation. -* DATAJPA-992 - Fix package name in JavaDoc of AuditingEntityListener. -* DATAJPA-984 - JPA projection query returning single non-standard JPA type results in "No aliases found in result tuple" error. -* DATAJPA-978 - Upgrade Hibernate 5.2 build profile to 5.2.4. -* DATAJPA-974 - Projections referring to collection attributes don't create a proper selection (missing joins). -* DATAJPA-970 - Ensure JDK 6 compatibility of regular expression used in QueryUtils. -* DATAJPA-965 - Mapping Sort instances to ORDER BY expressions should be restricted to fields for manually defined queries. -* DATAJPA-962 - Update Travis configuration with build profiles for Spring 5. -* DATAJPA-960 - Order by clause not created correctly if a manually defined query is not using an alias. -* DATAJPA-956 - Error creating DefaultJpaContext bean with a non-EntityManagerFactory JndiObjectFactoryBean defined via JavaConfig. -* DATAJPA-953 - Fix typo in reference documentation. -* DATAJPA-951 - Projections not handled correctly when Optional is used as wrapping return type. -* DATAJPA-950 - Upgrade Hibernate 5 build profile to 5.2.2. -* DATAJPA-941 - JpaClassUtils.isOfType performance improvement. -* DATAJPA-939 - Release 1.11 RC1 (Ingalls). -* DATAJPA-938 - Complex select clauses in manually declared query using constructor expressions fail. -* DATAJPA-920 - Add support for exists projection in repository query derivation. -* DATAJPA-914 - findAll with Iterable parameter gives JpaSystemException when using Hibernate 5.2. -* DATAJPA-911 - Assert compatibility with Hibernate 5.2. -* DATAJPA-790 - org.hibernate.QueryException when applying @EntityGraph on (Querydsl) findAll(Predicate, Pageable) method. - - -Changes in version 2.0.0.M1 (2016-11-23) ----------------------------------------- -* DATAJPA-998 - Set up 2.0 development. -* DATAJPA-997 - Release 2.0 M1 (Kay). - - -Changes in version 1.10.5.RELEASE (2016-11-03) ----------------------------------------------- -* DATAJPA-993 - Improve infrastructure setup example in reference documentation. -* DATAJPA-992 - Fix package name in JavaDoc of AuditingEntityListener. -* DATAJPA-984 - JPA projection query returning single non-standard JPA type results in "No aliases found in result tuple" error. -* DATAJPA-978 - Upgrade Hibernate 5.2 build profile to 5.2.4. -* DATAJPA-977 - Release 1.10.5 (Hopper SR5). -* DATAJPA-790 - org.hibernate.QueryException when applying @EntityGraph on (Querydsl) findAll(Predicate, Pageable) method. - - -Changes in version 1.10.4.RELEASE (2016-09-29) ----------------------------------------------- -* DATAJPA-974 - Projections referring to collection attributes don't create a proper selection (missing joins). -* DATAJPA-970 - Ensure JDK 6 compatibility of regular expression used in QueryUtils. -* DATAJPA-966 - Release 1.10.4 (Hopper SR4). - - -Changes in version 1.9.6.RELEASE (2016-09-29) ---------------------------------------------- -* DATAJPA-975 - Release 1.9.6 (Gosling SR6). -* DATAJPA-970 - Ensure JDK 6 compatibility of regular expression used in QueryUtils. - - -Changes in version 1.9.5.RELEASE (2016-09-20) ---------------------------------------------- -* DATAJPA-965 - Mapping Sort instances to ORDER BY expressions should be restricted to fields for manually defined queries. -* DATAJPA-964 - Release 1.9.5 (Gosling SR5). -* DATAJPA-960 - Order by clause not created correctly if a manually defined query is not using an alias. -* DATAJPA-953 - Fix typo in reference documentation. -* DATAJPA-908 - Add note about when @Param is needed for named parameters. -* DATAJPA-904 - Potential NullPointerException in JpaPersistentPropertyImpl.isEntity(). -* DATAJPA-903 - Stream missing result attributes on Hibernate. -* DATAJPA-891 - Avoid exceptions being thrown and caught immediately in JpaPersistentPropertyImpl.isEntity(). -* DATAJPA-888 - Clarify usage of native queries with Pageable. -* DATAJPA-413 - Nested Id classes fail to populate in JpaMetamodelEntityInformation. - - -Changes in version 1.10.3.RELEASE (2016-09-20) ----------------------------------------------- -* DATAJPA-965 - Mapping Sort instances to ORDER BY expressions should be restricted to fields for manually defined queries. -* DATAJPA-960 - Order by clause not created correctly if a manually defined query is not using an alias. -* DATAJPA-956 - Error creating DefaultJpaContext bean with a non-EntityManagerFactory JndiObjectFactoryBean defined via JavaConfig. -* DATAJPA-953 - Fix typo in reference documentation. -* DATAJPA-951 - Projections not handled correctly when Optional is used as wrapping return type. -* DATAJPA-950 - Upgrade Hibernate 5 build profile to 5.2.2. -* DATAJPA-938 - Complex select clauses in manually declared query using constructor expressions fail. -* DATAJPA-937 - Broken placeholder in exception message to be created in QueryByExamplePredicateBuilder.getPredicate(…). -* DATAJPA-929 - NullPointerException in AbstractStringBasedJpaQuery.isJpaManaged(). -* DATAJPA-916 - Release 1.10.3 (Hopper SR3). -* DATAJPA-413 - Nested Id classes fail to populate in JpaMetamodelEntityInformation. - - -Changes in version 1.11.0.M1 (2016-07-27) ------------------------------------------ -* DATAJPA-937 - Broken placeholder in exception message to be created in QueryByExamplePredicateBuilder.getPredicate(…). -* DATAJPA-932 - Integrate version badge from spring.io. -* DATAJPA-923 - Add support for query by example using OR operator instead of AND. -* DATAJPA-915 - Update Hibernate build profiles. -* DATAJPA-912 - Optimize out the count query for paging when the data query returns less than a full page with a 0 offset. -* DATAJPA-909 - Count query breaks if pagination is used with projections. -* DATAJPA-908 - Add note about when @Param is needed for named parameters. -* DATAJPA-904 - Potential NullPointerException in JpaPersistentPropertyImpl.isEntity(). -* DATAJPA-903 - Stream missing result attributes on Hibernate. -* DATAJPA-893 - Update Spring Data JPA version in Github readme. -* DATAJPA-891 - Avoid exceptions being thrown and caught immediately in JpaPersistentPropertyImpl.isEntity(). -* DATAJPA-888 - Clarify usage of native queries with Pageable. -* DATAJPA-886 - NullPointerException for repository query methods using DTOs. -* DATAJPA-885 - No aliases found in result tuple. -* DATAJPA-883 - Auditing broken in Hopper GA. -* DATAJPA-882 - Release 1.11 M1 (Ingalls). -* DATAJPA-413 - Nested Id classes fail to populate in JpaMetamodelEntityInformation. - - -Changes in version 1.10.2.RELEASE (2016-06-15) ----------------------------------------------- -* DATAJPA-909 - Count query breaks if pagination is used with projections. -* DATAJPA-908 - Add note about when @Param is needed for named parameters. -* DATAJPA-904 - Potential NullPointerException in JpaPersistentPropertyImpl.isEntity(). -* DATAJPA-903 - Stream missing result attributes on Hibernate. -* DATAJPA-891 - Avoid exceptions being thrown and caught immediately in JpaPersistentPropertyImpl.isEntity(). -* DATAJPA-888 - Clarify usage of native queries with Pageable. -* DATAJPA-886 - NullPointerException for repository query methods using DTOs. -* DATAJPA-885 - No aliases found in result tuple. -* DATAJPA-884 - Release 1.10.2 (Hopper SR2). - - -Changes in version 1.10.1.RELEASE (2016-04-06) ----------------------------------------------- -* DATAJPA-883 - Auditing broken in Hopper GA. -* DATAJPA-881 - Release 1.10.1 (Hopper SR1). - - -Changes in version 1.10.0.RELEASE (2016-04-06) ----------------------------------------------- -* DATAJPA-880 - Add pull request template. -* DATAJPA-878 - Release 1.10 GA (Hopper). -* DATAJPA-876 - Update documentation for Spring Data JPA 1.10. -* DATAJPA-838 - Add section on ad-hoc @EntityGraphs to reference documentation. - - -Changes in version 1.10.0.RC1 (2016-03-18) ------------------------------------------- -* DATAJPA-871 - Allow usage of composed annotations using @AliasFor. -* DATAJPA-869 - Release 1.10 RC1 (Hopper). -* DATAJPA-867 - Upgrade persistence provider dependencies to latest version. -* DATAJPA-864 - Query execution fails for manually defined queries containing a constructor expression. -* DATAJPA-862 - Reference documentation contains wrong property index "?0" in @Query example. -* DATAJPA-813 - Error creating DefaultJpaContext bean in test cases. -* DATAJPA-218 - Support for Query by Example. - - -Changes in version 1.9.4.RELEASE (2016-02-23) ---------------------------------------------- -* DATAJPA-862 - Reference documentation contains wrong property index "?0" in @Query example. -* DATAJPA-860 - Release 1.9.4 (Gosling SR4). -* DATAJPA-859 - Add build profile for Hibernate 5.1. -* DATAJPA-858 - Contains binding inspects first property for collections, not the leaf property. -* DATAJPA-850 - Reference documentation should mention @EntityListener to set up auditing. -* DATAJPA-848 - AbstractPersistable.equals(…) always returns false if target entity is a proxy. -* DATAJPA-847 - Upgrade to EclipseLink 2.6.2. -* DATAJPA-845 - PersistenceProvider static from* methods are a performance hit. - - -Changes in version 1.10.0.M1 (2016-02-12) ------------------------------------------ -* DATAJPA-859 - Add build profile for Hibernate 5.1. -* DATAJPA-858 - Contains binding inspects first property for collections, not the leaf property. -* DATAJPA-854 - Add code of conduct. -* DATAJPA-852 - Release 1.10 M1 (Hopper). -* DATAJPA-850 - Reference documentation should mention @EntityListener to set up auditing. -* DATAJPA-849 - Sample in README broken. -* DATAJPA-848 - AbstractPersistable.equals(…) always returns false if target entity is a proxy. -* DATAJPA-847 - Upgrade to EclipseLink 2.6.2. -* DATAJPA-845 - PersistenceProvider static from* methods are a performance hit. -* DATAJPA-839 - EntityGraph, specified for repository method, that was called first, applies to all other methods of the same repository. -* DATAJPA-834 - Use a less common bean name for EntityManagerBeanDefinitionRegistrarPostProcessor to avoid collisions. -* DATAJPA-833 - Improve log message in ClasspathScanningPersistenceUnitPostProcessor. -* DATAJPA-831 - Upgrade Hibernate build profile to 5.0.5. -* DATAJPA-830 - NotContaining doesn't work for String properties on query methods. -* DATAJPA-829 - Contains expression on a collection property should result in member of predicate. -* DATAJPA-826 - Add Jacoco agent to test executions explicitly. -* DATAJPA-821 - Upgrade to OpenJPA 2.4.0. -* DATAJPA-820 - New entity merged rather than persisted when ID is not generated and version is in a MappedSuperClass. -* DATAJPA-819 - CrudMethodMetadataPostProcessor does not use bean class loader. -* DATAJPA-817 - Upgrade to EclipseLink 2.6.1. -* DATAJPA-815 - order by qualifyReference when property begins with joinAlias. -* DATAJPA-809 - ParameterBinder should use ParameterAccessor. -* DATAJPA-808 - Add AttributeConverter implementations for ZoneId. -* DATAJPA-805 - Typo in @Modifying documentation. -* DATAJPA-804 - Support projections on repository query methods. -* DATAJPA-798 - Repository query method fails when sorting with Pageable and query with line breaks. -* DATAJPA-796 - Typo in JavaDoc of AbstractPersistable. -* DATAJPA-795 - Upgrade Hibernate 5 build profile to 5.0.1. -* DATAJPA-793 - Spring Boot has broken custom repository methods in Spring Data JPA. -* DATAJPA-765 - Upgrade to Querydsl 4. -* DATAJPA-742 - Java 8 Stream support broken. -* DATAJPA-585 - Incomplete null handling in QueryDslJpaRepository. -* DATAJPA-382 - Provide access to auditor in manually defined queries. - - -Changes in version 1.9.2.RELEASE (2015-12-18) ---------------------------------------------- -* DATAJPA-839 - Thread-bound lookup of CrudMethodMetadata broken. -* DATAJPA-837 - Release 1.9.2 (Gosling). -* DATAJPA-834 - Use a less common bean name for EntityManagerBeanDefinitionRegistrarPostProcessor to avoid collisions. -* DATAJPA-833 - Improve log message in ClasspathScanningPersistenceUnitPostProcessor. -* DATAJPA-831 - Upgrade Hibernate build profile to 5.0.5. -* DATAJPA-830 - NotContaining doesn't work for String properties on query methods. -* DATAJPA-829 - Contains expression on a collection property should result in member of predicate. - - -Changes in version 1.9.1.RELEASE (2015-11-15) ---------------------------------------------- -* DATAJPA-823 - Release 1.9.1 (Gosling). -* DATAJPA-820 - New entity merged rather than persisted when ID is not generated and version is in a MappedSuperClass. -* DATAJPA-819 - CrudMethodMetadataPostProcessor does not use bean class loader. -* DATAJPA-817 - Upgrade to EclipseLink 2.6.1. -* DATAJPA-815 - order by qualifyReference when property begins with joinAlias. -* DATAJPA-805 - Typo in @Modifying documentation. -* DATAJPA-798 - Repository query method fails when sorting with Pageable and query with line breaks. -* DATAJPA-796 - Typo in JavaDoc of AbstractPersistable. -* DATAJPA-795 - Upgrade Hibernate 5 build profile to 5.0.1. -* DATAJPA-793 - Spring Boot has broken custom repository methods in Spring Data JPA. -* DATAJPA-742 - Java 8 Stream support broken. -* DATAJPA-585 - Incomplete null handling in QueryDslJpaRepository. -* DATAJPA-382 - Provide access to auditor in manually defined queries. - - -Changes in version 1.7.4.RELEASE (2015-10-14) ---------------------------------------------- -* DATAJPA-812 - Release 1.7.4 (Evans). - - -Changes in version 1.9.0.RELEASE (2015-09-01) ---------------------------------------------- -* DATAJPA-787 - Release 1.9 GA (Gosling). -* DATAJPA-785 - Make sure the build works with Hibernate 5 GA. -* DATAJPA-779 - Update reference documentation to mention newly introduced JpaContext. -* DATAJPA-775 - Fail fast when entity uses Spring Data Commons @Version instead of JPA @Version. -* DATAJPA-773 - Remove explicit registration of ExposeInvocationInterceptor from CrudMethodMetadataPostProcessor. -* DATAJPA-772 - ParameterMetadataProvider should apply LIKE-expansion only on String properties. - - -Changes in version 1.9.0.RC1 (2015-08-04) ------------------------------------------ -* DATAJPA-768 - Release 1.9 RC1 (Gosling). -* DATAJPA-763 - Executing a Specification with fetch-joins leads to wrong HQL query. -* DATAJPA-759 - Log names of Class and Mapping Files found during scan to DEBUG level in ClasspathScanningPersistenceUnitPostProcessor. -* DATAJPA-758 - Using Java 8 named parameter prevents positional query parameter binding. -* DATAJPA-743 - Typo in README. -* DATAJPA-736 - Count query creation fails when entity names contain non-ASCII characters. -* DATAJPA-728 - PageImpl : wrong total count. -* DATAJPA-669 - Provide a way to get the injected Entity Manager [Factory] in the custom implementations. - - -Changes in version 1.8.2.RELEASE (2015-07-28) ---------------------------------------------- -* DATAJPA-766 - Release 1.8.2 (Fowler). -* DATAJPA-763 - Executing a Specification with fetch-joins leads to wrong HQL query. -* DATAJPA-759 - Log names of Class and Mapping Files found during scan to DEBUG level in ClasspathScanningPersistenceUnitPostProcessor. -* DATAJPA-758 - Using Java 8 named parameter prevents positional query parameter binding. - - -Changes in version 1.6.6.RELEASE (2015-07-01) ---------------------------------------------- -* DATAJPA-750 - Release 1.6.6 (Dijkstra). -* DATAJPA-743 - Typo in README. -* DATAJPA-736 - Count query creation fails when entity names contain non-ASCII characters. -* DATAJPA-728 - PageImpl : wrong total count. -* DATAJPA-726 - Pageable Always Adding From Entity to Sort Field Name. -* DATAJPA-721 - Enable Slack notifications for Travis build. -* DATAJPA-720 - Remove relative reference to parent POM to make sure the right Spring version is picked up. -* DATAJPA-715 - Fix typo in @EnableJpaRepositories' JavaDoc. -* DATAJPA-702 - Add convenience methods to ease building up a more complex JpaSort. -* DATAJPA-699 - Upgrade to EclipseLink 2.5.2. -* DATAJPA-681 - Execution of derived stored procedures fails if named parameters are used. -* DATAJPA-672 - Activate Spring 4.2 build profile for travis. -* DATAJPA-656 - Count query with group by returns wrong result. -* DATAJPA-633 - Target entity type not considered for associations. - - -Changes in version 1.7.3.RELEASE (2015-07-01) ---------------------------------------------- -* DATAJPA-751 - Release 1.7.3 (Evans). -* DATAJPA-743 - Typo in README. -* DATAJPA-741 - Back-port DATAJPA-608 to Evans. -* DATAJPA-736 - Count query creation fails when entity names contain non-ASCII characters. -* DATAJPA-728 - PageImpl : wrong total count. -* DATAJPA-726 - Pageable Always Adding From Entity to Sort Field Name. -* DATAJPA-721 - Enable Slack notifications for Travis build. -* DATAJPA-720 - Remove relative reference to parent POM to make sure the right Spring version is picked up. -* DATAJPA-715 - Fix typo in @EnableJpaRepositories' JavaDoc. -* DATAJPA-714 - Include new section on Spring Data and Spring Framework dependencies in reference documentation. -* DATAJPA-712 - Bug in binding in clause parameters with SPEL. -* DATAJPA-703 - Assert Hibernate 5 compatibilty. -* DATAJPA-702 - Add convenience methods to ease building up a more complex JpaSort. -* DATAJPA-699 - Upgrade to EclipseLink 2.5.2. -* DATAJPA-689 - Allow @EntityGraph on findOne method of CrudRepository. -* DATAJPA-681 - Execution of derived stored procedures fails if named parameters are used. -* DATAJPA-672 - Activate Spring 4.2 build profile for travis. -* DATAJPA-656 - Count query with group by returns wrong result. -* DATAJPA-633 - Target entity type not considered for associations. - - -Changes in version 1.8.1.RELEASE (2015-06-30) ---------------------------------------------- -* DATAJPA-752 - Release 1.8.1 (Fowler). -* DATAJPA-743 - Typo in README. -* DATAJPA-736 - Count query creation fails when entity names contain non-ASCII characters. -* DATAJPA-728 - PageImpl : wrong total count. -* DATAJPA-726 - Pageable Always Adding From Entity to Sort Field Name. -* DATAJPA-721 - Enable Slack notifications for Travis build. -* DATAJPA-720 - Remove relative reference to parent POM to make sure the right Spring version is picked up. -* DATAJPA-716 - JpaPersistentProperty should consider updatable flag of mapping annotations. -* DATAJPA-715 - Fix typo in @EnableJpaRepositories' JavaDoc. -* DATAJPA-714 - Include new section on Spring Data and Spring Framework dependencies in reference documentation. -* DATAJPA-712 - Bug in binding in clause parameters with SPEL. -* DATAJPA-703 - Assert Hibernate 5 compatibilty. -* DATAJPA-702 - Add convenience methods to ease building up a more complex JpaSort. -* DATAJPA-699 - Upgrade to EclipseLink 2.5.2. -* DATAJPA-695 - Package reference in javadoc wrong in Jsr310JpaConverters, ThreeTenBackPortJpaConverters. - - -Changes in version 1.9.0.M1 (2015-06-02) ----------------------------------------- -* DATAJPA-731 - Donwgrade to Querydsl 3.6.3. -* DATAJPA-730 - Release 1.9 M1 (Gosling). -* DATAJPA-726 - Pageable Always Adding From Entity to Sort Field Name. -* DATAJPA-721 - Enable Slack notifications for Travis build. -* DATAJPA-720 - Remove relative reference to parent POM to make sure the right Spring version is picked up. -* DATAJPA-716 - JpaPersistentProperty should consider updatable flag of mapping annotations. -* DATAJPA-715 - Fix typo in @EnableJpaRepositories' JavaDoc. -* DATAJPA-714 - Include new section on Spring Data and Spring Framework dependencies in reference documentation. -* DATAJPA-712 - Bug in binding in clause parameters with SPEL. -* DATAJPA-710 - Adapt API changes in Spring Data Commons to simplify custom repository base class registration. -* DATAJPA-703 - Assert Hibernate 5 compatibilty. -* DATAJPA-702 - Add convenience methods to ease building up a more complex JpaSort. -* DATAJPA-698 - Upgrade to EclipseLink 2.6.0. -* DATAJPA-696 - Support ad-hoc fetch graph configurations on repository methods. -* DATAJPA-695 - Package reference in javadoc wrong in Jsr310JpaConverters, ThreeTenBackPortJpaConverters. -* DATAJPA-674 - Problems following the documentation for adding custom behaviour to all repositories. - - -Changes in version 1.8.0.RELEASE (2015-03-23) ---------------------------------------------- -* DATAJPA-692 - Release 1.8 GA. -* DATAJPA-689 - Allow @EntityGraph on findOne method of CrudRepository. -* DATAJPA-685 - Allow to disable default transaction handling in @EnableJpaRepositories and XML namespace. - - -Changes in version 1.8.0.RC1 (2015-03-05) ------------------------------------------ -* DATAJPA-686 - Release 1.8 RC1. -* DATAJPA-681 - Execution of derived stored procedures fails if named parameters are used. -* DATAJPA-679 - Add QueryDslPredicateExecutor.findAll(Predicate, Sort) method. -* DATAJPA-677 - Add support for Java 8 Stream in repository finder methods. -* DATAJPA-672 - Activate Spring 4.2 build profile for travis. -* DATAJPA-665 - Add 'exists' method to QueryDslJpaRepository which accepts a querydsl Predicate. -* DATAJPA-664 - JpaPersistentProperty.getActualType() should consider specialized association type. -* DATAJPA-656 - Count query with group by returns wrong result. -* DATAJPA-655 - Add AttributeConverters for ThreeTen back port library. -* DATAJPA-654 - Enable Spring 4.1 build profile for Travis. -* DATAJPA-653 - Make sure test work with Spring 4.1. -* DATAJPA-652 - Provide support for ParameterMode.REF_CURSOR. -* DATAJPA-651 - Re-enable querydsl-next build profile for Travis. -* DATAJPA-650 - Add AttributeConverters for non-timezoned JSR-310 types. -* DATAJPA-641 - Move to new Travis build infrastructure. -* DATAJPA-546 - Provide a way to customize the domain type managed by a repository. - - -Changes in version 1.7.2.RELEASE (2015-01-28) ---------------------------------------------- -* DATAJPA-661 - Release 1.7.2. -* DATAJPA-654 - Enable Spring 4.1 build profile for Travis. -* DATAJPA-653 - Make sure test work with Spring 4.1. -* DATAJPA-641 - Move to new Travis build infrastructure. -* DATAJPA-639 - Set up Travis to only build with JDK 8. -* DATAJPA-638 - Improve bean definition setup for root configuration element. -* DATAJPA-634 - Switch to HTTPS for repository declarations. -* DATAJPA-632 - Improve SimpleJpaQueryUnitTests that fails after SonarQube improvements in Spring Data Commons. -* DATAJPA-631 - ClasspathScanningPersistenceUnitPostProcessor should forward ResourceLoader configured to the ClassPathScanningCandidateComponentProvider used for the entity type scanning. -* DATAJPA-629 - SPEL expression does not work with #{#entityName} in @Query. -* DATAJPA-628 - Use a shared SpelParser instead of recreating it for queries. -* DATAJPA-627 - Typo in BeanDefinitionNames. -* DATAJPA-624 - Fix SonarQube warnings as far as possible/reasonable. -* DATAJPA-622 - Hibernate thinks AbstractPersistable.isNew() is a property. -* DATAJPA-611 - SimpleJpaRepository.findAll(Iterable... orders). -* DATAJPA-634 - Switch to HTTPS for repository declarations. -* DATAJPA-632 - Improve SimpleJpaQueryUnitTests that fails after SonarQube improvements in Spring Data Commons. -* DATAJPA-631 - ClasspathScanningPersistenceUnitPostProcessor should forward ResourceLoader configured to the ClassPathScanningCandidateComponentProvider used for the entity type scanning. -* DATAJPA-630 - Identifier lookup fails for JPA proxies. -* DATAJPA-629 - SPEL expression does not work with #{#entityName} in @Query. -* DATAJPA-628 - Use a shared SpelParser instead of recreating it for queries. -* DATAJPA-627 - Typo in BeanDefinitionNames. -* DATAJPA-624 - Fix SonarQube warnings as far as possible/reasonable. -* DATAJPA-623 - Remove package cycles accidentally introduced in 1.6. -* DATAJPA-622 - Hibernate thinks AbstractPersistable.isNew() is a property. -* DATAJPA-620 - Infinite loop in unsynchronized HashMap in CrudMethodMetadataPostProcessor. -* DATAJPA-619 - JpaPersistentPropertyImpl should consider JPA access type settings. -* DATAJPA-617 - Named query lookups might cause transaction to be rolled back. -* DATAJPA-615 - The top and first query keyword to statically limit results are missing in documentation. -* DATAJPA-612 - Allow @EntityGraph on a method inherited from JpaRepository. -* DATAJPA-611 - SimpleJpaRepository.findAll(Iterable as alternative to null result. - -Changes in version 1.5.2.RELEASE (2014-04-15) ---------------------------------------------- -* Sorting for a field in a composite primary key throws "Can't cast to EntityPath". (DATAJPA-497) -* Sorting of Embeddables raises ClassCastException. (DATAJPA-500) -* OSGi version conflict regarding joda-time. (DATAJPA-502) -* Error on QueryDSL after upgrade (DATAJPA-504) -* Blob query result only returns first byte. (DATAJPA-505) -* Entity name used in 'count' queries ignores the entity name set in ORM mapping file. (DATAJPA-509) -* Performance tuning LockModePopulatingMethodIntercceptor.invoke(). (DATAJPA-507) -* Improve error message for missing @Param annotations for query methods with named parameters. (DATAJPA-513) -* Release 1.5.2. (DATAJPA-515) - -Changes in version 1.6.0.M1 (2014-03-31) ----------------------------------------- -* Can't use pagination (Pageable) with @IdClass entities (spring-data-jpa 1.4.3 & Hibernate 4.1.9). (DATAJPA-472) -* IllegalStateException caused by QueryUtils.toExpressionRecursively(…). (DATAJPA-476) -* Parameter binding detection for in clause broken for parameters surrounded with parentheses. (DATAJPA-483) -* findAll(Iterable) : Invalid comparation Exception thrown. (DATAJPA-492) -* Missing Imports in Spring Data JPA for Auditing. (DATAJPA-494) -* Query derivation doesn't create joins for in-clauses on element collections. (DATAJPA-496) -* Sorting for a field in a composite primary key throws "Can't cast to EntityPath". (DATAJPA-497) -* Sorting of Embeddables raises ClassCastException. (DATAJPA-500) -* OSGi version conflict regarding joda-time. (DATAJPA-502) -* Error on QueryDSL after upgrade. (DATAJPA-504) -* Blob query result only returns first byte. (DATAJPA-505) -* Support for @QueryHints on CRUD methods. (DATAJPA-173) -* Add support for returning subtypes of Repository Entity in JpaRepository.saveAndFlush. (DATAJPA-464) -* Postpone the construction of AuditorAware in AuditEntityListener. (DATAJPA-478) -* Repositories should expose a single MappingContext instance. (DATAJPA-484) -* Sort by property of an associated object generates left join only for the first level. (DATAJPA-491) -* Update auditing configuration to be able to use accessor annotations. (DATAJPA-501) -* Query creation for deleteBy / removeBy prefix. (DATAJPA-460) -* Add support for lazy loading configuration via JPA 2.1 fetch-/loadgraph. (DATAJPA-466) -* Add support for sliced execution. (DATAJPA-486) -* Upgrade Hibernate build profiles to latest version. (DATAJPA-485) -* Adapt to auditing configuration changes in Spring Data Commons. (DATAJPA-487) -* Adapt to API changes in RepositoryProxyPostProcessor. (DATAJPA-489) -* Upgrade to OpenJPA 2.3.0. (DATAJPA-493) -* Release 1.6 M1. (DATAJPA-481) - -Changes in version 1.5.1.RELEASE (2014-03-13) ---------------------------------------------- -* Verify that pagination works with entities with @IdClass. (DATAJPA-472) -* Mitigate spec violations in Hibernate for query creation. (DATAJPA-476) -* Fixed parameter binding detection with parentheses. (DATAJPA-483) -* Upgraded to Hibernate profiles to 4.3.4 and 4.2.10. (DATAJPA-485) -* Support order by arbitrarily nested association paths with Querydsl. (DATAJPA-491) -* Altered implementation of findAll(Iterable) to work around bug in OpenJPA. (DATAJPA-492) -* Fix template.mf to make sure auditing can be used on OSGi. (DATAJPA-494) -* Query derivation doesn't create joins for in-clauses on element collections. (DATAJPA-496) - -Changes in version 1.4.5.RELEASE (2014-03-10) ---------------------------------------------- -* When used "LIKE expression" and "other expression" at the same time, occurred the QuerySyntaxException of Hibernate. (DATAJPA-473) -* IllegalStateException caused by QueryUtils.toExpressionRecursively(…). (DATAJPA-476) -* findAll(Iterable) : Invalid comparation Exception thrown. (DATAJPA-492) -* Improve FAQ, C2.1 on the SessionFactory replacement. (DATAJPA-462) -* Documentation on CDI setup is incomplete. (DATAJPA-474) -* Upgrade Hibernate build profiles to latest version. (DATAJPA-485) -* Release 1.4.5. (DATAJPA-488) - -Changes in version 1.5.0.RELEASE (2014-02-24) ------------------------------------------ - -* Query on byte array fails (DATAJPA-454) -* Regression in parameter binding (DATAJPA-461) -* Nullpointer when using @Query (DATAJPA-465) -* Build fails against Spring 4 (DATAJPA-471) -* When used "LIKE expression" and "other expression" at the same time, occurred the QuerySyntaxException of Hibernate. (DATAJPA-473) -* Improve FAQ, C2.1 on the SessionFactory replacement (DATAJPA-462) -* Documentation on CDI setup is incomplete (DATAJPA-474) -* Add aop.xml to prevent Eclipse from weaving unnecessary aspects (DATAJPA-458) -* Reduce log output for Querydsl APT processor (DATAJPA-459) -* Release 1.5 GA (DATAJPA-469) -* Documentation overhaul (DATAJPA-470) - -Changes in version 1.4.4.RELEASE (2014-02-17) ---------------------------------------------- -* PersistenceProvider enum should also match org.hibernate.jpa.HibernateEntityManager (DATAJPA-444) -* Query on byte array fails (DATAJPA-454) -* Regression in parameter binding (DATAJPA-461) -* Upgrade Hibernate 4 build to latest versions (DATAJPA-443) -* Add contribution guidelines (DATAJPA-448) -* Release 1.4.4 (DATAJPA-463) - -Changes in version 1.5.0.RC1 (2014-01-29) ------------------------------------------ -* Add Sort implementations that use JPA Metamodel API and/or QueryDsl API (DATAJPA-12) -* Enable support to inject EntityManager via constructor (DATAJPA-445) -* needs explicitly given transaction manager even when there is only one tx manager (DATAJPA-410) -* COUNT syntax error when use scalar select (select a.b, a.c from …) with paging (DATAJPA-420) -* Sort by property of an associated object generates inner join instead of left join with QueryDsl and Hibernate (DATAJPA-427) -* Fix incompatibilities with latest Hibernate 4.3 (CR2 currently) (DATAJPA-430) -* Repository initialization causes transaction rollback in CDI environments (DATAJPA-442) -* PersistenceProvider enum should also match org.hibernate.jpa.HibernateEntityManager (DATAJPA-444) -* EntityManagerBeanDefinitionRegistrarPostProcessor doesn't find EntityManagerFactory definitions in BeanFactory hierarchy (DATAJPA-453) -* Upgrade to EclipseLink 2.5.1 (DATAJPA-417) -* Upgrade Hibernate 4 build to latest versions (DATAJPA-443) -* Add contribution guidelines (DATAJPA-448) -* Release Spring Data JPA 1.5.0.RC1 (DATAJPA-450) - -Changes in version 1.4.3.RELEASE (2013-12-11) ---------------------------------------------- -* Improved documentation on jpa:repository namespace (DATAJPA-410) -* Fixed count projection for manual queries with projections (DATAJPA-420) -* Guard against null predicates on query creation. (DATAJPA-405) -* Fixed alias detection in manually defined queries using SpEL (DATAJPA-424) -* Generate left joins for referenced associations in sort expressions (DATAJPA-427) -* Tweaks to be compatible with Hibernate 4.3 (DATAJPA-430) -* Release 1.4.3 (DATAJPA-434) - -Changes in version 1.5.0.M1 (2013-11-19) ----------------------------------------- -* CriteriaQuery caching causes problems with Hibernate's alias name generation in multithreaded environments (DATAJPA-396) -* Unnecessary OUTER JOIN are generated for querying one single table (DATAJPA-401) -* Unnecessary OUTER JOIN are generated when sorting on ElementCollection (DATAJPA-403) -* Custom repository method doesn't parse OrderBy clause properly (DATAJPA-405) -* Reference and Javadoc inconsistence - @Modifying.clearAutomatically default value (DATAJPA-406) -* ClasspathScanningPersistenceUnitPostProcessor throws IndexOutOfBoundsException on Windows (DATAJPA-407) -* org.springframework.data.jpa.repository.Query is not supported varargs (DATAJPA-415) -* #entityName SpEL does not match alias regex when detecting alias (DATAJPA-424) -* Make it possible to configure auditing with Java Config (DATAJPA-265) -* Improve execution of native queries (DATAJPA-389) -* Allow turning a CRUD method into a query method by annotating it with @Query (DATAJPA-398) -* Add support for nested repositories (DATAJPA-416) -* Upgrade to Spring Framework 3.2.5 (DATAJPA-418) -* Improve accessibility of methods in SimpleJpaRepository (DATAJPA-426) -* Add getReference method to the SimpleJpaRepository (DATAJPA-83) -* @Lazy for interface defined repositories (DATAJPA-419) - -Changes in version 1.4.2.RELEASE (2013-10-25) ---------------------------------------------- -* CriteriaQuery caching causes problems with Hibernate's alias name generation in multithreaded environments (DATAJPA-396) -* Unnecessary OUTER JOIN are generated for querying one single table (DATAJPA-401) -* Unnecessary OUTER JOIN are generated when sorting on ElementCollection (DATAJPA-403) -* Reference and Javadoc inconsistence - @Modifying.clearAutomatically default value (DATAJPA-406) -* ClasspathScanningPersistenceUnitPostProcessor throws IndexOutOfBoundsException on Windows (DATAJPA-407) -* Add Java Config example to readme.md (DATAJPA-399) -* Release 1.4.2 (DATAJPA-411) - -Changes in version 1.4.1.RELEASE (2013-09-09) ---------------------------------------------- -* Update to Spring Data Commons 1.6.1 (DATAJPA-400) - -Changes in version 1.4.0.RELEASE (2013-09-09) ---------------------------------------------- -* The H2 database needs a quoted name parameter in a @Query annotation (DATAJPA-354) -* Not specifying the ASC clause results in a double ORDER BY clause (DATAJPA-375) -* Undeclared dependency on spring aspectj (DATAJPA-367) -* @Transactional (AspectJ) and @Configurable not weaved for @Entity if this @Entity is managed by a JpaRepository (DATAJPA-298) -* Spring Data ignores alternative EntityManager Producer in CDI environment (DATAJPA-388) -* Typo in package-info.java of utility package (DATAJPA-392) -* Remove Hibernate specific test for DATAJPA-107 in PartTreeJpaQueryIntegrationTests (DATAJPA-381) - -Changes in version 1.4.0.RC1 (2012-08-01) ------------------------------------------ -* Sort with aggregate properties results in an invalid alias being generated and added to the query (DATAJPA-148) -* Composite Primary Key Class With Foreign Key using IdClass results in TypeMismatchException (DATAJPA-269) -* Sort by property of an associated object doesn't work. Join type should be LEFT OUTER JOIN for sorting on associated objects. (DATAJPA-346) -* @IdClass of @MappedSuperclass is not supported (DATAJPA-348) -* SimpleJpaQuery validates count queries to aggressively (DATAJPA-352) -* ClasspathScanningPersistenceUnitPostProcessor does not resolve mapping files properly (DATAJPA-353) -* @Lock annotation doesn't work on (at least) findOne method (DATAJPA-359) -* Improve SimpleJpaRepository.delete(ID id) by removing superfluous call to exists(…). (DATAJPA-363) -* NullPointerException for manually defined queries with multiple named like expressions (DATAJPA-373) -* Count queries should be issued without ORDER BY clause (DATAJPA-377) -* JpaRepository.findAll(Iterable ids) should return List, not Iterable (DATAJPA-357) -* Improve QueryDslJpaRepository.findAll(Predicate, Pageable) (DATAJPA-361) -* Explain usage of SpEL Expression in @Query(...) on Repository methods (DATAJPA-368) -* Problems with entities weaved by EclipseLink (DATAJPA-376) -* Add support for TemporalType parameter in named queries (DATAJPA-107) -* Passing parameters from child classes to the parents' annotations (DATAJPA-170) -* Ref doc - explain about save strategy repositories (DATAJPA-344) -* Add missing package-info.java files (DATAJPA-370) -* Release 1.4 RC1 (DATAJPA-379) - -Changes in version 1.3.4.RELEASE (2013-07-24) ---------------------------------------------- -* Fixed NullPointerException for manually defined queries with multiple named like expressions (DATAJPA-373) -* Added missing package-info.java files (DATAJPA-370) - -Changes in version 1.3.3.RELEASE (2013-07-19) ---------------------------------------------- -* Sort with aggregate properties results in an invalid alias being generated and added to the query (DATAJPA-148) -* Repository injection fails if BeanPostProcessor depends on repository (DATAJPA-335) -* New LIKE expression handling breaks backward compatibility (DATAJPA-341) -* Wrong calculation of total number of elements when using DISTINCT clause (DATAJPA-342) -* Subselect bug introduced in the snapshot on 20130507 (DATAJPA-343) -* @IdClass of @MappedSuperclass is not supported (DATAJPA-348) -* Improve query validation by creating explicit EntityManager instance (DATAJPA-350) -* @Lock annotation doesn't work on (at least) findOne method (DATAJPA-359) -* Improve SimpleJpaRepository.delete(ID id) by removing superfluous call to exists(…). (DATAJPA-363) -* Open up OSGi manifest for Spring 4 (DATAJPA-339) -* Impove OSGi manifest to allow usage with Spring 4 and Slf4j 1.6.x (DATAJPA-340) -* @QueryHints can not be used in a meta annotation (DATAJPA-345) -* Improve QueryDslJpaRepository.findAll(Predicate, Pageable) (DATAJPA-361) -* Release 1.3.3 (DATAJPA-369) - -Changes in version 1.4.0.M1 (2012-06-04) ----------------------------------------- -* Upgrade to Querydsl 3.0.0 (DATAJPA-321, DATAJPA-320) -* Support query internal LIKE expressions (DATAJPA-292, DATAJPA-341) -* Support to 'countBy' query methods. (DATAJPA-231) -* Support for case insensitive sorting in Pageable and Sort (DATAJPA-296, DATAJPA-327) -* Code doesn't compile with JDK 6 due to generic method signature (DATAJPA-304) -* XML Schema Validation errors in STS (DATAJPA-314) -* Allow building against Hibernate 4 (DATAJPA-316) -* JpaRepository interface should have @NoRepositoryBean (DATAJPA-317) -* findAll(ids) throws SQL grammar exception when given an empty collection (DATAJPA-332) -* Repository injection fails if BeanPostProcessor depends on repository (DATAJPA-335) -* Release 1.3.1 breaks auditing (DATAJPA-336) -* Wrong calculation of total number of elements when using DISTINCT clause (DATAJPA-342) -* Subselect bug introduced in the snapshot on 20130507 (DATAJPA-343) -* Improve query validation by creating explicit EntityManager instance (DATAJPA-350) -* JpaEntityInformationSupport could inspect potentially available @Version annotated field for null to discover isNew (DATAJPA-119) -* Specifications must support null as a parent Specification (DATAJPA-300) -* Prevent duplicate registration of PersistenceAnnotationBeanPostProcessor (DATAJPA-308) -* CDI repository beans should be application scoped (DATAJPA-319) -* Invalid class name referenced in readme.md (DATAJPA-334) -* Impove OSGi manifest to allow usage with Spring 4 and Slf4j 1.6.x (DATAJPA-340, DATAJPA-339) -* @QueryHints can now be used in a meta annotation (DATAJPA-345) -* Release 1.4. M1 (DATAJPA-351) - -Changes in version 1.3.2.RELEASE (2013-05-02) ---------------------------------------------- -* Release 1.3.1 breaks auditing (DATAJPA-336) -* findAll(ids) throws SQL grammar exception when given an empty collection (DATAJPA-332) -* Invalid class name referenced in readme.md (DATAJPA-334) - -Changes in version 1.3.1.RELEASE (2013-04-16) ---------------------------------------------- -* Support query internal LIKE expressions (DATAJPA-292) -* Code doesn't compile with JDK 6 due to generic method signature (DATAJPA-304) -* XML Schema Validation errors in STS (DATAJPA-314) -* Allow building against Hibernate 4 (DATAJPA-316) -* JpaRepository interface should have @NoRepositoryBean (DATAJPA-317) -* Specifications must support null as a parent Specification (DATAJPA-300) -* Prevent duplicate registration of PersistenceAnnotationBeanPostProcessor (DATAJPA-308) - -Changes in version 1.3.0.RELEASE (2012-02-07) ---------------------------------------------- -* Added support for annotation based auditing (DATAJPA-116) -* SimpleJpaRepository.exists(ID) works now, if ID is an composite key (DATAJPA-266) -* LockModePopulatingMethodInterceptor does not cause memory leak (DATAJPA-268) -* Pageable sorting by join property excludes null matches with Specifications (DATAJPA-277) -* Switch to Logback for test logging (DATAJPA-271) -* Fixed NullPointerException in unit tests (DATAJPA-280) -* Expose Spring Data Commons mapping metamodel (DATAJPA-274, DATAJPA-284, DATAJPA-283) -* Separated integration tests for different persistence providers (DATAJPA-286, DATAJPA-288) -* Polished documentation (DATAJPA-282, DATAJPA-291) -* Improved build (DATAJPA-285, DATAJPA-271) -* Upgraded to JodaTime 2.1 (DATAJPA-293) - -Changes in version 1.2.1.RELEASE (2012-02-07) ---------------------------------------------- -* SimpleJpaRepository.exists(ID) fails, if ID is an composite key (DATAJPA-266) -* LockModePopulatingMethodInterceptor leak causes memory leak (DATAJPA-268) -* Pageable sorting by join property excludes null matches with Specifications (DATAJPA-277) -* Switch to Logback for test logging (DATAJPA-271) - -Changes in version 1.2.0.RELEASE (2012-10-10) ----------------------------------------- -* Resolve conflicts between Spring Data Commons Core, JPA and Mongo (DATAJPA-146) -* Improved reference documentation/JavaDoc (DATAJPA-251, DATAJPA-261, DATAJPA-174, DATAJPA-238) -* Upgrade to latest Querydsl 2.8.0, APT 1.0.4 (DATAJPA-259, DATAJPA-260) -* Improved detection of joins to make sure sorting gets applied correctly (DATAJPA-252) -* Fixed application of Specifications.not(…) (DATAJPA-253) -* Fixed detection of total elements when paging over partitioned entities with EclipseLink (DATAJPA-257) - -Changes in version 1.2.0.RC1 (2012-07-24) ------------------------------------------ -* Sorting of paginated results is not working when Querydsl is used (DATAJPA-243) -* Namespace XSDs of current release version should refer to repositories XSD in version 1.0 (DATAJPA-239) -* NamedQuery should defer warning logs until the named query is actually detected (DATAJPA-241) -* JpaClassUtils#isEntityManagerOfType is not safe within an OSGI Environment (DATAJPA-244) -* Make Spring 3.1.2.RELEASE default Spring dependency version (DATAJPA-249) -* List compile time dependencies explicitly (DATAJPA-245) - -Changes in version 1.1.2.RELEASE (2012-08-24) ---------------------------------------------- -* Namespace XSDs of current release version should refer to repositories XSD in version 1.0 (DATAJPA-239) -* NamedQuery should defer warning logs until the named query is actually detected (DATAJPA-241) -* JpaClassUtils#isEntityManagerOfType is not safe within an OSGI Environment (DATAJPA-244) -* List compile time dependencies explicitly (DATAJPA-245) - -Changes in version 1.2.0.M1 (2012-07-23) ----------------------------------------- -* Added support for JavaConfig support for repositories (DATAJPA-69) -* Upgraded to Hibernate 3.6.10 and EclipseLink 2.4.0 (DATAJPA-229) -* A round of code improvements (DATAJPA-104) -* Throw more appropriate exception if manually defined query in @Query is invalid (DATAJPA-226) -* SimpleJpaRepository.findAll(Iterable ids) now works when handing in non-Lists (DATAJPA-232) -* OSGi bundle identifier is now org.springframework.data.jpa (DATAJPA-215) -* We now include JPA metamodel classes for our base domain types (DATAJPA-217) -* Improved documentation of how to use native queries with @Query (DATAJPA-227) - -Changes in version 1.1.1.RELEASE (2012-07-23) ---------------------------------------------- -* Throw more appropriate exception if manually defined query in @Query is invalid (DATAJPA-226) -* SimpleJpaRepository.findAll(Iterable ids) now works when handing in non-Lists (DATAJPA-232) -* OSGi bundle identifier is now org.springframework.data.jpa (DATAJPA-215) -* We now include JPA metamodel classes for our base domain types (DATAJPA-217) -* Improved documentation of how to use native queries with @Query (DATAJPA-227) -* Upgraded to Spring Data Commons 1.3.2.RELEASE (DATAJPA-233) - -Changes in version 1.1.0.RELEASE (2012-05-16) ---------------------------------------------- -* JPA Repository can get typing of @Query annotated findXXX methods wrong (DATAJPA-169) -* SimpleJpaRepository.delete(ID id) should validate entity/record existance prior to issue delete command (DATAJPA-177) -* StackOverflowError during query parsing if repository method does nots match with a property (DATAJPA-179) -* Typos in log message when named query is used with a Pageable (DATAJPA-186) -* MergingPersistenceUnitManager potentially adds classes multiple times (DATAJPA-189) -* JpaQueryExecution started to throw NullPointerException if pageable is null (DATAJPA-201) -* Typo in reference (DATAJPA-153) -* Readme file references old Spring Data JPA version (DATAJPA-155) -* Ability to specify template when create QueryDSL JPAQuery (DATAJPA-181) -* Provide more safety in custom @Queries with named @Params (DATAJPA-185) -* Log level used for message generated with named query with a Pageable parameter lower than the actual severity of the issue (DATAJPA-187) -* MergingPersistenceUnitManager should merge mapping file locations as well (DATAJPA-190) -* Adapt changes in CrudRepository interface (DATAJPA-191) -* @Query(nativeQuery=true) can't return List (DATAJPA-207) -* Add support for newly introduced StartingWith, EndingWith and Containing keywords (DATAJPA-180) -* Support Before and After keywords for query creation (DATAJPA-188) -* Minor typos in readme.md (DATAJPA-192) -* Upgrade to Spring Data Commons 1.3.0.RC1 (DATAJPA-193) -* Upgrade to Spring Data Commons 1.3.0.GA (DATAJPA-194) -* Upgrade to Querydsl 2.5.0 (DATAJPA-203) -* Sonar build is failing (DATAJPA-210) -* Document CDI integration (DATAJPA-211) -* Release 1.1 GA (DATAJPA-212) - -Changes in version 1.1.0.RC1 (2012-02-03) ------------------------------------------ -* Support for locking (DATAJPA-73) -* Added CDI integration for repositories (DATAJPA-136) -* Support for @IdClass in entities (DATAJPA-50) -* Support for LessThanEqual and GreaterThanEquals keywords in query methods (DATAJPA-108) -* Support for True/False as query keywords (DATAJPA-132) -* Queries derivated from methods now correctly bind null values using IS NULL (DATAJPA-121) -* Added PersistenceUnitPostProcessor to scan a package for JPA entities with Spring 3.0 (DATAJPA-123, DATAJPA-124) -* Improve performance for pagination queries if inexistant page is requested (DATAJPA-124) -* Don't apply query hints to count queries for pagination (DATAJPA-54) -* Fixed count query creation for manually declared queries with "order by" clause (DATAJPA-142) -* @OneToOne mappings in AbstractAuditable should rather be @ManyToOne ones (DATAJPA-120) -* Alias detection now works correctly with entity names containing numbers (DATAJPA-110) -* Added @NativeQuery annotation for repositories to trigger native queries (DATAJPA-117) -* Allow customization of NamedQuery to be used in @Query annotation (DATAJPA-129) -* Let deleteAll() use cascading deletes and introduced deleteAllInBatch() (DATAJPA-137) -* Fixed query creation for Comparable values (DATAJPA-99) -* The SimpleJpaRepository's deleteAll() does not call em.clear() anymore (DATAJPA-111) -* JpaMetamodelEntityInformation now deals with mapped superclasses as well (DATAJPA-141) -* MergingPersistenceUnitManager now works with Spring 3.1.0 DefaultPersistenceUnitManager (DATAJPA-138) -* Provide strategy interface to customize the date being set while auditing (DATAJPA-9) -* Improve exception message when crating a query from a method name fails (DATAJPA-159) -* Imporve injection of the EntityManager in QueryDslRepositorySupport (DATAJPA-135, DATAJPA-113) -* Consolidate Expression creation for property references and sort orders (DATAJPA-103) -* Fixed schema errors appearing in STS (DATAJPA-160) -* Change build system to build against Spring 3.0.x / 3.1 / 3.2 (DATAJPA-127) -* Upgraded dependency versions (Spring 3.0.7, Querydsl 2.3.0, Hibernate 3.6.9, EclipseLink 2.3.2, OpenJPA 2.1.1, AspectJ 1.6.12) (DATAJPA-102, DATAJPA-150, DATAJPA-145, DATAJPA-115) - -Changes in version 1.0.3.GA (2012-02-03) ----------------------------------------- -* MergingPersistenceUnitManager doesnt work with Spring 3.1.0 DefaultPersistenceUnitManager (DATAJPA-138) -* Spring Data- JPA Schema validation error in STS (DATAJPA-160) -* Improve exception message when crating a query from a method name fails (DATAJPA-159) - -Changes in version 1.0.2.GA (2011-12-06) ----------------------------------------- -* Fixed query creation for Comparable values (DATAJPA-99) -* Fixed alias detection when entity name contained number (DATAJPA-110) -* SimpleJpaRepository's deleteAll() does not call em.clear() anymore (DATAJPA-111) -* Upgraded to Querydsl 2.2.5 (DATAJPA-102, DATAJPA-115) -* Fixed auditor mappings in AbstractAuditable (DATAJPA-120) -* Consolidate Expression creation for property references and sort orders (DATAJPA-103) -* Fixed dependency injection in QueryDslRepositorySupport (DATAJPA-113) - -Changes in version 1.1.0.M1 (2011-09-05) ----------------------------------------- -* Added ability to ignore case in derived method names (DATAJPA-92) -* Upgraded dependency to Spring Data Commons 1.2.0.M1 - -Changes in version 1.0.1.GA (2011-09-05) ----------------------------------------- -* Fixed pagination for queries using group-by clause (DATAJPA-86) -* Fixed NoSuchElementException for queries using dynamic sorting (DATAJPA-90) -* JpaEntityInformationSupport.getMetadata(...) throws IllegalArgumentException if entity not found in JPA metamodel (DATAJPA-93) -* Sort handed via Pageable instance is now again regarded by query execution (DATAJPA-94) -* Fixed creating CriteriaQuery instances for some keywords when deriving queries from method names (DATAJPA-96) -* Fixed manifest version reference of javax.annotation (DATAJPA-79) -* Improved performanve of SimpleJpaRepository.exists(…) by not loading the entity (DATAJPA-81) -* Updated reference documentation covering persistence exception translation, custom namespace attributes (DATAJPA-91, DATAJPA-88, DATAJPA-80) -* Reformatted sources with Spring Data Eclipse formatter and added settings to project (DATAJPA-98) - -Changes in version 1.0.0.GA (2011-07-21) ----------------------------------------- -* Updated to Spring Data Commons 1.1.0.RELEASE - (DATACMNS-53, DATACMNS-49) -* Upgrade to Querydsl 2.2.0 (DATAJPA-78, DATACMNS-53) -* Improved performance by caching CriteriaQuery creation if possible (DATAJPA-71) -* Fixed version range for Spring Data Commons packages in template.mf (DATAJPA-72) -* Fixed count query execution for pagination queries with manually defined query (DATAJPA-77) -* Polished documentation - -Changes in version 1.0.0.RC1 (2011-06-21) - https://jira.springsource.org/browse/DATAJPA/fixforversion/11869 ------------------------------------------ -* Expose findAll(Specification spec, Sort sort) in JpaSpecificationExecutor (DATAJPA-48) -* Improved target domain class detection for queries returning void and numeric values (DATAJPA-44) -* Improved extensibility of QueryDslJpaRepository (DATAJPA-39) -* Configured Maven build to include Bundlor generated MANIFEST.MF (DATAJPA-42) -* Improved performance of query execution by uhsing method.getAnnotation(…) instead of AnnotationUtils.getAnnotation(…) -* Fixed exception thrown in QueryDslJpaRepository#findAll(Predicate predicate, Pageable pageable) if no result found (DATAJPA-61, DATACMNS-23) -* Improved error message in JpaQueryMethod (DATAJPA-59, DATAJPA-60) -* Querydsl dependency is now optional (DATAJPA-62) -* Improved isNew detection in AuditingEntityListener (DATAJPA-68) -* Documented In and NotIn keywords (DATAJPA-56) -* Updated documentation regarding Specifications usage (DATAJPA-43) -* Added MergingPersistenceUnitManager (DATAJPA-38) -* Added support for searching by Specification and Sort (DATAJPA-48) -* Automatic query creation now uses ParameterExpression binding over literal binding (DATAJPA-64) -* Changed clearAutomatically attribute of @Modifying to default to false (DATAJPA-31) -* JpaPersistableEntityInformation now uses Persistable.isNew(…) (DATAJPA-58) - -Changes in version 1.0.0.M2 (2011-03-24) - https://jira.springsource.org/browse/DATAJPA/fixforversion/11800 ----------------------------------------- -* Dynamic sorting functionality (through Sort query method parameter) works again -* Added support for 'Distinct' (DATACMNS-15) -* Added support for 'In' and 'NotIn' (DATAJPA-30) -* Adapted new metadata API (DATAJPA-32, DATACMNS-17) -* Support for XML based entity mapping (DATAJPA-28) -* @Query annotated queries get validated on Query meta-model creation (DATAJPA-14) -* Fixed dependency scopes and missing repository declarations (DATAJPA-33, DATAJPA-26) -* Adapted meta-model API from Commons module (DATAJPA-32) -* Added support for QueryDsl (DATAJPA-8) - -Changes in version 1.0.0.M1 (2011-02-10) - https://jira.springsource.org/browse/DATAJPA/fixforversion/11786 ----------------------------------------- -* Moved JPA sepcific code from Hades into Spring Data JPA (functional equivalent of Hades 2.0.2) building on common functionality in Spring Data Commons - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From d54056cdd15a46413c85443ffa5c3f524b608680 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Thu, 12 Aug 2021 15:02:55 +0200 Subject: [PATCH 043/821] Prepare 2.6 M2 (2021.1.0). See #2264 --- pom.xml | 8 ++++---- src/main/resources/notice.txt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 1c15621445..45bc0754ea 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-SNAPSHOT + 2.6.0-M2 @@ -25,7 +25,7 @@ 5.5.3.Final 8.0.23 42.2.19 - 2.6.0-SNAPSHOT + 2.6.0-M2 0.10.3 org.hibernate @@ -538,8 +538,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index e1e5caba6c..dc5d16a9fc 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data JPA 2.6 M1 (2021.1.0) +Spring Data JPA 2.6 M2 (2021.1.0) Copyright (c) [2011-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -27,4 +27,5 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From 4cb0b44e6fbe9705994a5cf0085368a10e232b22 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Thu, 12 Aug 2021 15:03:17 +0200 Subject: [PATCH 044/821] Release version 2.6 M2 (2021.1.0). See #2264 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 45bc0754ea..f6b9299683 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-SNAPSHOT + 2.6.0-M2 Spring Data JPA Spring Data module for JPA repositories. From b454750ded7eaf5e75478d5f6400cdc310361a97 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Thu, 12 Aug 2021 15:16:21 +0200 Subject: [PATCH 045/821] Prepare next development iteration. See #2264 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f6b9299683..45bc0754ea 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-M2 + 2.6.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. From 4350dc5e4f179791a1b0d43aa9e438901e8c506a Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Thu, 12 Aug 2021 15:16:23 +0200 Subject: [PATCH 046/821] After release cleanups. See #2264 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 45bc0754ea..1c15621445 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-M2 + 2.6.0-SNAPSHOT @@ -25,7 +25,7 @@ 5.5.3.Final 8.0.23 42.2.19 - 2.6.0-M2 + 2.6.0-SNAPSHOT 0.10.3 org.hibernate @@ -538,8 +538,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 01a99f40f00ebd3b0401bae671a3bb91a745a560 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 7 Sep 2021 09:17:50 +0200 Subject: [PATCH 047/821] Move documentation bits regarding AbstractAuditable into JPA-specific documentation. Closes #2284 --- src/main/asciidoc/jpa.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 1abb6f8d7c..f57182a3f3 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -988,9 +988,13 @@ interface UserRepository extends Repository { ==== :leveloffset: +1 + include::{spring-data-commons-docs}/auditing.adoc[] + :leveloffset: -1 +There is also a convenience base class, `AbstractAuditable`, which you can extend to avoid the need to manually implement the interface methods. Doing so increases the coupling of your domain classes to Spring Data, which might be something you want to avoid. Usually, the annotation-based way of defining auditing metadata is preferred as it is less invasive and more flexible. + [[jpa.auditing]] == JPA Auditing From ccf6039be5243de892598cc472039fbf53b6b67b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 10 Sep 2021 15:37:56 +0200 Subject: [PATCH 048/821] Upgrade to Maven Wrapper 3.8.2. See #2295 --- .mvn/wrapper/maven-wrapper.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 00d32aab1d..481b362fad 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip \ No newline at end of file +#Fri Sep 10 15:37:56 CEST 2021 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip From b56186897e2be793a81c9a0ccd8be1c8d04478a0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 14 Sep 2021 12:06:13 +0200 Subject: [PATCH 049/821] Disable parameter names for test compilation. We now no longer use compiled parameter names for tests to avoid interference with Eclipselink's mechanism of using these improperly for stored procedure calls. Especially the fact that a parameter is named "in" lets the database consider using an IN clause instead of the parameter name. When configuring the compile plugin, we also need to ensure that the AspectJ plugin post-processes the compiled classes and doesn't run before the Maven compiler plugin. Closes: #2298 See also https://github.com/spring-projects/spring-data-build/issues/1513 --- pom.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pom.xml b/pom.xml index 1c15621445..e31849e911 100644 --- a/pom.xml +++ b/pom.xml @@ -475,6 +475,22 @@ + + maven-compiler-plugin + + + java-test-compile + test-compile + + testCompile + + + false + + + + + io.starter aspectj-maven-plugin @@ -496,6 +512,7 @@ compile + process-classes From 6cf6835db815eafc6716747ee9656437fbcdaf26 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 15 Sep 2021 15:41:15 +0200 Subject: [PATCH 050/821] Change visibility of JpaMetamodelMappingContextFactoryBean. Closes #2299 --- .../config/JpaMetamodelMappingContextFactoryBean.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index 10c2a251c4..ce3cdd6f34 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -41,7 +41,7 @@ * @author Mark Paluch * @since 1.6 */ -class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean +public class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean implements ApplicationContextAware { private static final Logger LOG = LoggerFactory.getLogger(JpaMetamodelMappingContextFactoryBean.class); From 60299b6033e33179b59c9435cef97fb7cc74e56c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 Sep 2021 09:44:34 +0200 Subject: [PATCH 051/821] Prepare 2.6 M3 (2021.1.0). See #2277 --- pom.xml | 8 ++++---- src/main/resources/notice.txt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index e31849e911..03a12f7078 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-SNAPSHOT + 2.6.0-M3 @@ -25,7 +25,7 @@ 5.5.3.Final 8.0.23 42.2.19 - 2.6.0-SNAPSHOT + 2.6.0-M3 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index dc5d16a9fc..6e78db0fd5 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data JPA 2.6 M2 (2021.1.0) +Spring Data JPA 2.6 M3 (2021.1.0) Copyright (c) [2011-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -27,5 +27,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From 53542b6ad41d20cf255291c43d31b2864d33ac99 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 Sep 2021 09:44:56 +0200 Subject: [PATCH 052/821] Release version 2.6 M3 (2021.1.0). See #2277 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 03a12f7078..00c5148659 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-SNAPSHOT + 2.6.0-M3 Spring Data JPA Spring Data module for JPA repositories. From ce207f78457437b689e69feaf0075c9f875fa1f9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 Sep 2021 09:52:18 +0200 Subject: [PATCH 053/821] Prepare next development iteration. See #2277 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00c5148659..03a12f7078 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-M3 + 2.6.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. From 60b1182b7d5be1429101a4da8fe8192a71af1fc9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 Sep 2021 09:52:21 +0200 Subject: [PATCH 054/821] After release cleanups. See #2277 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 03a12f7078..e31849e911 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-M3 + 2.6.0-SNAPSHOT @@ -25,7 +25,7 @@ 5.5.3.Final 8.0.23 42.2.19 - 2.6.0-M3 + 2.6.0-SNAPSHOT 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 8d9fd1e92364a286abf053fe63eeb66fbc8fd87a Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 21 Sep 2021 14:15:16 +0200 Subject: [PATCH 055/821] Add Java 17 verification to CI pipeline. The pipeline replaces the one for 16. No changes apart from the CI configuration was necessary. Closes #2307 Original pull request #2308 --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 906d2af6c8..09e35f5a37 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -67,7 +67,7 @@ pipeline { } } - stage("test: baseline (jdk16)") { + stage("test: baseline (jdk17)") { agent { label 'data' } @@ -78,7 +78,7 @@ pipeline { steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('adoptopenjdk/openjdk16:latest').inside('-v $HOME:/tmp/jenkins-home') { + docker.image('openjdk:17-bullseye').inside('-v $HOME:/tmp/jenkins-home') { sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pjava11 clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' } } From a88b90a6b177a87e925dc75ea30653b152b5e469 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 30 Sep 2021 10:24:36 +0200 Subject: [PATCH 056/821] Refine CI triggers. See #2307 --- Jenkinsfile | 37 ++++++------------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 09e35f5a37..254514b07f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,8 +14,9 @@ pipeline { stages { stage("test: baseline (jdk8)") { when { + beforeAgent(true) anyOf { - branch 'main' + branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP") not { triggeredBy 'UpstreamCause' } } } @@ -42,8 +43,9 @@ pipeline { stage("Test other configurations") { when { + beforeAgent(true) allOf { - branch 'main' + branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP") not { triggeredBy 'UpstreamCause' } } } @@ -90,8 +92,9 @@ pipeline { stage('Release to artifactory') { when { + beforeAgent(true) anyOf { - branch 'main' + branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP") not { triggeredBy 'UpstreamCause' } } } @@ -121,34 +124,6 @@ pipeline { } } } - stage('Publish documentation') { - when { - branch 'main' - } - agent { - label 'data' - } - options { timeout(time: 20, unit: 'MINUTES') } - - environment { - ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') - } - - steps { - script { - docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute ' + - '-Dartifactory.server=https://repo.spring.io ' + - "-Dartifactory.username=${ARTIFACTORY_USR} " + - "-Dartifactory.password=${ARTIFACTORY_PSW} " + - "-Dartifactory.distribution-repository=temp-private-local " + - '-Dmaven.test.skip=true clean deploy -U -B' - } - } - } - } - } } post { From fa11454bd93f529a5fb8fae9bfb35341251c06a5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 6 Oct 2021 09:32:25 +0200 Subject: [PATCH 057/821] Log a warning when a query method is annotated with a query and a query name. We now log when a query method has an ambiguous declaration to clarify that the declared query is used. Closes #2319 --- .../query/JpaQueryLookupStrategy.java | 10 ++++++++++ .../jpa/repository/query/JpaQueryMethod.java | 8 ++++++++ .../query/JpaQueryLookupStrategyUnitTests.java | 17 +++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 0cb10ca92e..89d592420d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -19,6 +19,9 @@ import javax.persistence.EntityManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.data.jpa.repository.Query; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.NamedQueries; @@ -40,6 +43,8 @@ */ public final class JpaQueryLookupStrategy { + private static final Logger LOG = LoggerFactory.getLogger(JpaQueryLookupStrategy.class); + /** * Private constructor to prevent instantiation. */ @@ -145,6 +150,11 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, RepositoryQuery query = JpaQueryFactory.INSTANCE.fromQueryAnnotation(method, em, evaluationContextProvider); + if (query != null && method.hasAnnotatedQueryName()) { + LOG.warn(String.format( + "Query method %s is annotated with both, a query and a query name. Using the declared query.", method)); + } + if (null != query) { return query; } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index 4e19f32805..37a3cbdced 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -274,6 +274,13 @@ String getAnnotatedQuery() { return StringUtils.hasText(query) ? query : null; } + /** + * @return {@code true} if this method is annotated with {@code @Query(name=…)}. + */ + boolean hasAnnotatedQueryName() { + return StringUtils.hasText(getAnnotationValue("name", String.class)); + } + /** * Returns the required query string declared in a {@link Query} annotation or throws {@link IllegalStateException} if * neither the annotation found nor the attribute was specified. @@ -442,4 +449,5 @@ StoredProcedureAttributes getProcedureAttributes() { return storedProcedureAttributes; } + } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index d2b9ed30c7..c5b8f7bacd 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -46,6 +46,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.RepositoryQuery; /** * Unit tests for {@link JpaQueryLookupStrategy}. @@ -110,6 +111,19 @@ void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() thro .withMessageContaining(method.toString()); } + @Test // GH-2319 + void prefersDeclaredQuery() throws Exception { + + QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, + EVALUATION_CONTEXT_PROVIDER, EscapeCharacter.DEFAULT); + Method method = UserRepository.class.getMethod("annotatedQueryWithQueryAndQueryName"); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); + + RepositoryQuery repositoryQuery = strategy.resolveQuery(method, metadata, projectionFactory, namedQueries); + + assertThat(repositoryQuery).isInstanceOf(AbstractStringBasedJpaQuery.class); + } + interface UserRepository extends Repository { @Query("something absurd") @@ -117,5 +131,8 @@ interface UserRepository extends Repository { @Query(value = "select u.* from User u", nativeQuery = true) List findByInvalidNativeQuery(String param, Sort sort); + + @Query(value = "something absurd", name = "my-query-name") + User annotatedQueryWithQueryAndQueryName(); } } From 648bd74be5f34a115fdc0e569a2e3fb3d3d52cd3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 6 Oct 2021 09:32:43 +0200 Subject: [PATCH 058/821] Polishing. Move off deprecated API. See #2319 --- .../data/jpa/repository/query/DeclaredQuery.java | 6 +++--- .../data/jpa/repository/query/StoredProcedureJpaQuery.java | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java index 818b6f55b7..60b4906f87 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java @@ -18,7 +18,7 @@ import java.util.List; import org.springframework.lang.Nullable; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; /** * A wrapper for a String representation of a query offering information about the query. @@ -35,7 +35,7 @@ interface DeclaredQuery { * @return a {@literal DeclaredQuery} instance even for a {@literal null} or empty argument. */ static DeclaredQuery of(@Nullable String query) { - return StringUtils.isEmpty(query) ? EmptyDeclaredQuery.EMPTY_QUERY : new StringQuery(query); + return ObjectUtils.isEmpty(query) ? EmptyDeclaredQuery.EMPTY_QUERY : new StringQuery(query); } /** @@ -77,7 +77,7 @@ static DeclaredQuery of(@Nullable String query) { * Creates a new {@literal DeclaredQuery} representing a count query, i.e. a query returning the number of rows to be * expected from the original query, either derived from the query wrapped by this instance or from the information * passed as arguments. - * + * * @param countQuery an optional query string to be used if present. * @param countQueryProjection an optional return type for the query. * @return a new {@literal DeclaredQuery} instance. diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index 7b5c39ef9f..d8f276c0dc 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -62,7 +62,6 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { super(method, em); this.procedureAttributes = method.getProcedureAttributes(); this.useNamedParameters = useNamedParameters(method); - } /** @@ -204,7 +203,7 @@ private StoredProcedureQuery newAdhocStoredProcedureQuery() { ProcedureParameter procedureOutput = procedureAttributes.getOutputProcedureParameters().get(0); - /** + /* * If there is a {@link java.sql.ResultSet} with a {@link ParameterMode#REF_CURSOR}, find the output parameter. * Otherwise, no need, there is no need to find an output parameter. */ @@ -229,7 +228,7 @@ private StoredProcedureQuery newAdhocStoredProcedureQuery() { /** * Does this stored procedure have a {@link java.sql.ResultSet} using {@link ParameterMode#REF_CURSOR}? - * + * * @param procedureOutput * @return */ From 93705f1a9e85e004a0d5c25b726024da418c4c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9da=20Housni=20Alaoui?= Date: Fri, 17 Sep 2021 22:23:11 +0200 Subject: [PATCH 059/821] EntityManagerBeanDefinitionRegistrarPostProcessor sets primary flag on EntityManager bean definitions. Closes #2288. --- ...rBeanDefinitionRegistrarPostProcessor.java | 2 ++ ...egistrarPostProcessorIntegrationTests.java | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index 9f4d2f1381..c693221e9e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -43,6 +43,7 @@ * {@link EntityManagerFactory} instances. * * @author Oliver Gierke + * @author Réda Housni Alaoui */ public class EntityManagerBeanDefinitionRegistrarPostProcessor implements BeanFactoryPostProcessor, Ordered { @@ -85,6 +86,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) AbstractBeanDefinition emBeanDefinition = builder.getRawBeanDefinition(); + emBeanDefinition.setPrimary(definition.getBeanDefinition().isPrimary()); emBeanDefinition.addQualifier(new AutowireCandidateQualifier(Qualifier.class, definition.getBeanName())); emBeanDefinition.setScope(definition.getBeanDefinition().getScope()); emBeanDefinition.setSource(definition.getBeanDefinition().getSource()); diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java index 67c5919502..3013af435e 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java @@ -35,6 +35,7 @@ import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.Primary; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.stereotype.Component; @@ -46,6 +47,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Réda Housni Alaoui */ @ExtendWith(SpringExtension.class) @ContextConfiguration @@ -57,7 +59,8 @@ public class EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests { void injectsEntityManagerIntoConstructors() { assertThat(target).isNotNull(); - assertThat(target.em).isNotNull(); + assertThat(target.firstEm).isNotNull(); + assertThat(target.primaryEm).isNotNull(); } /** @@ -104,16 +107,24 @@ LocalContainerEntityManagerFactoryBean firstEmf() { LocalContainerEntityManagerFactoryBean secondEmf() { return emf(); } + + @Primary + @Bean + LocalContainerEntityManagerFactoryBean thirdEmf() { + return emf(); + } } @TestComponent static class EntityManagerInjectionTarget { - private final EntityManager em; + private final EntityManager firstEm; + private final EntityManager primaryEm; @Autowired - public EntityManagerInjectionTarget(@Qualifier("firstEmf") EntityManager em) { - this.em = em; + public EntityManagerInjectionTarget(@Qualifier("firstEmf") EntityManager firstEm, EntityManager primaryEm) { + this.firstEm = firstEm; + this.primaryEm = primaryEm; } } } From 58b73cf30882270f6631e5d0380a5d6dd37c20df Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 7 Oct 2021 16:01:41 -0500 Subject: [PATCH 060/821] Polishing. Related: #2288. --- .../EntityManagerBeanDefinitionRegistrarPostProcessor.java | 2 +- ...gerBeanDefinitionRegistrarPostProcessorIntegrationTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index c693221e9e..f86ac330bb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -32,7 +32,7 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.Ordered; -import org.springframework.data.jpa.util.BeanDefinitionUtils.EntityManagerFactoryBeanDefinition; +import org.springframework.data.jpa.util.BeanDefinitionUtils.*; import org.springframework.orm.jpa.SharedEntityManagerCreator; /** diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java index 3013af435e..7526a8afeb 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; @@ -123,6 +122,7 @@ static class EntityManagerInjectionTarget { @Autowired public EntityManagerInjectionTarget(@Qualifier("firstEmf") EntityManager firstEm, EntityManager primaryEm) { + this.firstEm = firstEm; this.primaryEm = primaryEm; } From ea430f34aa35a6a188a927148eed6abcb501c991 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 16 Sep 2021 14:04:25 -0500 Subject: [PATCH 061/821] Implement FluentQuery for Querydsl and Query by Example. Add support for both QueryByExampleExecutor and QuerydslPredicateExecutor. This is used in SimpleJpaRepository and QuerydslJpaPredicateExecutor, resulting in various test cases proving support by both examples and Querydsl predicates. NOTE: Class-based DTOs are NOT supported yet. See #2294 Original pull request: #2326. --- pom.xml | 2 +- .../jpa/repository/query/JpaQueryCreator.java | 10 +- .../FetchableFluentQueryByExample.java | 183 +++++++++++++++++ .../FetchableFluentQueryByPredicate.java | 173 ++++++++++++++++ .../support/FluentQuerySupport.java | 86 ++++++++ .../support/QuerydslJpaPredicateExecutor.java | 53 ++++- .../support/QuerydslJpaRepository.java | 12 +- .../support/SimpleJpaRepository.java | 42 +++- .../jpa/repository/UserRepositoryTests.java | 189 +++++++++++++++++- ...QuerydslJpaPredicateExecutorUnitTests.java | 133 +++++++++++- .../support/QuerydslJpaRepositoryTests.java | 16 +- .../support/SimpleJpaRepositoryUnitTests.java | 4 +- 12 files changed, 875 insertions(+), 28 deletions(-) create mode 100644 src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java create mode 100644 src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java create mode 100644 src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java diff --git a/pom.xml b/pom.xml index e31849e911..d91c077f28 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 5.5.3.Final 8.0.23 42.2.19 - 2.6.0-SNAPSHOT + 2.6.0-2228-SNAPSHOT 0.10.3 org.hibernate diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 6c9407852e..825d8a3415 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -144,8 +144,8 @@ protected Predicate or(Predicate base, Predicate predicate) { /** * Finalizes the given {@link Predicate} and applies the given sort. Delegates to - * {@link #complete(Predicate, Sort, CriteriaQuery, CriteriaBuilder, Root)} and hands it the current {@link CriteriaQuery} - * and {@link CriteriaBuilder}. + * {@link #complete(Predicate, Sort, CriteriaQuery, CriteriaBuilder, Root)} and hands it the current + * {@link CriteriaQuery} and {@link CriteriaBuilder}. */ @Override protected final CriteriaQuery complete(Predicate predicate, Sort sort) { @@ -271,10 +271,12 @@ public Predicate build() { return getTypedPath(root, part).isNotNull(); case NOT_IN: // cast required for eclipselink workaround, see DATAJPA-433 - return upperIfIgnoreCase(getTypedPath(root, part)).in((Expression>) provider.next(part, Collection.class).getExpression()).not(); + return upperIfIgnoreCase(getTypedPath(root, part)) + .in((Expression>) provider.next(part, Collection.class).getExpression()).not(); case IN: // cast required for eclipselink workaround, see DATAJPA-433 - return upperIfIgnoreCase(getTypedPath(root, part)).in((Expression>) provider.next(part, Collection.class).getExpression()); + return upperIfIgnoreCase(getTypedPath(root, part)) + .in((Expression>) provider.next(part, Collection.class).getExpression()); case STARTING_WITH: case ENDING_WITH: case CONTAINING: diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java new file mode 100644 index 0000000000..798f4663e5 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -0,0 +1,183 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; + +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.query.EscapeCharacter; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.lang.Nullable; + +/** + * Immutable implementation of {@link FetchableFluentQuery} based on Query by {@link Example}. All methods that return a + * {@link FetchableFluentQuery} will return a new instance, not the original. + * + * @param Domain type + * @param Result type + * @author Greg Turnquist + * @since 2.6 + */ +class FetchableFluentQueryByExample extends FluentQuerySupport implements FetchableFluentQuery { + + private final Example example; + private final Function> finder; + private final Function, Long> countOperation; + private final Function, Boolean> existsOperation; + private final EntityManager entityManager; + private final EscapeCharacter escapeCharacter; + + public FetchableFluentQueryByExample(Example example, Function> finder, + Function, Long> countOperation, Function, Boolean> existsOperation, + MappingContext, ? extends PersistentProperty> context, + EntityManager entityManager, EscapeCharacter escapeCharacter) { + this(example, (Class) example.getProbeType(), Sort.unsorted(), null, finder, countOperation, existsOperation, + context, entityManager, escapeCharacter); + } + + private FetchableFluentQueryByExample(Example example, Class returnType, Sort sort, + @Nullable Collection properties, Function> finder, + Function, Long> countOperation, Function, Boolean> existsOperation, + MappingContext, ? extends PersistentProperty> context, + EntityManager entityManager, EscapeCharacter escapeCharacter) { + + super(returnType, sort, properties, context); + this.example = example; + this.finder = finder; + this.countOperation = countOperation; + this.existsOperation = existsOperation; + this.entityManager = entityManager; + this.escapeCharacter = escapeCharacter; + } + + @Override + public FetchableFluentQuery sortBy(Sort sort) { + + return new FetchableFluentQueryByExample(this.example, this.resultType, this.sort.and(sort), this.properties, + this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); + } + + @Override + public FetchableFluentQuery as(Class resultType) { + + if (!resultType.isInterface()) { + throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); + } + + return new FetchableFluentQueryByExample(this.example, resultType, this.sort, this.properties, this.finder, + this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); + } + + @Override + public FetchableFluentQuery project(Collection properties) { + + return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort, mergeProperties(properties), + this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); + } + + @Override + public R oneValue() { + + TypedQuery limitedQuery = this.finder.apply(this.sort); + limitedQuery.setMaxResults(2); // Never need more than 2 values + + List results = limitedQuery // + .getResultStream() // + .map(getConversionFunction(this.example.getProbeType(), this.resultType)) // + .collect(Collectors.toList()); + ; + + if (results.size() > 1) { + throw new IncorrectResultSizeDataAccessException(1); + } + + return results.isEmpty() ? null : results.get(0); + } + + @Override + public R firstValue() { + + TypedQuery limitedQuery = this.finder.apply(this.sort); + limitedQuery.setMaxResults(1); // Never need more than 1 value + + List results = limitedQuery // + .getResultStream() // + .map(getConversionFunction(this.example.getProbeType(), this.resultType)) // + .collect(Collectors.toList()); + + return results.isEmpty() ? null : results.get(0); + } + + @Override + public List all() { + return stream().collect(Collectors.toList()); + } + + @Override + public Page page(Pageable pageable) { + return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); + } + + @Override + public Stream stream() { + + return this.finder.apply(this.sort) // + .getResultStream() // + .map(getConversionFunction(this.example.getProbeType(), this.resultType)); + } + + @Override + public long count() { + return this.countOperation.apply(example); + } + + @Override + public boolean exists() { + return this.existsOperation.apply(example); + } + + private Page readPage(Pageable pageable) { + + TypedQuery pagedQuery = this.finder.apply(this.sort); + + if (pageable.isPaged()) { + pagedQuery.setFirstResult((int) pageable.getOffset()); + pagedQuery.setMaxResults(pageable.getPageSize()); + } + + List paginatedResults = pagedQuery.getResultStream() // + .map(getConversionFunction(this.example.getProbeType(), this.resultType)) // + .collect(Collectors.toList()); + + return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.example)); + } +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java new file mode 100644 index 0000000000..ba2b0790fc --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -0,0 +1,173 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import java.util.Collection; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.lang.Nullable; + +import com.querydsl.core.types.Predicate; +import com.querydsl.jpa.JPQLQuery; + +/** + * Immutable implementation of {@link FetchableFluentQuery} based on a Querydsl {@link Predicate}. All methods that + * return a {@link FetchableFluentQuery} will return a new instance, not the original. + * + * @param Domain type + * @param Result type + * @author Greg Turnquist + * @since 2.6 + */ +class FetchableFluentQueryByPredicate extends FluentQuerySupport implements FetchableFluentQuery { + + private final Predicate predicate; + private final Function> finder; + private final BiFunction> pagedFinder; + private final Function countOperation; + private final Function existsOperation; + private final Class entityType; + + public FetchableFluentQueryByPredicate(Predicate predicate, Class resultType, Function> finder, + BiFunction> pagedFinder, Function countOperation, + Function existsOperation, Class entityType, + MappingContext, ? extends PersistentProperty> context) { + this(predicate, resultType, Sort.unsorted(), null, finder, pagedFinder, countOperation, existsOperation, entityType, + context); + } + + private FetchableFluentQueryByPredicate(Predicate predicate, Class resultType, Sort sort, + @Nullable Collection properties, Function> finder, + BiFunction> pagedFinder, Function countOperation, + Function existsOperation, Class entityType, + MappingContext, ? extends PersistentProperty> context) { + + super(resultType, sort, properties, context); + this.predicate = predicate; + this.finder = finder; + this.pagedFinder = pagedFinder; + this.countOperation = countOperation; + this.existsOperation = existsOperation; + this.entityType = entityType; + } + + @Override + public FetchableFluentQuery sortBy(Sort sort) { + + return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort.and(sort), this.properties, + this.finder, this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context); + } + + @Override + public FetchableFluentQuery as(Class resultType) { + + if (!resultType.isInterface()) { + throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); + } + + return new FetchableFluentQueryByPredicate<>(this.predicate, resultType, this.sort, this.properties, this.finder, + this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context); + } + + @Override + public FetchableFluentQuery project(Collection properties) { + + return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort, + mergeProperties(properties), this.finder, this.pagedFinder, this.countOperation, this.existsOperation, + this.entityType, this.context); + } + + @Override + public R oneValue() { + + List results = this.finder.apply(this.sort) // + .limit(2) // Never need more than 2 values + .stream() // + .map(getConversionFunction(this.entityType, this.resultType)) // + .collect(Collectors.toList()); + + if (results.size() > 1) { + throw new IncorrectResultSizeDataAccessException(1); + } + + return results.isEmpty() ? null : results.get(0); + } + + @Override + public R firstValue() { + + List results = this.finder.apply(this.sort) // + .limit(1) // Never need more than 1 value + .stream() // + .map(getConversionFunction(this.entityType, this.resultType)) // + .collect(Collectors.toList()); + + return results.isEmpty() ? null : results.get(0); + } + + @Override + public List all() { + return stream().collect(Collectors.toList()); + } + + @Override + public Page page(Pageable pageable) { + return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); + } + + @Override + public Stream stream() { + + return this.finder.apply(this.sort) // + .stream() // + .map(getConversionFunction(this.entityType, this.resultType)); + } + + @Override + public long count() { + return this.countOperation.apply(this.predicate); + } + + @Override + public boolean exists() { + return this.existsOperation.apply(this.predicate); + } + + private Page readPage(Pageable pageable) { + + JPQLQuery pagedQuery = this.pagedFinder.apply(this.sort, pageable); + + List paginatedResults = pagedQuery.stream() // + .map(getConversionFunction(this.entityType, this.resultType)) // + .collect(Collectors.toList()); + + return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.predicate)); + } +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java new file mode 100644 index 0000000000..d8cf794577 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.data.domain.Sort; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; +import org.springframework.lang.Nullable; + +/** + * Supporting class containing some state and convenience methods for building and executing fluent queries. + * + * @param The resulting type of the query. + * @author Greg Turnquist + * @since 2.6 + */ +abstract class FluentQuerySupport { + + protected final Class resultType; + protected final Sort sort; + protected final @Nullable Set properties; + protected final MappingContext, ? extends PersistentProperty> context; + + private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); + + FluentQuerySupport(Class resultType, Sort sort, @Nullable Collection properties, + MappingContext, ? extends PersistentProperty> context) { + + this.resultType = resultType; + this.sort = sort; + + if (properties != null) { + this.properties = new HashSet<>(properties); + } else { + this.properties = null; + } + + this.context = context; + } + + final Collection mergeProperties(Collection additionalProperties) { + + Set newProperties = new HashSet<>(); + if (this.properties != null) { + newProperties.addAll(this.properties); + } + newProperties.addAll(additionalProperties); + return Collections.unmodifiableCollection(newProperties); + } + + @SuppressWarnings("unchecked") + final Function getConversionFunction(Class inputType, Class targetType) { + + if (targetType.isAssignableFrom(inputType)) { + return (Function) Function.identity(); + } + + if (targetType.isInterface()) { + return o -> projectionFactory.createProjection(targetType, o); + } + + return o -> DefaultConversionService.getSharedInstance().convert(o, targetType); + } +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 14c893165a..32676777c7 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -15,8 +15,11 @@ */ package org.springframework.data.jpa.repository.support; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Function; import javax.persistence.EntityManager; import javax.persistence.LockModeType; @@ -25,10 +28,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.querydsl.QSort; import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.repository.support.PageableExecutionUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -51,6 +56,7 @@ * @author Jocelyn Ntakpe * @author Christoph Strobl * @author Jens Schauder + * @author Greg Turnquist */ public class QuerydslJpaPredicateExecutor implements QuerydslPredicateExecutor { @@ -80,9 +86,9 @@ public QuerydslJpaPredicateExecutor(JpaEntityInformation entityInformation } /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.mysema.query.types.Predicate) - */ + * (non-Javadoc) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.mysema.query.types.Predicate) + */ @Override public Optional findOne(Predicate predicate) { @@ -161,10 +167,45 @@ public Page findAll(Predicate predicate, Pageable pageable) { return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount); } + @Override + public R findBy(Predicate predicate, Function, R> queryFunction) { + + Assert.notNull(predicate, "Predicate must not be null!"); + Assert.notNull(queryFunction, "Function must not be null!"); + + Function> finder = sort -> { + JPQLQuery select = createQuery(predicate).select(path); + + if (sort != null) { + select = querydsl.applySorting(sort, select); + } + + return select; + }; + + BiFunction> pagedFinder = (sort, pageable) -> { + + JPQLQuery select = finder.apply(sort); + + if (pageable.isPaged()) { + select = querydsl.applyPagination(pageable, select); + } + + return select; + }; + + FetchableFluentQuery fluentQuery = (FetchableFluentQuery) new FetchableFluentQueryByPredicate<>(predicate, + entityInformation.getJavaType(), finder, pagedFinder, this::count, this::exists, + this.entityInformation.getJavaType(), + new JpaMetamodelMappingContext(Collections.singleton(this.entityManager.getMetamodel()))); + + return queryFunction.apply(fluentQuery); + } + /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.mysema.query.types.Predicate) - */ + * (non-Javadoc) + * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.mysema.query.types.Predicate) + */ @Override public long count(Predicate predicate) { return createQuery(predicate).fetchCount(); diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index cb92df3977..05307124fe 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -18,6 +18,7 @@ import java.io.Serializable; import java.util.List; import java.util.Optional; +import java.util.function.Function; import javax.persistence.EntityManager; import javax.persistence.LockModeType; @@ -30,6 +31,7 @@ import org.springframework.data.querydsl.QSort; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.querydsl.SimpleEntityPathResolver; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.data.repository.support.PageableExecutionUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -46,13 +48,14 @@ * QueryDsl specific extension of {@link SimpleJpaRepository} which adds implementation for * {@link QuerydslPredicateExecutor}. * - * @deprecated Instead of this class use {@link QuerydslJpaPredicateExecutor} * @author Oliver Gierke * @author Thomas Darimont * @author Mark Paluch * @author Jocelyn Ntakpe * @author Christoph Strobl * @author Jens Schauder + * @author Greg Turnquist + * @deprecated Instead of this class use {@link QuerydslJpaPredicateExecutor} */ @Deprecated public class QuerydslJpaRepository extends SimpleJpaRepository @@ -164,6 +167,13 @@ public Page findAll(Predicate predicate, Pageable pageable) { return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount); } + @Override + public R findBy(Predicate predicate, + Function, R> queryFunction) { + throw new UnsupportedOperationException( + "Fluent Query API support for Querydsl is only found in QuerydslJpaPredicateExecutor."); + } + /* * (non-Javadoc) * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.mysema.query.types.Predicate) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 2f3af6f25e..cc2b001a85 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import javax.persistence.EntityManager; import javax.persistence.LockModeType; @@ -47,11 +48,16 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder; import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.query.EscapeCharacter; import org.springframework.data.jpa.repository.query.QueryUtils; import org.springframework.data.jpa.repository.support.QueryHints.NoHints; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.repository.support.PageableExecutionUtils; import org.springframework.data.util.ProxyUtils; import org.springframework.data.util.Streamable; @@ -64,6 +70,8 @@ * Default implementation of the {@link org.springframework.data.repository.CrudRepository} interface. This will offer * you a more sophisticated interface than the plain {@link EntityManager} . * + * @param the type of the entity to handle + * @param the type of the entity's identifier * @author Oliver Gierke * @author Eberhard Wolff * @author Thomas Darimont @@ -75,8 +83,7 @@ * @author Moritz Becker * @author Sander Krabbenborg * @author Jesse Wouters - * @param the type of the entity to handle - * @param the type of the entity's identifier + * @author Greg Turnquist */ @Repository @Transactional(readOnly = true) @@ -87,6 +94,7 @@ public class SimpleJpaRepository implements JpaRepositoryImplementation entityInformation; private final EntityManager em; private final PersistenceProvider provider; + private final MappingContext, ? extends PersistentProperty> context; private @Nullable CrudMethodMetadata metadata; private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT; @@ -105,6 +113,9 @@ public SimpleJpaRepository(JpaEntityInformation entityInformation, EntityM this.entityInformation = entityInformation; this.em = entityManager; this.provider = PersistenceProvider.fromEntityManager(entityManager); + this.context = em.getMetamodel() != null // + ? new JpaMetamodelMappingContext(Collections.singleton(em.getMetamodel())) // + : null; } /** @@ -567,9 +578,30 @@ public Page findAll(Example example, Pageable pageable) { /* * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#count() + * @see org.springframework.data.repository.query.QueryByExampleExecutor#findBy(org.springframework.data.domain.Example, java.util.function.Function) */ @Override + public R findBy(Example example, Function, R> queryFunction) { + + Function> finder = sort -> { + + ExampleSpecification spec = new ExampleSpecification<>(example, escapeCharacter); + Class probeType = example.getProbeType(); + + return getQuery(spec, probeType, sort); + }; + + FetchableFluentQuery fluentQuery = new FetchableFluentQueryByExample<>(example, finder, this::count, + this::exists, this.context, this.em, this.escapeCharacter); + + return queryFunction.apply(fluentQuery); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#count() + */ + @Override public long count() { return em.createQuery(getCountQueryString(), Long.class).getSingleResult(); } @@ -872,8 +904,8 @@ private static boolean isUnpaged(Pageable pageable) { * {@link SimpleJpaRepository#findAllById(Iterable)}. Workaround for OpenJPA not binding collections to in-clauses * correctly when using by-name binding. * - * @see OPENJPA-2018 * @author Oliver Gierke + * @see OPENJPA-2018 */ @SuppressWarnings("rawtypes") private static final class ByIdsSpecification implements Specification { @@ -905,9 +937,9 @@ public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuild * {@link Specification} that gives access to the {@link Predicate} instance representing the values contained in the * {@link Example}. * + * @param * @author Christoph Strobl * @since 1.10 - * @param */ private static class ExampleSpecification implements Specification { diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 26ea26416e..29f03ef110 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -24,6 +24,8 @@ import static org.springframework.data.jpa.domain.Specification.not; import static org.springframework.data.jpa.domain.sample.UserSpecifications.*; +import lombok.Data; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -47,7 +49,6 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; @@ -75,7 +76,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; - /** * Base integration test class for {@code UserRepository}. Loads a basic (non-namespace) Spring configuration file as * well as Hibernate configuration to execute tests. @@ -92,6 +92,7 @@ * @author Andrey Kovalev * @author Sander Krabbenborg * @author Jesse Wouters + * @author Greg Turnquist */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @@ -2030,6 +2031,186 @@ void findOneByExampleWithExcludedAttributes() { assertThat(repository.findOne(example)).contains(firstUser); } + @Test // GH-2294 + void findByFluentExampleWithSorting() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + List users = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.sortBy(Sort.by("firstname")).all()); + + assertThat(users).containsExactly(thirdUser, firstUser, fourthUser); + } + + @Test // GH-2294 + void findByFluentExampleFirstValue() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + User firstUser = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.sortBy(Sort.by("firstname")).firstValue()); + + assertThat(firstUser).isEqualTo(thirdUser); + } + + @Test // GH-2294 + void findByFluentExampleOneValue() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> { + repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.sortBy(Sort.by("firstname")).oneValue()); + }); + } + + @Test // GH-2294 + void findByFluentExampleStream() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + Stream userStream = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.sortBy(Sort.by("firstname")).stream()); + + assertThat(userStream).containsExactly(thirdUser, firstUser, fourthUser); + } + + @Test // GH-2294 + void findByFluentExamplePage() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + Example userProbe = of(prototype, matching().withIgnorePaths("age", "createdAt", "active") + .withMatcher("firstname", GenericPropertyMatcher::contains)); + + Page page0 = repository.findBy(userProbe, // + q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(0, 2))); + + Page page1 = repository.findBy(userProbe, // + q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(1, 2))); + + assertThat(page0.getContent()).containsExactly(thirdUser, firstUser); + assertThat(page1.getContent()).containsExactly(fourthUser); + } + + @Test // GH-2294 + void findByFluentExampleWithInterfaceBasedProjection() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + List users = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.as(UserProjectionInterfaceBased.class).all()); + + assertThat(users).extracting(UserProjectionInterfaceBased::getFirstname) + .containsExactlyInAnyOrder(firstUser.getFirstname(), thirdUser.getFirstname(), fourthUser.getFirstname()); + } + + @Test // GH-2294 + void findByFluentExampleWithSortedInterfaceBasedProjection() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + List users = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.as(UserProjectionInterfaceBased.class).sortBy(Sort.by("firstname")).all()); + + assertThat(users).extracting(UserProjectionInterfaceBased::getFirstname) + .containsExactlyInAnyOrder(thirdUser.getFirstname(), firstUser.getFirstname(), fourthUser.getFirstname()); + } + + @Test // GH-2294 + void fluentExamplesWithClassBasedDtosNotYetSupported() { + + @Data + class UserDto { + String firstname; + } + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> { + + User prototype = new User(); + prototype.setFirstname("v"); + + repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.as(UserDto.class).sortBy(Sort.by("firstname")).all()); + }); + } + + @Test // GH-2294 + void countByFluentExample() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + long numOfUsers = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.sortBy(Sort.by("firstname")).count()); + + assertThat(numOfUsers).isEqualTo(3); + } + + @Test // GH-2294 + void existsByFluentExample() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setFirstname("v"); + + boolean exists = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.sortBy(Sort.by("firstname")).exists()); + + assertThat(exists).isTrue(); + } + @Test // DATAJPA-218 void countByExampleWithExcludedAttributes() { @@ -2349,4 +2530,8 @@ private Page executeSpecWithSort(Sort sort) { assertThat(result.getTotalElements()).isEqualTo(2L); return result; } + + private interface UserProjectionInterfaceBased { + String getFirstname(); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index d707765826..49ee607867 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -17,17 +17,19 @@ import static org.assertj.core.api.Assertions.*; +import lombok.Data; + import java.sql.Date; +import java.time.LocalDate; import java.util.List; +import java.util.stream.Stream; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; -import java.time.LocalDate; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -60,6 +62,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Malte Mauelshagen + * @author Greg Turnquist */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @@ -87,7 +90,8 @@ void setUp() { oliver = repository.save(new User("Oliver", "matthews", "oliver@matthews.com")); adminRole = em.merge(new Role("admin")); - this.predicateExecutor = new QuerydslJpaPredicateExecutor<>(information, em, SimpleEntityPathResolver.INSTANCE, null); + this.predicateExecutor = new QuerydslJpaPredicateExecutor<>(information, em, SimpleEntityPathResolver.INSTANCE, + null); } @Test @@ -217,7 +221,8 @@ void findBySpecificationWithSortByQueryDslOrderSpecifierWithQPageRequest() { QUser user = QUser.user; - Page page = predicateExecutor.findAll(user.firstname.isNotNull(), new QPageRequest(0, 10, user.firstname.asc())); + Page page = predicateExecutor.findAll(user.firstname.isNotNull(), + new QPageRequest(0, 10, user.firstname.asc())); assertThat(page.getContent()).containsExactly(carter, dave, oliver); } @@ -317,7 +322,127 @@ void findOneWithPredicateReturnsOptionalEmptyWhenNoDataFound() { @Test // DATAJPA-1115 void findOneWithPredicateThrowsExceptionForNonUniqueResults() { + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) .isThrownBy(() -> predicateExecutor.findOne(user.emailAddress.contains("com"))); } + + @Test // GH-2294 + void findByFluentPredicate() { + + List users = predicateExecutor.findBy(user.firstname.eq("Dave"), q -> q.sortBy(Sort.by("firstname")).all()); + + assertThat(users).containsExactly(dave); + } + + @Test // GH-2294 + void findByFluentPredicateWithSorting() { + + List users = predicateExecutor.findBy(user.firstname.isNotNull(), q -> q.sortBy(Sort.by("firstname")).all()); + + assertThat(users).containsExactly(carter, dave, oliver); + } + + @Test // GH-2294 + void findByFluentPredicateWithEqualsAndSorting() { + + List users = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.sortBy(Sort.by("firstname")).all()); + + assertThat(users).containsExactly(dave, oliver); + } + + @Test // GH-2294 + void findByFluentPredicateFirstValue() { + + User firstUser = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.sortBy(Sort.by("firstname")).firstValue()); + + assertThat(firstUser).isEqualTo(dave); + } + + @Test // GH-2294 + void findByFluentPredicateOneValue() { + + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy( + () -> predicateExecutor.findBy(user.firstname.contains("v"), q -> q.sortBy(Sort.by("firstname")).oneValue())); + } + + @Test // GH-2294 + void findByFluentPredicateStream() { + + Stream userStream = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.sortBy(Sort.by("firstname")).stream()); + + assertThat(userStream).containsExactly(dave, oliver); + } + + @Test // GH-2294 + void findByFluentPredicatePage() { + + Predicate predicate = user.firstname.contains("v"); + + Page page0 = predicateExecutor.findBy(predicate, + q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(0, 1))); + + Page page1 = predicateExecutor.findBy(predicate, + q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(1, 1))); + + assertThat(page0.getContent()).containsExactly(dave); + assertThat(page1.getContent()).containsExactly(oliver); + } + + @Test // GH-2294 + void findByFluentPredicateWithInterfaceBasedProjection() { + + List users = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.as(UserProjectionInterfaceBased.class).all()); + + assertThat(users).extracting(UserProjectionInterfaceBased::getFirstname) + .containsExactlyInAnyOrder(dave.getFirstname(), oliver.getFirstname()); + } + + @Test // GH-2294 + void findByFluentPredicateWithSortedInterfaceBasedProjection() { + + List userProjections = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.as(UserProjectionInterfaceBased.class).sortBy(Sort.by("firstname")).all()); + + assertThat(userProjections).extracting(UserProjectionInterfaceBased::getFirstname) + .containsExactly(dave.getFirstname(), oliver.getFirstname()); + } + + @Test // GH-2294 + void countByFluentPredicate() { + + long userCount = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.sortBy(Sort.by("firstname")).count()); + + assertThat(userCount).isEqualTo(2); + } + + @Test // GH-2294 + void existsByFluentPredicate() { + + boolean exists = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.sortBy(Sort.by("firstname")).exists()); + + assertThat(exists).isTrue(); + } + + @Test // GH-2294 + void fluentExamplesWithClassBasedDtosNotYetSupported() { + + @Data + class UserDto { + String firstname; + } + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> predicateExecutor + .findBy(user.firstname.contains("v"), q -> q.as(UserDto.class).sortBy(Sort.by("firstname")).all())); + } + + private interface UserProjectionInterfaceBased { + String getFirstname(); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java index bff696d650..7d0df51936 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java @@ -16,18 +16,21 @@ package org.springframework.data.jpa.repository.support; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.domain.Example.*; +import static org.springframework.data.domain.ExampleMatcher.*; + +import lombok.Data; import java.sql.Date; +import java.time.LocalDate; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; -import java.time.LocalDate; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -58,6 +61,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Malte Mauelshagen + * @author Greg Turnquist */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @@ -325,7 +329,15 @@ void findOneWithPredicateReturnsOptionalEmptyWhenNoDataFound() { @Test // DATAJPA-1115 void findOneWithPredicateThrowsExceptionForNonUniqueResults() { + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) .isThrownBy(() -> repository.findOne(user.emailAddress.contains("com"))); } + + @Test // GH-2294 + void findByFluentQuery() { + + assertThatExceptionOfType(UnsupportedOperationException.class) + .isThrownBy(() -> repository.findBy(user.firstname.eq("Dave"), q -> q.sortBy(Sort.by("firstname")).all())); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index 5a49cd8520..1c2d3827e9 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -35,7 +35,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.domain.sample.User; @@ -186,12 +185,11 @@ void doNothingWhenNonExistentInstanceGetsDeleted() { newUser.setId(23); when(information.isNew(newUser)).thenReturn(false); - when(em.find(User.class,23)).thenReturn(null); + when(em.find(User.class, 23)).thenReturn(null); repo.delete(newUser); verify(em, never()).remove(newUser); verify(em, never()).merge(newUser); } - } From a31c39db7a12113b5adcb6fbaa2a92d97f1b3a02 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 8 Oct 2021 11:23:58 +0200 Subject: [PATCH 062/821] Polishing. Fix version reference to Spring Data Commons. Avoid stream creation when fetching all/one/first element. Move off deprecated API. Add override comments. Add null guargs. See #2294 Original pull request: #2326. --- pom.xml | 2 +- .../FetchableFluentQueryByExample.java | 93 +++++++++++++++---- .../FetchableFluentQueryByPredicate.java | 91 ++++++++++++++---- .../support/QuerydslJpaPredicateExecutor.java | 13 ++- .../support/SimpleJpaRepository.java | 5 +- 5 files changed, 162 insertions(+), 42 deletions(-) diff --git a/pom.xml b/pom.xml index d91c077f28..e31849e911 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 5.5.3.Final 8.0.23 42.2.19 - 2.6.0-2228-SNAPSHOT + 2.6.0-SNAPSHOT 0.10.3 org.hibernate diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index 798f4663e5..c187ccd884 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.repository.support; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; import javax.persistence.EntityManager; @@ -37,6 +37,7 @@ import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * Immutable implementation of {@link FetchableFluentQuery} based on Query by {@link Example}. All methods that return a @@ -45,6 +46,7 @@ * @param Domain type * @param Result type * @author Greg Turnquist + * @author Mark Paluch * @since 2.6 */ class FetchableFluentQueryByExample extends FluentQuerySupport implements FetchableFluentQuery { @@ -79,24 +81,39 @@ private FetchableFluentQueryByExample(Example example, Class returnType, S this.escapeCharacter = escapeCharacter; } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort) + */ @Override public FetchableFluentQuery sortBy(Sort sort) { - return new FetchableFluentQueryByExample(this.example, this.resultType, this.sort.and(sort), this.properties, + Assert.notNull(sort, "Sort must not be null!"); + + return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort.and(sort), this.properties, this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) + */ @Override public FetchableFluentQuery as(Class resultType) { + Assert.notNull(resultType, "Projection target type must not be null!"); if (!resultType.isInterface()) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); } - return new FetchableFluentQueryByExample(this.example, resultType, this.sort, this.properties, this.finder, + return new FetchableFluentQueryByExample<>(this.example, resultType, this.sort, this.properties, this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection) + */ @Override public FetchableFluentQuery project(Collection properties) { @@ -104,62 +121,86 @@ public FetchableFluentQuery project(Collection properties) { this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() + */ @Override public R oneValue() { TypedQuery limitedQuery = this.finder.apply(this.sort); limitedQuery.setMaxResults(2); // Never need more than 2 values - List results = limitedQuery // - .getResultStream() // - .map(getConversionFunction(this.example.getProbeType(), this.resultType)) // - .collect(Collectors.toList()); - ; + List results = limitedQuery.getResultList(); if (results.size() > 1) { throw new IncorrectResultSizeDataAccessException(1); } - return results.isEmpty() ? null : results.get(0); + return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue() + */ @Override public R firstValue() { TypedQuery limitedQuery = this.finder.apply(this.sort); limitedQuery.setMaxResults(1); // Never need more than 1 value - List results = limitedQuery // - .getResultStream() // - .map(getConversionFunction(this.example.getProbeType(), this.resultType)) // - .collect(Collectors.toList()); + List results = limitedQuery.getResultList(); - return results.isEmpty() ? null : results.get(0); + return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() + */ @Override public List all() { - return stream().collect(Collectors.toList()); + + List resultList = this.finder.apply(this.sort).getResultList(); + + return convert(resultList); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) + */ @Override public Page page(Pageable pageable) { return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() + */ @Override public Stream stream() { return this.finder.apply(this.sort) // .getResultStream() // - .map(getConversionFunction(this.example.getProbeType(), this.resultType)); + .map(getConversionFunction()); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() + */ @Override public long count() { return this.countOperation.apply(example); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() + */ @Override public boolean exists() { return this.existsOperation.apply(example); @@ -174,10 +215,24 @@ private Page readPage(Pageable pageable) { pagedQuery.setMaxResults(pageable.getPageSize()); } - List paginatedResults = pagedQuery.getResultStream() // - .map(getConversionFunction(this.example.getProbeType(), this.resultType)) // - .collect(Collectors.toList()); + List paginatedResults = convert(pagedQuery.getResultList()); return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.example)); } + + private List convert(List resultList) { + + Function conversionFunction = getConversionFunction(); + List mapped = new ArrayList<>(resultList.size()); + + for (S s : resultList) { + mapped.add(conversionFunction.apply(s)); + } + return mapped; + } + + private Function getConversionFunction() { + return getConversionFunction(this.example.getProbeType(), this.resultType); + } + } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index ba2b0790fc..a5890f568f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.repository.support; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.springframework.dao.IncorrectResultSizeDataAccessException; @@ -33,6 +33,7 @@ import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import com.querydsl.core.types.Predicate; import com.querydsl.jpa.JPQLQuery; @@ -44,6 +45,7 @@ * @param Domain type * @param Result type * @author Greg Turnquist + * @author Mark Paluch * @since 2.6 */ class FetchableFluentQueryByPredicate extends FluentQuerySupport implements FetchableFluentQuery { @@ -78,16 +80,27 @@ private FetchableFluentQueryByPredicate(Predicate predicate, Class resultType this.entityType = entityType; } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort) + */ @Override public FetchableFluentQuery sortBy(Sort sort) { + Assert.notNull(sort, "Sort must not be null!"); + return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort.and(sort), this.properties, this.finder, this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) + */ @Override public FetchableFluentQuery as(Class resultType) { + Assert.notNull(resultType, "Projection target type must not be null!"); if (!resultType.isInterface()) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); } @@ -96,6 +109,10 @@ public FetchableFluentQuery as(Class resultType) { this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection) + */ @Override public FetchableFluentQuery project(Collection properties) { @@ -104,57 +121,83 @@ public FetchableFluentQuery project(Collection properties) { this.entityType, this.context); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() + */ @Override public R oneValue() { - List results = this.finder.apply(this.sort) // + List results = this.finder.apply(this.sort) // .limit(2) // Never need more than 2 values - .stream() // - .map(getConversionFunction(this.entityType, this.resultType)) // - .collect(Collectors.toList()); + .fetch(); if (results.size() > 1) { throw new IncorrectResultSizeDataAccessException(1); } - return results.isEmpty() ? null : results.get(0); + return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue() + */ @Override public R firstValue() { - List results = this.finder.apply(this.sort) // + List results = this.finder.apply(this.sort) // .limit(1) // Never need more than 1 value - .stream() // - .map(getConversionFunction(this.entityType, this.resultType)) // - .collect(Collectors.toList()); + .fetch(); - return results.isEmpty() ? null : results.get(0); + return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() + */ @Override public List all() { - return stream().collect(Collectors.toList()); + + JPQLQuery query = this.finder.apply(this.sort); + return convert(query.fetch()); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) + */ @Override public Page page(Pageable pageable) { return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() + */ @Override public Stream stream() { return this.finder.apply(this.sort) // .stream() // - .map(getConversionFunction(this.entityType, this.resultType)); + .map(getConversionFunction()); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() + */ @Override public long count() { return this.countOperation.apply(this.predicate); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() + */ @Override public boolean exists() { return this.existsOperation.apply(this.predicate); @@ -163,11 +206,25 @@ public boolean exists() { private Page readPage(Pageable pageable) { JPQLQuery pagedQuery = this.pagedFinder.apply(this.sort, pageable); - - List paginatedResults = pagedQuery.stream() // - .map(getConversionFunction(this.entityType, this.resultType)) // - .collect(Collectors.toList()); + List paginatedResults = convert(pagedQuery.fetch()); return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.predicate)); } + + private List convert(List resultList) { + + Function conversionFunction = getConversionFunction(); + List mapped = new ArrayList<>(resultList.size()); + + for (S s : resultList) { + mapped.add(conversionFunction.apply(s)); + } + + return mapped; + } + + private Function getConversionFunction() { + return getConversionFunction(this.entityType, this.resultType); + } + } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 32676777c7..19ecfcf79a 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -34,7 +34,7 @@ import org.springframework.data.querydsl.QSort; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; -import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.data.support.PageableExecutionUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -167,11 +167,16 @@ public Page findAll(Predicate predicate, Pageable pageable) { return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount); } + /* + * (non-Javadoc) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function) + */ + @SuppressWarnings("unchecked") @Override public R findBy(Predicate predicate, Function, R> queryFunction) { Assert.notNull(predicate, "Predicate must not be null!"); - Assert.notNull(queryFunction, "Function must not be null!"); + Assert.notNull(queryFunction, "Query function must not be null!"); Function> finder = sort -> { JPQLQuery select = createQuery(predicate).select(path); @@ -194,12 +199,12 @@ public R findBy(Predicate predicate, Function fluentQuery = (FetchableFluentQuery) new FetchableFluentQueryByPredicate<>(predicate, + FetchableFluentQueryByPredicate fluentQuery = new FetchableFluentQueryByPredicate<>(predicate, entityInformation.getJavaType(), finder, pagedFinder, this::count, this::exists, this.entityInformation.getJavaType(), new JpaMetamodelMappingContext(Collections.singleton(this.entityManager.getMetamodel()))); - return queryFunction.apply(fluentQuery); + return queryFunction.apply((FetchableFluentQuery) fluentQuery); } /* diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index cc2b001a85..b69d43667f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -58,7 +58,7 @@ import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; -import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.data.support.PageableExecutionUtils; import org.springframework.data.util.ProxyUtils; import org.springframework.data.util.Streamable; import org.springframework.lang.Nullable; @@ -583,6 +583,9 @@ public Page findAll(Example example, Pageable pageable) { @Override public R findBy(Example example, Function, R> queryFunction) { + Assert.notNull(example, "Sample must not be null!"); + Assert.notNull(queryFunction, "Query function must not be null!"); + Function> finder = sort -> { ExampleSpecification spec = new ExampleSpecification<>(example, escapeCharacter); From 8780c967499950d2ca83971abc87892e9f052fef Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 11 Oct 2021 14:30:21 +0200 Subject: [PATCH 063/821] Upgrade to Maven Wrapper 3.8.3. See #2331 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 481b362fad..e4e722b003 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Fri Sep 10 15:37:56 CEST 2021 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip +#Mon Oct 11 14:30:21 CEST 2021 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip From 87da84b7e7ba7fcaaf8dfdc40c7d5d85315f0044 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 12 Oct 2021 16:08:43 +0200 Subject: [PATCH 064/821] Use `@IdClass` as identifier for IdClasses with a single attribute for repository operations. We now use the IdClass type for lookups when the entity defines a `@IdClass`. Previously, we uses the type of the defined singular identifier attribute which lead to invalid queries. Closes #2330 --- .../jpa/provider/PersistenceProvider.java | 29 +++++++ .../JpaMetamodelEntityInformation.java | 24 ++++-- .../sample/PersistableWithSingleIdClass.java | 50 ++++++++++++ .../PersistableWithSingleIdClassPK.java | 76 +++++++++++++++++++ ...odelEntityInformationIntegrationTests.java | 12 +++ src/test/resources/META-INF/persistence.xml | 6 +- 6 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java create mode 100644 src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java diff --git a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 80094aa87a..4f9323c7df 100644 --- a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -20,15 +20,19 @@ import java.util.Collections; import java.util.NoSuchElementException; +import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.Query; +import javax.persistence.metamodel.IdentifiableType; import javax.persistence.metamodel.Metamodel; +import javax.persistence.metamodel.SingularAttribute; import org.eclipse.persistence.jpa.JpaQuery; import org.eclipse.persistence.queries.ScrollableCursor; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; +import org.hibernate.metamodel.model.domain.spi.IdentifiableTypeDescriptor; import org.hibernate.proxy.HibernateProxy; import org.springframework.data.util.CloseableIterator; import org.springframework.lang.Nullable; @@ -93,6 +97,17 @@ public Object getIdentifierFrom(Object entity) { return ((HibernateProxy) entity).getHibernateLazyInitializer().getIdentifier(); } + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.provider.PersistenceProvider#getIdClassAttributes(javax.persistence.metamodel.IdentifiableType) + */ + @Override + public Set> getIdClassAttributes(IdentifiableType type) { + return type instanceof IdentifiableTypeDescriptor && ((IdentifiableTypeDescriptor) type).hasIdClass() + ? super.getIdClassAttributes(type) + : Collections.emptySet(); + } + /* * (non-Javadoc) * @see org.springframework.data.jpa.provider.PersistenceProvider#executeQueryWithResultStream(javax.persistence.Query) @@ -291,6 +306,20 @@ public boolean canExtractQuery() { return true; } + /** + * @param type the entity type. + * @return the set of identifier attributes used in a {@code @IdClass} for {@code type}. Empty when {@code type} does + * not use {@code @IdClass}. + * @since 2.5.6 + */ + public Set> getIdClassAttributes(IdentifiableType type) { + try { + return type.getIdClassAttributes(); + } catch (IllegalArgumentException e) { + return Collections.emptySet(); + } + } + /** * Holds the PersistenceProvider specific interface names. * diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index e2481270bc..fe344c8fd5 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -86,7 +86,7 @@ public JpaMetamodelEntityInformation(Class domainClass, Metamodel metamodel) IdentifiableType identifiableType = (IdentifiableType) type; - this.idMetadata = new IdMetadata<>(identifiableType); + this.idMetadata = new IdMetadata<>(identifiableType, PersistenceProvider.fromMetamodel(metamodel)); this.versionAttribute = findVersionAttribute(identifiableType, metamodel); } @@ -260,20 +260,22 @@ public boolean isNew(T entity) { private static class IdMetadata implements Iterable> { private final IdentifiableType type; + private final Set> idClassAttributes; private final Set> attributes; private @Nullable Class idType; @SuppressWarnings("unchecked") - IdMetadata(IdentifiableType source) { + IdMetadata(IdentifiableType source, PersistenceProvider persistenceProvider) { this.type = source; + this.idClassAttributes = persistenceProvider.getIdClassAttributes(source); this.attributes = (Set>) (source.hasSingleIdAttribute() ? Collections.singleton(source.getId(source.getIdType().getJavaType())) : source.getIdClassAttributes()); } boolean hasSimpleId() { - return attributes.size() == 1; + return idClassAttributes.isEmpty() && attributes.size() == 1; } public Class getType() { @@ -296,18 +298,26 @@ public Class getType() { private Class tryExtractIdTypeWithFallbackToIdTypeLookup() { try { + + Class idClassType = lookupIdClass(type); + if (idClassType != null) { + return idClassType; + } + Type idType = type.getIdType(); - return idType == null ? fallbackIdTypeLookup(type) : idType.getJavaType(); + return idType == null ? null : idType.getJavaType(); } catch (IllegalStateException e) { // see https://hibernate.onjira.com/browse/HHH-6951 - return fallbackIdTypeLookup(type); + return null; } } @Nullable - private static Class fallbackIdTypeLookup(IdentifiableType type) { + private static Class lookupIdClass(IdentifiableType type) { - IdClass annotation = AnnotationUtils.findAnnotation(type.getJavaType(), IdClass.class); + IdClass annotation = type.getJavaType() != null + ? AnnotationUtils.findAnnotation(type.getJavaType(), IdClass.class) + : null; return annotation == null ? null : annotation.value(); } diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java b/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java new file mode 100644 index 0000000000..c1531ce0bd --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java @@ -0,0 +1,50 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; + +/** + * Sample entity using {@link IdClass} annotation to demarcate ids. + * + * @author Mark Paluch + */ +@Entity +@IdClass(PersistableWithSingleIdClassPK.class) +public class PersistableWithSingleIdClass { + + private static final long serialVersionUID = 1L; + + @Id private Long first; + + protected PersistableWithSingleIdClass() { + + } + + public PersistableWithSingleIdClass(Long first) { + this.first = first; + } + + /** + * @return the first + */ + public Long getFirst() { + return first; + } + +} diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java b/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java new file mode 100644 index 0000000000..cedf4aa1ee --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java @@ -0,0 +1,76 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import static org.springframework.util.ObjectUtils.*; + +import java.io.Serializable; + +/** + * @author Mark Paluch + */ +public class PersistableWithSingleIdClassPK implements Serializable { + + private static final long serialVersionUID = 23126782341L; + + private Long first; + + public PersistableWithSingleIdClassPK() { + + } + + public PersistableWithSingleIdClassPK(Long first) { + this.first = first; + } + + public void setFirst(Long first) { + this.first = first; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (obj == null || !(obj.getClass().equals(getClass()))) { + return false; + } + + PersistableWithSingleIdClassPK that = (PersistableWithSingleIdClassPK) obj; + + return nullSafeEquals(this.first, that.first); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + + int result = 17; + + result += nullSafeHashCode(this.first); + + return result; + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java index 8596518d46..88dd673e3d 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java @@ -92,6 +92,18 @@ void returnsIdOfPersistableInstanceCorrectly() { assertThat(id).isEqualTo(new PersistableWithIdClassPK(2L, 4L)); } + @Test // GH-2330 + void returnsIdOfSingleAttributeIdClassCorrectly() { + + PersistableWithSingleIdClass entity = new PersistableWithSingleIdClass(2L); + + JpaEntityInformation information = getEntityInformation( + PersistableWithSingleIdClass.class, em); + Object id = information.getId(entity); + + assertThat(id).isEqualTo(new PersistableWithSingleIdClassPK(2L)); + } + @Test // DATAJPA-413 void returnsIdOfEntityWithIdClassCorrectly() { diff --git a/src/test/resources/META-INF/persistence.xml b/src/test/resources/META-INF/persistence.xml index 42fe37d433..d5fbd08bc8 100644 --- a/src/test/resources/META-INF/persistence.xml +++ b/src/test/resources/META-INF/persistence.xml @@ -32,6 +32,8 @@ org.springframework.data.jpa.domain.sample.Order org.springframework.data.jpa.domain.sample.Parent org.springframework.data.jpa.domain.sample.PersistableWithIdClass + org.springframework.data.jpa.domain.sample.PersistableWithSingleIdClass + org.springframework.data.jpa.domain.sample.PrimitiveVersionProperty org.springframework.data.jpa.domain.sample.Product org.springframework.data.jpa.domain.sample.Role @@ -89,9 +91,9 @@ - + - + org.hibernate.jpa.HibernatePersistenceProvider org.springframework.data.jpa.domain.sample.CustomAbstractPersistable From 711482e71570a3d368f4cd68a79db491e22231b2 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 13 Oct 2021 12:05:49 +0200 Subject: [PATCH 065/821] Consider named count query for named queries. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now consider `@Query(countName = "…")` when the actual query is a string query or named query using Properties to declare its query. Named queries using JPA named queries remain unchanged. Closes #2217 --- .../query/AbstractStringBasedJpaQuery.java | 5 +- .../jpa/repository/query/JpaQueryFactory.java | 27 +++------- .../query/JpaQueryLookupStrategy.java | 47 ++++++++++++++---- .../data/jpa/repository/query/NamedQuery.java | 4 +- .../jpa/repository/query/NativeJpaQuery.java | 6 ++- .../jpa/repository/query/SimpleJpaQuery.java | 11 +++-- ...ctStringBasedJpaQueryIntegrationTests.java | 2 +- .../JpaQueryLookupStrategyUnitTests.java | 49 +++++++++++++++++++ .../query/SimpleJpaQueryUnitTests.java | 15 +++--- 9 files changed, 119 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 9496900a38..d8b19a925f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -22,6 +22,7 @@ import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -49,10 +50,12 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { * @param method must not be {@literal null}. * @param em must not be {@literal null}. * @param queryString must not be {@literal null}. + * @param countQueryString must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}. * @param parser must not be {@literal null}. */ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, + @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { super(method, em); @@ -64,7 +67,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri this.evaluationContextProvider = evaluationContextProvider; this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser); - DeclaredQuery countQuery = query.deriveCountQuery(method.getCountQuery(), method.getCountQueryProjection()); + DeclaredQuery countQuery = query.deriveCountQuery(countQueryString, method.getCountQueryProjection()); this.countQuery = ExpressionBasedStringQuery.from(countQuery, method.getEntityInformation(), parser); this.parser = parser; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 79a4366f8d..16e60825f6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -19,8 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.QueryMethod; + import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -39,42 +38,28 @@ enum JpaQueryFactory { private static final SpelExpressionParser PARSER = new SpelExpressionParser(); private static final Logger LOG = LoggerFactory.getLogger(JpaQueryFactory.class); - /** - * Creates a {@link RepositoryQuery} from the given {@link QueryMethod} that is potentially annotated with - * {@link Query}. - * - * @param method must not be {@literal null}. - * @param em must not be {@literal null}. - * @param evaluationContextProvider - * @return the {@link RepositoryQuery} derived from the annotation or {@code null} if no annotation found. - */ - @Nullable - AbstractJpaQuery fromQueryAnnotation(JpaQueryMethod method, EntityManager em, - QueryMethodEvaluationContextProvider evaluationContextProvider) { - - LOG.debug("Looking up query for method {}", method.getName()); - return fromMethodWithQueryString(method, em, method.getAnnotatedQuery(), evaluationContextProvider); - } - /** * Creates a {@link RepositoryQuery} from the given {@link String} query. * * @param method must not be {@literal null}. * @param em must not be {@literal null}. * @param queryString must not be {@literal null} or empty. + * @param countQueryString * @param evaluationContextProvider * @return */ @Nullable AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, @Nullable String queryString, + @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider) { if (queryString == null) { return null; } - return method.isNativeQuery() ? new NativeJpaQuery(method, em, queryString, evaluationContextProvider, PARSER) - : new SimpleJpaQuery(method, em, queryString, evaluationContextProvider, PARSER); + return method.isNativeQuery() + ? new NativeJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER) + : new SimpleJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER); } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 89d592420d..86288ad9c9 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -32,6 +32,7 @@ import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Query lookup strategy to execute finders. @@ -148,18 +149,20 @@ public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory query @Override protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { - RepositoryQuery query = JpaQueryFactory.INSTANCE.fromQueryAnnotation(method, em, evaluationContextProvider); + String countQuery = getCountQuery(method, namedQueries, em); - if (query != null && method.hasAnnotatedQueryName()) { - LOG.warn(String.format( - "Query method %s is annotated with both, a query and a query name. Using the declared query.", method)); - } + if (StringUtils.hasText(method.getAnnotatedQuery())) { - if (null != query) { - return query; + if (method.hasAnnotatedQueryName()) { + LOG.warn(String.format( + "Query method %s is annotated with both, a query and a query name. Using the declared query.", method)); + } + + return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getAnnotatedQuery(), countQuery, + evaluationContextProvider); } - query = JpaQueryFactory.INSTANCE.fromProcedureAnnotation(method, em); + RepositoryQuery query = JpaQueryFactory.INSTANCE.fromProcedureAnnotation(method, em); if (null != query) { return query; @@ -167,7 +170,7 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, String name = method.getNamedQueryName(); if (namedQueries.hasQuery(name)) { - return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), + return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), countQuery, evaluationContextProvider); } @@ -180,6 +183,32 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, throw new IllegalStateException( String.format("Did neither find a NamedQuery nor an annotated query for method %s!", method)); } + + @Nullable + private String getCountQuery(JpaQueryMethod method, NamedQueries namedQueries, EntityManager em) { + + if (StringUtils.hasText(method.getCountQuery())) { + return method.getCountQuery(); + } + + String queryName = method.getNamedCountQueryName(); + + if (!StringUtils.hasText(queryName)) { + return method.getCountQuery(); + } + + if (namedQueries.hasQuery(queryName)) { + return namedQueries.getQuery(queryName); + } + + boolean namedQuery = NamedQuery.hasNamedQuery(em, queryName); + + if (namedQuery) { + return method.getQueryExtractor().extractQueryString(em.createNamedQuery(queryName)); + } + + return null; + } } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 5ee7c1448d..b6d473a116 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -15,8 +15,6 @@ */ package org.springframework.data.jpa.repository.query; -import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.*; - import javax.persistence.EntityManager; import javax.persistence.Query; import javax.persistence.Tuple; @@ -105,7 +103,7 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { * @param queryName must not be {@literal null}. * @return */ - private static boolean hasNamedQuery(EntityManager em, String queryName) { + static boolean hasNamedQuery(EntityManager em, String queryName) { /* * See DATAJPA-617, we have to use a dedicated em for the lookups to avoid a diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index da0d718976..b31753f2a7 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -34,6 +34,7 @@ * @author Thomas Darimont * @author Oliver Gierke * @author Jens Schauder + * @author Mark Paluch */ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { @@ -43,12 +44,13 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { * @param method must not be {@literal null}. * @param em must not be {@literal null}. * @param queryString must not be {@literal null} or empty. + * @param countQueryString must not be {@literal null} or empty. * @param evaluationContextProvider */ - public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, + public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { - super(method, em, queryString, evaluationContextProvider, parser); + super(method, em, queryString, countQueryString, evaluationContextProvider, parser); Parameters parameters = method.getParameters(); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index 93a9bf4050..9fcff18664 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -21,6 +21,7 @@ import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; /** * {@link RepositoryQuery} implementation that inspects a {@link org.springframework.data.repository.query.QueryMethod} @@ -38,12 +39,13 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { * * @param method must not be {@literal null} * @param em must not be {@literal null} + * @param countQueryString * @param evaluationContextProvider must not be {@literal null} * @param parser must not be {@literal null} */ - public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, + public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { - this(method, em, method.getRequiredAnnotatedQuery(), evaluationContextProvider, parser); + this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, evaluationContextProvider, parser); } /** @@ -52,13 +54,14 @@ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, * @param method must not be {@literal null} * @param em must not be {@literal null} * @param queryString must not be {@literal null} or empty + * @param countQueryString * @param evaluationContextProvider must not be {@literal null} * @param parser must not be {@literal null} */ - public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, + public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { - super(method, em, queryString, evaluationContextProvider, parser); + super(method, em, queryString, countQueryString, evaluationContextProvider, parser); validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s!", method); diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index 8e4864a6cb..55feb14707 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -61,7 +61,7 @@ void createsNormalQueryForJpaManagedReturnTypes() throws Exception { JpaQueryMethod method = getMethod("findRolesByEmailAddress", String.class); AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, - QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser()); + null, QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser()); jpaQuery.createJpaQuery(method.getAnnotatedQuery(), method.getResultProcessor().getReturnedType()); diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index c5b8f7bacd..79699c4207 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -34,6 +34,8 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.QueryExtractor; @@ -111,6 +113,47 @@ void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() thro .withMessageContaining(method.toString()); } + @Test // GH-2217 + void considersNamedCountQuery() throws Exception { + + QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, + EVALUATION_CONTEXT_PROVIDER, EscapeCharacter.DEFAULT); + + when(namedQueries.hasQuery("foo.count")).thenReturn(true); + when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); + + when(namedQueries.hasQuery("User.findByNamedQuery")).thenReturn(true); + when(namedQueries.getQuery("User.findByNamedQuery")).thenReturn("select foo"); + + Method method = UserRepository.class.getMethod("findByNamedQuery", String.class, Pageable.class); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); + + RepositoryQuery repositoryQuery = strategy.resolveQuery(method, metadata, projectionFactory, namedQueries); + assertThat(repositoryQuery).isInstanceOf(SimpleJpaQuery.class); + SimpleJpaQuery query = (SimpleJpaQuery) repositoryQuery; + assertThat(query.getQuery().getQueryString()).isEqualTo("select foo"); + assertThat(query.getCountQuery().getQueryString()).isEqualTo("foo count"); + } + + @Test // GH-2217 + void considersNamedCountOnStringQueryQuery() throws Exception { + + QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, + EVALUATION_CONTEXT_PROVIDER, EscapeCharacter.DEFAULT); + + when(namedQueries.hasQuery("foo.count")).thenReturn(true); + when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); + + Method method = UserRepository.class.getMethod("findByStringQueryWithNamedCountQuery", String.class, + Pageable.class); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); + + RepositoryQuery repositoryQuery = strategy.resolveQuery(method, metadata, projectionFactory, namedQueries); + assertThat(repositoryQuery).isInstanceOf(SimpleJpaQuery.class); + SimpleJpaQuery query = (SimpleJpaQuery) repositoryQuery; + assertThat(query.getCountQuery().getQueryString()).isEqualTo("foo count"); + } + @Test // GH-2319 void prefersDeclaredQuery() throws Exception { @@ -132,6 +175,12 @@ interface UserRepository extends Repository { @Query(value = "select u.* from User u", nativeQuery = true) List findByInvalidNativeQuery(String param, Sort sort); + @Query(countName = "foo.count") + Page findByNamedQuery(String foo, Pageable pageable); + + @Query(value = "foo.query", countName = "foo.count") + Page findByStringQueryWithNamedCountQuery(String foo, Pageable pageable); + @Query(value = "something absurd", name = "my-query-name") User annotatedQueryWithQueryAndQueryName(); } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index e8212840d2..71339337e6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -111,7 +111,7 @@ void prefersDeclaredCountQueryOverCreatingOne() throws Exception { metadata, factory, extractor); when(em.createQuery("foo", Long.class)).thenReturn(typedQuery); - SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", EVALUATION_CONTEXT_PROVIDER, + SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", null, EVALUATION_CONTEXT_PROVIDER, PARSER); assertThat(jpaQuery.createCountQuery(new JpaParametersParameterAccessor(method.getParameters(), new Object[] {}))) @@ -126,7 +126,8 @@ void doesNotApplyPaginationToCountQuery() throws Exception { Method method = UserRepository.class.getMethod("findAllPaged", Pageable.class); JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); - AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", EVALUATION_CONTEXT_PROVIDER, + AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", null, + EVALUATION_CONTEXT_PROVIDER, PARSER); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -141,8 +142,8 @@ void discoversNativeQuery() throws Exception { Method method = SampleRepository.class.getMethod("findNativeByLastname", String.class); JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); - AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromQueryAnnotation(queryMethod, em, - EVALUATION_CONTEXT_PROVIDER); + AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, + queryMethod.getAnnotatedQuery(), null, EVALUATION_CONTEXT_PROVIDER); assertThat(jpaQuery instanceof NativeJpaQuery).isTrue(); @@ -244,7 +245,8 @@ void resolvesExpressionInCountQuery() throws Exception { Method method = SampleRepository.class.getMethod("findAllWithExpressionInCountQuery", Pageable.class); JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); - AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", EVALUATION_CONTEXT_PROVIDER, + AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", + "select count(u.id) from #{#entityName} u", EVALUATION_CONTEXT_PROVIDER, PARSER); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -256,7 +258,8 @@ void resolvesExpressionInCountQuery() throws Exception { private AbstractJpaQuery createJpaQuery(Method method) { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); - return JpaQueryFactory.INSTANCE.fromQueryAnnotation(queryMethod, em, EVALUATION_CONTEXT_PROVIDER); + return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, queryMethod.getAnnotatedQuery(), null, + EVALUATION_CONTEXT_PROVIDER); } interface SampleRepository { From 1a00e2f588cce7c5be5302764b1052441b1f0d81 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 13 Oct 2021 12:08:35 +0200 Subject: [PATCH 066/821] Polishing. Make query factory methods non-nullable by moving conditionals to the lookup strategy. See #2217 --- .../jpa/repository/query/JpaQueryFactory.java | 17 +---------------- .../query/JpaQueryLookupStrategy.java | 17 +++++++---------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 16e60825f6..00232af63f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -17,9 +17,6 @@ import javax.persistence.EntityManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -36,7 +33,6 @@ enum JpaQueryFactory { INSTANCE; private static final SpelExpressionParser PARSER = new SpelExpressionParser(); - private static final Logger LOG = LoggerFactory.getLogger(JpaQueryFactory.class); /** * Creates a {@link RepositoryQuery} from the given {@link String} query. @@ -48,15 +44,10 @@ enum JpaQueryFactory { * @param evaluationContextProvider * @return */ - @Nullable - AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, @Nullable String queryString, + AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider) { - if (queryString == null) { - return null; - } - return method.isNativeQuery() ? new NativeJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER) : new SimpleJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER); @@ -69,13 +60,7 @@ AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager * @param em must not be {@literal null}. * @return */ - @Nullable public StoredProcedureJpaQuery fromProcedureAnnotation(JpaQueryMethod method, EntityManager em) { - - if (!method.isProcedureQuery()) { - return null; - } - return new StoredProcedureJpaQuery(method, em); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 86288ad9c9..9c3db738a8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -149,7 +149,9 @@ public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory query @Override protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { - String countQuery = getCountQuery(method, namedQueries, em); + if (method.isProcedureQuery()) { + return JpaQueryFactory.INSTANCE.fromProcedureAnnotation(method, em); + } if (StringUtils.hasText(method.getAnnotatedQuery())) { @@ -158,23 +160,18 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, "Query method %s is annotated with both, a query and a query name. Using the declared query.", method)); } - return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getAnnotatedQuery(), countQuery, + return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getRequiredAnnotatedQuery(), + getCountQuery(method, namedQueries, em), evaluationContextProvider); } - RepositoryQuery query = JpaQueryFactory.INSTANCE.fromProcedureAnnotation(method, em); - - if (null != query) { - return query; - } - String name = method.getNamedQueryName(); if (namedQueries.hasQuery(name)) { - return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), countQuery, + return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), getCountQuery(method, namedQueries, em), evaluationContextProvider); } - query = NamedQuery.lookupFrom(method, em); + RepositoryQuery query = NamedQuery.lookupFrom(method, em); if (null != query) { return query; From a31052c3f0e9e36f298d382a4023d0914e8fbdde Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 14 Oct 2021 11:51:19 +0200 Subject: [PATCH 067/821] Upgrade to Hibernate 5.6. Fixes #2335. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e31849e911..27f5fb3f0e 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ DATAJPA 2.7.9 - 5.5.3.Final + 5.6.0.Final 8.0.23 42.2.19 2.6.0-SNAPSHOT From 9f827fa1ba0fe73d03d6092eb4b8e8b6c4a5e07e Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 18 Oct 2021 11:30:24 +0200 Subject: [PATCH 068/821] Add a note about the new getById method. See #2169 --- src/main/asciidoc/new-features.adoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index 952975692b..1c71bcf5d4 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -1,6 +1,14 @@ [[new-features]] = New & Noteworthy +[[new-features.2-5-0]] +== What's New in Spring Data JPA 2.5 + +There is a new `getById` method in the `JpaRepository` which will replace `getOne`, which is now deprecated. +Since this method returns a reference this changes the behaviour of an existing `getById` method which before was implemented by query derivation. +This in turn might lead to an unexpected `LazyLoadingException` when accessing attributes of that reference outside a transaction. +To avoid this please rename your existing `getById` method to `getXyzById` with `Xyz` being an arbitrary string. + [[new-features.1-11-0]] == What's New in Spring Data JPA 1.11 From 69f7fc1364127cbd167cad38ff7a7a3d0bf0b972 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 18 Oct 2021 13:47:46 +0200 Subject: [PATCH 069/821] Prepare 2.6 RC1 (2021.1.0). See #2302 --- pom.xml | 8 ++++---- src/main/resources/notice.txt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 27f5fb3f0e..c62f0bf1a8 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-SNAPSHOT + 2.6.0-RC1 @@ -25,7 +25,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.6.0-SNAPSHOT + 2.6.0-RC1 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 6e78db0fd5..2132e61574 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data JPA 2.6 M3 (2021.1.0) +Spring Data JPA 2.6 RC1 (2021.1.0) Copyright (c) [2011-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -28,5 +28,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From 36f06647292e94b33791d1d2bd29c043537b1d22 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 18 Oct 2021 13:48:13 +0200 Subject: [PATCH 070/821] Release version 2.6 RC1 (2021.1.0). See #2302 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c62f0bf1a8..01277763c1 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-SNAPSHOT + 2.6.0-RC1 Spring Data JPA Spring Data module for JPA repositories. From 451d81aebe21491a0cf591c267791673f60a27ef Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 18 Oct 2021 13:55:38 +0200 Subject: [PATCH 071/821] Prepare next development iteration. See #2302 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01277763c1..c62f0bf1a8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-RC1 + 2.6.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. From ee9ef8665ed1740858b6fa9621d7d16ba86b9b9f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 18 Oct 2021 13:55:41 +0200 Subject: [PATCH 072/821] After release cleanups. See #2302 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c62f0bf1a8..27f5fb3f0e 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-RC1 + 2.6.0-SNAPSHOT @@ -25,7 +25,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.6.0-RC1 + 2.6.0-SNAPSHOT 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 43305f67cb22ed3af1a60a0b13d22f10f7bb9096 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 25 Oct 2021 08:34:41 +0200 Subject: [PATCH 073/821] Polishing. Comments and formatting. --- .../jpa/repository/support/FluentQuerySupport.java | 1 + .../support/QuerydslJpaPredicateExecutor.java | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java index d8cf794577..0bd878d024 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -40,6 +40,7 @@ abstract class FluentQuerySupport { protected final Class resultType; protected final Sort sort; + /** Properties on which the query projects. {@literal null} stands for no special projection. */ protected final @Nullable Set properties; protected final MappingContext, ? extends PersistentProperty> context; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 19ecfcf79a..7f32ced6e4 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -199,10 +199,16 @@ public R findBy(Predicate predicate, Function fluentQuery = new FetchableFluentQueryByPredicate<>(predicate, - entityInformation.getJavaType(), finder, pagedFinder, this::count, this::exists, - this.entityInformation.getJavaType(), - new JpaMetamodelMappingContext(Collections.singleton(this.entityManager.getMetamodel()))); + FetchableFluentQueryByPredicate fluentQuery = new FetchableFluentQueryByPredicate<>( // + predicate, // + entityInformation.getJavaType(), // + finder, // + pagedFinder, // + this::count, // + this::exists, // + this.entityInformation.getJavaType(), // + new JpaMetamodelMappingContext(Collections.singleton(this.entityManager.getMetamodel())) // + ); return queryFunction.apply((FetchableFluentQuery) fluentQuery); } From 83ba970465a8f6909e0368c94da6bba0c94e616c Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 25 Oct 2021 14:55:20 +0200 Subject: [PATCH 074/821] Translate projected properties of the fluent query API into a fetchgraph. When a property path based projection is specified we still return the root entity. But we do provide a fetchgraph. The JPA implementation will (should) load only the specified attributes eagerly. It most likely will also load all other attributes from all selected tables. Once we have infrastructure in place for for multilevel projections the same approach can and should be used for those. Currently this is not the case. Closes #2329 Original pull request: #2345. --- .../FetchableFluentQueryByExample.java | 63 +++++++----- .../FetchableFluentQueryByPredicate.java | 90 +++++++++-------- .../support/FluentQuerySupport.java | 17 ++-- .../jpa/repository/support/Projector.java | 72 ++++++++++++++ .../support/QuerydslJpaPredicateExecutor.java | 22 ++--- .../repository/support/QuerydslProjector.java | 39 ++++++++ .../support/TypedQueryProjector.java | 37 +++++++ .../jpa/repository/UserRepositoryTests.java | 79 +++++++++++++++ ...QuerydslJpaPredicateExecutorUnitTests.java | 82 +++++++++++++++- .../support/QuerydslProjectorUnitTests.java | 98 +++++++++++++++++++ 10 files changed, 513 insertions(+), 86 deletions(-) create mode 100644 src/main/java/org/springframework/data/jpa/repository/support/Projector.java create mode 100644 src/main/java/org/springframework/data/jpa/repository/support/QuerydslProjector.java create mode 100644 src/main/java/org/springframework/data/jpa/repository/support/TypedQueryProjector.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/support/QuerydslProjectorUnitTests.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index c187ccd884..7e395ca15e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.stream.Stream; @@ -36,7 +37,6 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -47,9 +47,10 @@ * @param Result type * @author Greg Turnquist * @author Mark Paluch + * @author Jens Schauder * @since 2.6 */ -class FetchableFluentQueryByExample extends FluentQuerySupport implements FetchableFluentQuery { +class FetchableFluentQueryByExample extends FluentQuerySupport implements FetchableFluentQuery { private final Example example; private final Function> finder; @@ -57,28 +58,31 @@ class FetchableFluentQueryByExample extends FluentQuerySupport implemen private final Function, Boolean> existsOperation; private final EntityManager entityManager; private final EscapeCharacter escapeCharacter; + private final Projector> projector; public FetchableFluentQueryByExample(Example example, Function> finder, Function, Long> countOperation, Function, Boolean> existsOperation, MappingContext, ? extends PersistentProperty> context, EntityManager entityManager, EscapeCharacter escapeCharacter) { - this(example, (Class) example.getProbeType(), Sort.unsorted(), null, finder, countOperation, existsOperation, - context, entityManager, escapeCharacter); + this(example, example.getProbeType(), (Class) example.getProbeType(), Sort.unsorted(), Collections.emptySet(), + finder, countOperation, existsOperation, context, entityManager, escapeCharacter, + new TypedQueryProjector(entityManager)); } - private FetchableFluentQueryByExample(Example example, Class returnType, Sort sort, - @Nullable Collection properties, Function> finder, - Function, Long> countOperation, Function, Boolean> existsOperation, + private FetchableFluentQueryByExample(Example example, Class entityType, Class returnType, Sort sort, + Collection properties, Function> finder, Function, Long> countOperation, + Function, Boolean> existsOperation, MappingContext, ? extends PersistentProperty> context, - EntityManager entityManager, EscapeCharacter escapeCharacter) { + EntityManager entityManager, EscapeCharacter escapeCharacter, Projector> projector) { - super(returnType, sort, properties, context); + super(returnType, sort, properties, context, entityType); this.example = example; this.finder = finder; this.countOperation = countOperation; this.existsOperation = existsOperation; this.entityManager = entityManager; this.escapeCharacter = escapeCharacter; + this.projector = projector; } /* @@ -90,8 +94,9 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null!"); - return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort.and(sort), this.properties, - this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); + return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort.and(sort), properties, finder, + countOperation, existsOperation, context, entityManager, escapeCharacter, + new TypedQueryProjector(entityManager)); } /* @@ -106,8 +111,9 @@ public FetchableFluentQuery as(Class resultType) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); } - return new FetchableFluentQueryByExample<>(this.example, resultType, this.sort, this.properties, this.finder, - this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); + return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, properties, finder, + countOperation, existsOperation, context, entityManager, escapeCharacter, + new TypedQueryProjector(entityManager)); } /* @@ -117,8 +123,9 @@ public FetchableFluentQuery as(Class resultType) { @Override public FetchableFluentQuery project(Collection properties) { - return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort, mergeProperties(properties), - this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter); + return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, mergeProperties(properties), + finder, countOperation, existsOperation, context, entityManager, escapeCharacter, + new TypedQueryProjector(entityManager)); } /* @@ -128,7 +135,7 @@ public FetchableFluentQuery project(Collection properties) { @Override public R oneValue() { - TypedQuery limitedQuery = this.finder.apply(this.sort); + TypedQuery limitedQuery = createSortedAndProjectedQuery(); limitedQuery.setMaxResults(2); // Never need more than 2 values List results = limitedQuery.getResultList(); @@ -147,7 +154,7 @@ public R oneValue() { @Override public R firstValue() { - TypedQuery limitedQuery = this.finder.apply(this.sort); + TypedQuery limitedQuery = createSortedAndProjectedQuery(); limitedQuery.setMaxResults(1); // Never need more than 1 value List results = limitedQuery.getResultList(); @@ -162,7 +169,7 @@ public R firstValue() { @Override public List all() { - List resultList = this.finder.apply(this.sort).getResultList(); + List resultList = createSortedAndProjectedQuery().getResultList(); return convert(resultList); } @@ -183,7 +190,7 @@ public Page page(Pageable pageable) { @Override public Stream stream() { - return this.finder.apply(this.sort) // + return createSortedAndProjectedQuery() // .getResultStream() // .map(getConversionFunction()); } @@ -194,7 +201,7 @@ public Stream stream() { */ @Override public long count() { - return this.countOperation.apply(example); + return countOperation.apply(example); } /* @@ -203,12 +210,12 @@ public long count() { */ @Override public boolean exists() { - return this.existsOperation.apply(example); + return existsOperation.apply(example); } private Page readPage(Pageable pageable) { - TypedQuery pagedQuery = this.finder.apply(this.sort); + TypedQuery pagedQuery = createSortedAndProjectedQuery(); if (pageable.isPaged()) { pagedQuery.setFirstResult((int) pageable.getOffset()); @@ -217,7 +224,15 @@ private Page readPage(Pageable pageable) { List paginatedResults = convert(pagedQuery.getResultList()); - return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.example)); + return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(example)); + } + + private TypedQuery createSortedAndProjectedQuery() { + + TypedQuery query = finder.apply(sort); + projector.apply(entityType, query, properties); + + return query; } private List convert(List resultList) { @@ -232,7 +247,7 @@ private List convert(List resultList) { } private Function getConversionFunction() { - return getConversionFunction(this.example.getProbeType(), this.resultType); + return getConversionFunction(example.getProbeType(), resultType); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index a5890f568f..ff9c67f83e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; @@ -32,11 +33,10 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.querydsl.core.types.Predicate; -import com.querydsl.jpa.JPQLQuery; +import com.querydsl.jpa.impl.AbstractJPAQuery; /** * Immutable implementation of {@link FetchableFluentQuery} based on a Querydsl {@link Predicate}. All methods that @@ -46,38 +46,41 @@ * @param Result type * @author Greg Turnquist * @author Mark Paluch + * @author Jens Schauder * @since 2.6 */ -class FetchableFluentQueryByPredicate extends FluentQuerySupport implements FetchableFluentQuery { +class FetchableFluentQueryByPredicate extends FluentQuerySupport implements FetchableFluentQuery { private final Predicate predicate; - private final Function> finder; - private final BiFunction> pagedFinder; + private final Function> finder; + private final BiFunction> pagedFinder; private final Function countOperation; private final Function existsOperation; - private final Class entityType; - - public FetchableFluentQueryByPredicate(Predicate predicate, Class resultType, Function> finder, - BiFunction> pagedFinder, Function countOperation, - Function existsOperation, Class entityType, - MappingContext, ? extends PersistentProperty> context) { - this(predicate, resultType, Sort.unsorted(), null, finder, pagedFinder, countOperation, existsOperation, entityType, - context); + private final Projector> projector; + + public FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, + Function> finder, BiFunction> pagedFinder, + Function countOperation, Function existsOperation, + MappingContext, ? extends PersistentProperty> context, + Projector> projector) { + this(predicate, entityType, (Class) entityType, Sort.unsorted(), Collections.emptySet(), finder, pagedFinder, + countOperation, existsOperation, context, projector); } - private FetchableFluentQueryByPredicate(Predicate predicate, Class resultType, Sort sort, - @Nullable Collection properties, Function> finder, - BiFunction> pagedFinder, Function countOperation, - Function existsOperation, Class entityType, - MappingContext, ? extends PersistentProperty> context) { + private FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, Class resultType, Sort sort, + Collection properties, Function> finder, + BiFunction> pagedFinder, Function countOperation, + Function existsOperation, + MappingContext, ? extends PersistentProperty> context, + Projector> projector) { - super(resultType, sort, properties, context); + super(resultType, sort, properties, context, entityType); this.predicate = predicate; this.finder = finder; this.pagedFinder = pagedFinder; this.countOperation = countOperation; this.existsOperation = existsOperation; - this.entityType = entityType; + this.projector = projector; } /* @@ -89,8 +92,8 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null!"); - return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort.and(sort), this.properties, - this.finder, this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context); + return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort.and(sort), properties, finder, + pagedFinder, countOperation, existsOperation, context, projector); } /* @@ -101,12 +104,13 @@ public FetchableFluentQuery sortBy(Sort sort) { public FetchableFluentQuery as(Class resultType) { Assert.notNull(resultType, "Projection target type must not be null!"); + if (!resultType.isInterface()) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); } - return new FetchableFluentQueryByPredicate<>(this.predicate, resultType, this.sort, this.properties, this.finder, - this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context); + return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, properties, finder, + pagedFinder, countOperation, existsOperation, context, projector); } /* @@ -116,9 +120,8 @@ public FetchableFluentQuery as(Class resultType) { @Override public FetchableFluentQuery project(Collection properties) { - return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort, - mergeProperties(properties), this.finder, this.pagedFinder, this.countOperation, this.existsOperation, - this.entityType, this.context); + return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, mergeProperties(properties), + finder, pagedFinder, countOperation, existsOperation, context, projector); } /* @@ -128,7 +131,7 @@ public FetchableFluentQuery project(Collection properties) { @Override public R oneValue() { - List results = this.finder.apply(this.sort) // + List results = createSortedAndProjectedQuery() // .limit(2) // Never need more than 2 values .fetch(); @@ -146,7 +149,7 @@ public R oneValue() { @Override public R firstValue() { - List results = this.finder.apply(this.sort) // + List results = createSortedAndProjectedQuery() // .limit(1) // Never need more than 1 value .fetch(); @@ -159,9 +162,7 @@ public R firstValue() { */ @Override public List all() { - - JPQLQuery query = this.finder.apply(this.sort); - return convert(query.fetch()); + return convert(createSortedAndProjectedQuery().fetch()); } /* @@ -180,7 +181,7 @@ public Page page(Pageable pageable) { @Override public Stream stream() { - return this.finder.apply(this.sort) // + return createSortedAndProjectedQuery() // .stream() // .map(getConversionFunction()); } @@ -191,7 +192,7 @@ public Stream stream() { */ @Override public long count() { - return this.countOperation.apply(this.predicate); + return countOperation.apply(predicate); } /* @@ -200,31 +201,38 @@ public long count() { */ @Override public boolean exists() { - return this.existsOperation.apply(this.predicate); + return existsOperation.apply(predicate); + } + + private AbstractJPAQuery createSortedAndProjectedQuery() { + + final AbstractJPAQuery query = finder.apply(sort); + projector.apply(entityType, query, properties); + return query; } private Page readPage(Pageable pageable) { - JPQLQuery pagedQuery = this.pagedFinder.apply(this.sort, pageable); + AbstractJPAQuery pagedQuery = pagedFinder.apply(sort, pageable); List paginatedResults = convert(pagedQuery.fetch()); - return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.predicate)); + return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(predicate)); } - private List convert(List resultList) { + private List convert(List resultList) { Function conversionFunction = getConversionFunction(); List mapped = new ArrayList<>(resultList.size()); - for (S s : resultList) { - mapped.add(conversionFunction.apply(s)); + for (Object o : resultList) { + mapped.add(conversionFunction.apply(o)); } return mapped; } private Function getConversionFunction() { - return getConversionFunction(this.entityType, this.resultType); + return getConversionFunction(entityType, resultType); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java index 0bd878d024..2f299cc9c1 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -34,20 +34,22 @@ * * @param The resulting type of the query. * @author Greg Turnquist + * @author Jens Schauder * @since 2.6 */ -abstract class FluentQuerySupport { +abstract class FluentQuerySupport { protected final Class resultType; protected final Sort sort; /** Properties on which the query projects. {@literal null} stands for no special projection. */ - protected final @Nullable Set properties; + protected final Set properties; protected final MappingContext, ? extends PersistentProperty> context; + protected final Class entityType; private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); FluentQuerySupport(Class resultType, Sort sort, @Nullable Collection properties, - MappingContext, ? extends PersistentProperty> context) { + MappingContext, ? extends PersistentProperty> context, Class entityType) { this.resultType = resultType; this.sort = sort; @@ -55,24 +57,23 @@ abstract class FluentQuerySupport { if (properties != null) { this.properties = new HashSet<>(properties); } else { - this.properties = null; + this.properties = new HashSet<>(); } this.context = context; + this.entityType = entityType; } final Collection mergeProperties(Collection additionalProperties) { Set newProperties = new HashSet<>(); - if (this.properties != null) { - newProperties.addAll(this.properties); - } + newProperties.addAll(properties); newProperties.addAll(additionalProperties); return Collections.unmodifiableCollection(newProperties); } @SuppressWarnings("unchecked") - final Function getConversionFunction(Class inputType, Class targetType) { + final Function getConversionFunction(Class inputType, Class targetType) { if (targetType.isAssignableFrom(inputType)) { return (Function) Function.identity(); diff --git a/src/main/java/org/springframework/data/jpa/repository/support/Projector.java b/src/main/java/org/springframework/data/jpa/repository/support/Projector.java new file mode 100644 index 0000000000..482d5f01cb --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/Projector.java @@ -0,0 +1,72 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import java.util.Set; + +import javax.persistence.EntityGraph; +import javax.persistence.EntityManager; +import javax.persistence.Subgraph; + +import org.springframework.data.mapping.PropertyPath; + +/** + * Turns a collection of property paths to an {@link EntityGraph} and applies it to a query abstraction + * + * @param the type of the query abstraction. + * @author Jens Schauder + * @since 2.6 + */ +abstract class Projector { + + private final EntityManager entityManager; + + protected Projector(EntityManager entityManager) { + this.entityManager = entityManager; + } + + public void apply(Class domainType, Q query, Set properties) { + + if (!properties.isEmpty()) { + + final javax.persistence.EntityGraph entityGraph = entityManager.createEntityGraph(domainType); + + for (String property : properties) { + + Subgraph subgraph = null; + + for (PropertyPath path : PropertyPath.from(property, domainType)) { + + if (path.hasNext()) { + subgraph = subgraph == null ? entityGraph.addSubgraph(path.getSegment()) + : subgraph.addSubgraph(path.getSegment()); + } else { + + if (subgraph == null) { + entityGraph.addAttributeNodes(path.getSegment()); + } else { + subgraph.addAttributeNodes(path.getSegment()); + } + } + } + } + + applyEntityGraph(query, entityGraph); + } + } + + abstract void applyEntityGraph(Q query, EntityGraph entityGraph); +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 7f32ced6e4..80ac351113 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -69,7 +69,7 @@ public class QuerydslJpaPredicateExecutor implements QuerydslPredicateExecuto /** * Creates a new {@link QuerydslJpaPredicateExecutor} from the given domain class and {@link EntityManager} and uses * the given {@link EntityPathResolver} to translate the domain class into an {@link EntityPath}. - * + * * @param entityInformation must not be {@literal null}. * @param entityManager must not be {@literal null}. * @param resolver must not be {@literal null}. @@ -178,22 +178,22 @@ public R findBy(Predicate predicate, Function> finder = sort -> { - JPQLQuery select = createQuery(predicate).select(path); + Function> finder = sort -> { + AbstractJPAQuery select = (AbstractJPAQuery) createQuery(predicate).select(path); if (sort != null) { - select = querydsl.applySorting(sort, select); + select = (AbstractJPAQuery) querydsl.applySorting(sort, select); } return select; }; - BiFunction> pagedFinder = (sort, pageable) -> { + BiFunction> pagedFinder = (sort, pageable) -> { - JPQLQuery select = finder.apply(sort); + AbstractJPAQuery select = finder.apply(sort); if (pageable.isPaged()) { - select = querydsl.applyPagination(pageable, select); + select = (AbstractJPAQuery) querydsl.applyPagination(pageable, select); } return select; @@ -201,13 +201,13 @@ public R findBy(Predicate predicate, Function fluentQuery = new FetchableFluentQueryByPredicate<>( // predicate, // - entityInformation.getJavaType(), // + this.entityInformation.getJavaType(), // finder, // pagedFinder, // this::count, // this::exists, // - this.entityInformation.getJavaType(), // - new JpaMetamodelMappingContext(Collections.singleton(this.entityManager.getMetamodel())) // + new JpaMetamodelMappingContext(Collections.singleton(this.entityManager.getMetamodel())), // + new QuerydslProjector(entityManager) // ); return queryFunction.apply((FetchableFluentQuery) fluentQuery); @@ -237,7 +237,7 @@ public boolean exists(Predicate predicate) { * @param predicate * @return the Querydsl {@link JPQLQuery}. */ - protected JPQLQuery createQuery(Predicate... predicate) { + protected AbstractJPAQuery createQuery(Predicate... predicate) { Assert.notNull(predicate, "Predicate must not be null!"); diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslProjector.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslProjector.java new file mode 100644 index 0000000000..22bd98bed7 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslProjector.java @@ -0,0 +1,39 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import javax.persistence.EntityGraph; +import javax.persistence.EntityManager; + +import com.querydsl.jpa.impl.AbstractJPAQuery; + +/** + * Applies fetchgraph hints to {@code AbstractJPAQuery}. + * + * @author Jens Schauder + * @since 2.6 + */ +class QuerydslProjector extends Projector> { + + QuerydslProjector(EntityManager entityManager) { + super(entityManager); + } + + @Override + void applyEntityGraph(AbstractJPAQuery query, EntityGraph entityGraph) { + query.setHint("javax.persistence.fetchgraph", entityGraph); + } +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/TypedQueryProjector.java b/src/main/java/org/springframework/data/jpa/repository/support/TypedQueryProjector.java new file mode 100644 index 0000000000..d3b15e5cf7 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/TypedQueryProjector.java @@ -0,0 +1,37 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import javax.persistence.EntityGraph; +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; + +/** + * Applies fetchgraph hints to {@code TypedQuery}. + * + * @author Jens Schauder + * @since 2.6 + */ +public class TypedQueryProjector extends Projector> { + + public TypedQueryProjector(EntityManager entityManager) { + super(entityManager); + } + + void applyEntityGraph(TypedQuery query, EntityGraph entityGraph) { + query.setHint("javax.persistence.fetchgraph", entityGraph); + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 29f03ef110..623bb41fa0 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -45,6 +45,7 @@ import javax.persistence.criteria.Root; import org.assertj.core.api.SoftAssertions; +import org.hibernate.LazyInitializationException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -2138,6 +2139,84 @@ void findByFluentExampleWithInterfaceBasedProjection() { .containsExactlyInAnyOrder(firstUser.getFirstname(), thirdUser.getFirstname(), fourthUser.getFirstname()); } + @Test // GH-2294 + void findByFluentExampleWithSimplePropertyPathsDoesntLoadUnrequestedPaths() { + + flushTestUsers(); + // make sure we don't get preinitialized entities back: + em.clear(); + + User prototype = new User(); + prototype.setFirstname("v"); + + List users = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.project("firstname").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + + assertThatExceptionOfType(LazyInitializationException.class) // + .isThrownBy( // + () -> users.forEach(u -> u.getRoles().size()) // forces loading of roles + ); + } + + @Test // GH-2294 + void findByFluentExampleWithCollectionPropertyPathsDoesntLoadUnrequestedPaths() { + + flushTestUsers(); + // make sure we don't get preinitialized entities back: + em.clear(); + + User prototype = new User(); + prototype.setFirstname("v"); + + List users = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.project("firstname", "roles").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + + assertThat(users).allMatch(u -> u.getRoles().isEmpty()); + } + + @Test // GH-2294 + void findByFluentExampleWithComplexPropertyPathsDoesntLoadUnrequestedPaths() { + + flushTestUsers(); + // make sure we don't get preinitialized entities back: + em.clear(); + + User prototype = new User(); + prototype.setFirstname("v"); + + List users = repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.project("roles.name").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + + assertThat(users).allMatch(u -> u.getRoles().isEmpty()); + } + @Test // GH-2294 void findByFluentExampleWithSortedInterfaceBasedProjection() { diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index 49ee607867..1df41b945f 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -22,11 +22,13 @@ import java.sql.Date; import java.time.LocalDate; import java.util.List; +import java.util.Set; import java.util.stream.Stream; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; +import org.hibernate.LazyInitializationException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -405,8 +407,12 @@ void findByFluentPredicateWithInterfaceBasedProjection() { @Test // GH-2294 void findByFluentPredicateWithSortedInterfaceBasedProjection() { - List userProjections = predicateExecutor.findBy(user.firstname.contains("v"), - q -> q.as(UserProjectionInterfaceBased.class).sortBy(Sort.by("firstname")).all()); + List userProjections = predicateExecutor.findBy( // + user.firstname.contains("v"), // + q -> q.as(UserProjectionInterfaceBased.class) // + .sortBy(Sort.by("firstname")) // + .all() // + ); assertThat(userProjections).extracting(UserProjectionInterfaceBased::getFirstname) .containsExactly(dave.getFirstname(), oliver.getFirstname()); @@ -442,7 +448,79 @@ class UserDto { .findBy(user.firstname.contains("v"), q -> q.as(UserDto.class).sortBy(Sort.by("firstname")).all())); } + @Test // GH-2329 + void findByFluentPredicateWithSimplePropertyPathsDoesntLoadUnrequestedPaths() { + + // make sure the entities are actually written to the database: + em.flush(); + // make sure we don't get preinitialized entities back: + em.clear(); + + List users = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.project("firstname", "lastname").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname) // + .containsExactlyInAnyOrder( // + dave.getFirstname(), // + oliver.getFirstname() // + ); + + assertThatExceptionOfType(LazyInitializationException.class) // + .isThrownBy( // + () -> users.forEach(u -> u.getRoles().size()) // forces loading of roles + ); + } + + @Test // GH-2329 + void findByFluentPredicateWithCollectionPropertyPathsLoadsRequestedPaths() { + + // make sure the entities are actually written to the database: + em.flush(); + // make sure we don't get preinitialized entities back: + em.clear(); + + List users = predicateExecutor.findBy(user.firstname.contains("v"), + q -> q.project("firstname", "roles").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder( // + dave.getFirstname(), // + oliver.getFirstname() // + ); + + assertThat(users).allMatch(u -> u.getRoles().isEmpty()); + + } + + @Test // GH-2329 + void findByFluentPredicateWithComplexPropertyPathsDoesntLoadsRequestedPaths() { + + // make sure the entities are actually written to the database: + em.flush(); + // make sure we don't get preinitialized entities back: + em.clear(); + + List users = predicateExecutor.findBy(user.firstname.contains("v"), q -> q.project("roles.name").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder( // + dave.getFirstname(), // + oliver.getFirstname() // + ); + + assertThat(users).allMatch(u -> u.getRoles().isEmpty()); + } + private interface UserProjectionInterfaceBased { String getFirstname(); + + Set getRoles(); } } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslProjectorUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslProjectorUnitTests.java new file mode 100644 index 0000000000..cac7e92005 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslProjectorUnitTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import static java.util.Arrays.*; +import static java.util.Collections.*; +import static org.mockito.Mockito.*; + +import java.util.HashSet; + +import javax.persistence.EntityGraph; +import javax.persistence.EntityManager; +import javax.persistence.Subgraph; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.querydsl.jpa.impl.AbstractJPAQuery; + +/** + * Unit tests for {@link QuerydslProjector}. + * + * @author Jens Schauder + */ +public class QuerydslProjectorUnitTests { + + EntityManager em = mock(EntityManager.class); + private EntityGraph entityGraph; + private AbstractJPAQuery jpaQuery = mock(AbstractJPAQuery.class); + + @BeforeEach + void beforeEach() { + + entityGraph = mock(EntityGraph.class, RETURNS_DEEP_STUBS); + when(em.createEntityGraph(DummyEntity.class)).thenReturn(entityGraph); + } + + // GH-2329 + @Test + void emptySetOfPropertiesDoesNotCreateEntityGraph() { + new QuerydslProjector(em).apply(DummyEntity.class, jpaQuery, emptySet()); + } + + // GH-2329 + @Test + void simpleSetOfPropertiesGetRegistered() { + + final HashSet properties = new HashSet<>(asList("one", "two")); + + new QuerydslProjector(em).apply(DummyEntity.class, jpaQuery, properties); + + verify(jpaQuery).setHint("javax.persistence.fetchgraph", entityGraph); + verify(entityGraph).addAttributeNodes("one"); + verify(entityGraph).addAttributeNodes("two"); + } + + // GH-2329 + @Test + void setOfCompositePropertiesGetRegisteredPiecewise() { + + final HashSet properties = new HashSet<>(asList("one.two", "eins.zwei.drei")); + + new QuerydslProjector(em).apply(DummyEntity.class, jpaQuery, properties); + + verify(jpaQuery).setHint("javax.persistence.fetchgraph", entityGraph); + + verify(entityGraph).addSubgraph("one"); + Subgraph one = entityGraph.addSubgraph("one"); + verify(one).addAttributeNodes("two"); + + verify(entityGraph).addSubgraph("eins"); + Subgraph eins = entityGraph.addSubgraph("eins"); + verify(eins).addSubgraph("zwei"); + Subgraph zwei = eins.addSubgraph("zwei"); + verify(zwei).addAttributeNodes("drei"); + } + + private static class DummyEntity { + DummyEntity one; + DummyEntity two; + DummyEntity eins; + DummyEntity zwei; + DummyEntity drei; + } +} From d637ce35df37eb9c47cdfe9e0e99f2728a5c41f1 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 29 Oct 2021 12:01:17 +0200 Subject: [PATCH 075/821] Polishing. Rename Projector to EntityGraphFactory. Remove QuerydslProjector and TypedQueryProjector. Remove MappingContext creation. See #2329 Original pull request: #2345. --- .../support/EntityGraphFactory.java | 70 ++++++++++++++++++ .../FetchableFluentQueryByExample.java | 48 ++++++------- .../FetchableFluentQueryByPredicate.java | 53 +++++++------- .../support/FluentQuerySupport.java | 11 +-- .../jpa/repository/support/Projector.java | 72 ------------------- .../support/QuerydslJpaPredicateExecutor.java | 9 +-- .../repository/support/QuerydslProjector.java | 39 ---------- .../support/SimpleJpaRepository.java | 10 +-- .../support/TypedQueryProjector.java | 37 ---------- ....java => EntityGraphFactoryUnitTests.java} | 34 +++------ 10 files changed, 134 insertions(+), 249 deletions(-) create mode 100644 src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java delete mode 100644 src/main/java/org/springframework/data/jpa/repository/support/Projector.java delete mode 100644 src/main/java/org/springframework/data/jpa/repository/support/QuerydslProjector.java delete mode 100644 src/main/java/org/springframework/data/jpa/repository/support/TypedQueryProjector.java rename src/test/java/org/springframework/data/jpa/repository/support/{QuerydslProjectorUnitTests.java => EntityGraphFactoryUnitTests.java} (63%) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java new file mode 100644 index 0000000000..a619916bf9 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java @@ -0,0 +1,70 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import java.util.Set; + +import javax.persistence.EntityGraph; +import javax.persistence.EntityManager; +import javax.persistence.Subgraph; + +import org.springframework.data.mapping.PropertyPath; + +/** + * Factory class to create an {@link EntityGraph} from a collection of property paths. + * + * @author Jens Schauder + * @since 2.6 + */ +abstract class EntityGraphFactory { + + public static final String HINT = "javax.persistence.fetchgraph"; + + /** + * Create an {@link EntityGraph} from a collection of properties. + * + * @param domainType + * @param properties + */ + public static EntityGraph create(EntityManager entityManager, Class domainType, Set properties) { + + EntityGraph entityGraph = entityManager.createEntityGraph(domainType); + + for (String property : properties) { + + Subgraph current = null; + + for (PropertyPath path : PropertyPath.from(property, domainType)) { + + if (path.hasNext()) { + current = current == null ? entityGraph.addSubgraph(path.getSegment()) + : current.addSubgraph(path.getSegment()); + continue; + } + + if (current == null) { + entityGraph.addAttributeNodes(path.getSegment()); + } else { + current.addAttributeNodes(path.getSegment()); + + } + } + } + + return entityGraph; + } + +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index 7e395ca15e..cb2645bfc1 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -32,9 +32,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.query.EscapeCharacter; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.util.Assert; @@ -58,34 +55,29 @@ class FetchableFluentQueryByExample extends FluentQuerySupport imple private final Function, Boolean> existsOperation; private final EntityManager entityManager; private final EscapeCharacter escapeCharacter; - private final Projector> projector; public FetchableFluentQueryByExample(Example example, Function> finder, Function, Long> countOperation, Function, Boolean> existsOperation, - MappingContext, ? extends PersistentProperty> context, EntityManager entityManager, EscapeCharacter escapeCharacter) { this(example, example.getProbeType(), (Class) example.getProbeType(), Sort.unsorted(), Collections.emptySet(), - finder, countOperation, existsOperation, context, entityManager, escapeCharacter, - new TypedQueryProjector(entityManager)); + finder, countOperation, existsOperation, entityManager, escapeCharacter); } private FetchableFluentQueryByExample(Example example, Class entityType, Class returnType, Sort sort, Collection properties, Function> finder, Function, Long> countOperation, Function, Boolean> existsOperation, - MappingContext, ? extends PersistentProperty> context, - EntityManager entityManager, EscapeCharacter escapeCharacter, Projector> projector) { + EntityManager entityManager, EscapeCharacter escapeCharacter) { - super(returnType, sort, properties, context, entityType); + super(returnType, sort, properties, entityType); this.example = example; this.finder = finder; this.countOperation = countOperation; this.existsOperation = existsOperation; this.entityManager = entityManager; this.escapeCharacter = escapeCharacter; - this.projector = projector; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort) */ @@ -95,11 +87,10 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null!"); return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort.and(sort), properties, finder, - countOperation, existsOperation, context, entityManager, escapeCharacter, - new TypedQueryProjector(entityManager)); + countOperation, existsOperation, entityManager, escapeCharacter); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) */ @@ -112,11 +103,10 @@ public FetchableFluentQuery as(Class resultType) { } return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, properties, finder, - countOperation, existsOperation, context, entityManager, escapeCharacter, - new TypedQueryProjector(entityManager)); + countOperation, existsOperation, entityManager, escapeCharacter); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection) */ @@ -124,11 +114,10 @@ public FetchableFluentQuery as(Class resultType) { public FetchableFluentQuery project(Collection properties) { return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, mergeProperties(properties), - finder, countOperation, existsOperation, context, entityManager, escapeCharacter, - new TypedQueryProjector(entityManager)); + finder, countOperation, existsOperation, entityManager, escapeCharacter); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() */ @@ -147,7 +136,7 @@ public R oneValue() { return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue() */ @@ -162,7 +151,7 @@ public R firstValue() { return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() */ @@ -174,7 +163,7 @@ public List all() { return convert(resultList); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) */ @@ -183,7 +172,7 @@ public Page page(Pageable pageable) { return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() */ @@ -195,7 +184,7 @@ public Stream stream() { .map(getConversionFunction()); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() */ @@ -204,7 +193,7 @@ public long count() { return countOperation.apply(example); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() */ @@ -230,7 +219,10 @@ private Page readPage(Pageable pageable) { private TypedQuery createSortedAndProjectedQuery() { TypedQuery query = finder.apply(sort); - projector.apply(entityType, query, properties); + + if (!properties.isEmpty()) { + query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties)); + } return query; } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index ff9c67f83e..f71eeacc0b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -23,14 +23,13 @@ import java.util.function.Function; import java.util.stream.Stream; +import javax.persistence.EntityManager; + import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.util.Assert; @@ -56,34 +55,32 @@ class FetchableFluentQueryByPredicate extends FluentQuerySupport imp private final BiFunction> pagedFinder; private final Function countOperation; private final Function existsOperation; - private final Projector> projector; + private final EntityManager entityManager; public FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, Function> finder, BiFunction> pagedFinder, Function countOperation, Function existsOperation, - MappingContext, ? extends PersistentProperty> context, - Projector> projector) { + EntityManager entityManager) { this(predicate, entityType, (Class) entityType, Sort.unsorted(), Collections.emptySet(), finder, pagedFinder, - countOperation, existsOperation, context, projector); + countOperation, existsOperation, entityManager); } private FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, Class resultType, Sort sort, Collection properties, Function> finder, BiFunction> pagedFinder, Function countOperation, Function existsOperation, - MappingContext, ? extends PersistentProperty> context, - Projector> projector) { + EntityManager entityManager) { - super(resultType, sort, properties, context, entityType); + super(resultType, sort, properties, entityType); this.predicate = predicate; this.finder = finder; this.pagedFinder = pagedFinder; this.countOperation = countOperation; this.existsOperation = existsOperation; - this.projector = projector; + this.entityManager = entityManager; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort) */ @@ -93,10 +90,10 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null!"); return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort.and(sort), properties, finder, - pagedFinder, countOperation, existsOperation, context, projector); + pagedFinder, countOperation, existsOperation, entityManager); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) */ @@ -110,10 +107,10 @@ public FetchableFluentQuery as(Class resultType) { } return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, properties, finder, - pagedFinder, countOperation, existsOperation, context, projector); + pagedFinder, countOperation, existsOperation, entityManager); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection) */ @@ -121,10 +118,10 @@ public FetchableFluentQuery as(Class resultType) { public FetchableFluentQuery project(Collection properties) { return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, mergeProperties(properties), - finder, pagedFinder, countOperation, existsOperation, context, projector); + finder, pagedFinder, countOperation, existsOperation, entityManager); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() */ @@ -142,7 +139,7 @@ public R oneValue() { return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue() */ @@ -156,7 +153,7 @@ public R firstValue() { return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() */ @@ -165,7 +162,7 @@ public List all() { return convert(createSortedAndProjectedQuery().fetch()); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) */ @@ -174,7 +171,7 @@ public Page page(Pageable pageable) { return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() */ @@ -186,7 +183,7 @@ public Stream stream() { .map(getConversionFunction()); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() */ @@ -195,7 +192,7 @@ public long count() { return countOperation.apply(predicate); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() */ @@ -206,8 +203,12 @@ public boolean exists() { private AbstractJPAQuery createSortedAndProjectedQuery() { - final AbstractJPAQuery query = finder.apply(sort); - projector.apply(entityType, query, properties); + AbstractJPAQuery query = finder.apply(sort); + + if (!properties.isEmpty()) { + query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties)); + } + return query; } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java index 2f299cc9c1..ab7d58ddc9 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -23,9 +23,6 @@ import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.domain.Sort; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.lang.Nullable; @@ -41,15 +38,12 @@ abstract class FluentQuerySupport { protected final Class resultType; protected final Sort sort; - /** Properties on which the query projects. {@literal null} stands for no special projection. */ protected final Set properties; - protected final MappingContext, ? extends PersistentProperty> context; protected final Class entityType; private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); - FluentQuerySupport(Class resultType, Sort sort, @Nullable Collection properties, - MappingContext, ? extends PersistentProperty> context, Class entityType) { + FluentQuerySupport(Class resultType, Sort sort, @Nullable Collection properties, Class entityType) { this.resultType = resultType; this.sort = sort; @@ -57,10 +51,9 @@ abstract class FluentQuerySupport { if (properties != null) { this.properties = new HashSet<>(properties); } else { - this.properties = new HashSet<>(); + this.properties = Collections.emptySet(); } - this.context = context; this.entityType = entityType; } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/Projector.java b/src/main/java/org/springframework/data/jpa/repository/support/Projector.java deleted file mode 100644 index 482d5f01cb..0000000000 --- a/src/main/java/org/springframework/data/jpa/repository/support/Projector.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.support; - -import java.util.Set; - -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.Subgraph; - -import org.springframework.data.mapping.PropertyPath; - -/** - * Turns a collection of property paths to an {@link EntityGraph} and applies it to a query abstraction - * - * @param the type of the query abstraction. - * @author Jens Schauder - * @since 2.6 - */ -abstract class Projector { - - private final EntityManager entityManager; - - protected Projector(EntityManager entityManager) { - this.entityManager = entityManager; - } - - public void apply(Class domainType, Q query, Set properties) { - - if (!properties.isEmpty()) { - - final javax.persistence.EntityGraph entityGraph = entityManager.createEntityGraph(domainType); - - for (String property : properties) { - - Subgraph subgraph = null; - - for (PropertyPath path : PropertyPath.from(property, domainType)) { - - if (path.hasNext()) { - subgraph = subgraph == null ? entityGraph.addSubgraph(path.getSegment()) - : subgraph.addSubgraph(path.getSegment()); - } else { - - if (subgraph == null) { - entityGraph.addAttributeNodes(path.getSegment()); - } else { - subgraph.addAttributeNodes(path.getSegment()); - } - } - } - } - - applyEntityGraph(query, entityGraph); - } - } - - abstract void applyEntityGraph(Q query, EntityGraph entityGraph); -} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 80ac351113..d748761ddb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -15,7 +15,6 @@ */ package org.springframework.data.jpa.repository.support; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.BiFunction; @@ -28,7 +27,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.querydsl.QSort; @@ -69,7 +67,7 @@ public class QuerydslJpaPredicateExecutor implements QuerydslPredicateExecuto /** * Creates a new {@link QuerydslJpaPredicateExecutor} from the given domain class and {@link EntityManager} and uses * the given {@link EntityPathResolver} to translate the domain class into an {@link EntityPath}. - * + * * @param entityInformation must not be {@literal null}. * @param entityManager must not be {@literal null}. * @param resolver must not be {@literal null}. @@ -167,7 +165,7 @@ public Page findAll(Predicate predicate, Pageable pageable) { return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount); } - /* + /* * (non-Javadoc) * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function) */ @@ -206,8 +204,7 @@ public R findBy(Predicate predicate, Function) fluentQuery); diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslProjector.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslProjector.java deleted file mode 100644 index 22bd98bed7..0000000000 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslProjector.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.support; - -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; - -import com.querydsl.jpa.impl.AbstractJPAQuery; - -/** - * Applies fetchgraph hints to {@code AbstractJPAQuery}. - * - * @author Jens Schauder - * @since 2.6 - */ -class QuerydslProjector extends Projector> { - - QuerydslProjector(EntityManager entityManager) { - super(entityManager); - } - - @Override - void applyEntityGraph(AbstractJPAQuery query, EntityGraph entityGraph) { - query.setHint("javax.persistence.fetchgraph", entityGraph); - } -} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index b69d43667f..f5f2fe1ef8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -48,15 +48,11 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder; import org.springframework.data.jpa.domain.Specification; -import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.query.EscapeCharacter; import org.springframework.data.jpa.repository.query.QueryUtils; import org.springframework.data.jpa.repository.support.QueryHints.NoHints; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.data.util.ProxyUtils; @@ -94,7 +90,6 @@ public class SimpleJpaRepository implements JpaRepositoryImplementation entityInformation; private final EntityManager em; private final PersistenceProvider provider; - private final MappingContext, ? extends PersistentProperty> context; private @Nullable CrudMethodMetadata metadata; private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT; @@ -113,9 +108,6 @@ public SimpleJpaRepository(JpaEntityInformation entityInformation, EntityM this.entityInformation = entityInformation; this.em = entityManager; this.provider = PersistenceProvider.fromEntityManager(entityManager); - this.context = em.getMetamodel() != null // - ? new JpaMetamodelMappingContext(Collections.singleton(em.getMetamodel())) // - : null; } /** @@ -595,7 +587,7 @@ public R findBy(Example example, Function fluentQuery = new FetchableFluentQueryByExample<>(example, finder, this::count, - this::exists, this.context, this.em, this.escapeCharacter); + this::exists, this.em, this.escapeCharacter); return queryFunction.apply(fluentQuery); } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/TypedQueryProjector.java b/src/main/java/org/springframework/data/jpa/repository/support/TypedQueryProjector.java deleted file mode 100644 index d3b15e5cf7..0000000000 --- a/src/main/java/org/springframework/data/jpa/repository/support/TypedQueryProjector.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.support; - -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; - -/** - * Applies fetchgraph hints to {@code TypedQuery}. - * - * @author Jens Schauder - * @since 2.6 - */ -public class TypedQueryProjector extends Projector> { - - public TypedQueryProjector(EntityManager entityManager) { - super(entityManager); - } - - void applyEntityGraph(TypedQuery query, EntityGraph entityGraph) { - query.setHint("javax.persistence.fetchgraph", entityGraph); - } -} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslProjectorUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java similarity index 63% rename from src/test/java/org/springframework/data/jpa/repository/support/QuerydslProjectorUnitTests.java rename to src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java index cac7e92005..e0ac1ce52c 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslProjectorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java @@ -16,7 +16,6 @@ package org.springframework.data.jpa.repository.support; import static java.util.Arrays.*; -import static java.util.Collections.*; import static org.mockito.Mockito.*; import java.util.HashSet; @@ -28,18 +27,16 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import com.querydsl.jpa.impl.AbstractJPAQuery; - /** - * Unit tests for {@link QuerydslProjector}. + * Unit tests for {@link EntityGraphFactory}. * * @author Jens Schauder */ -public class QuerydslProjectorUnitTests { +@SuppressWarnings("rawtypes") +class EntityGraphFactoryUnitTests { EntityManager em = mock(EntityManager.class); - private EntityGraph entityGraph; - private AbstractJPAQuery jpaQuery = mock(AbstractJPAQuery.class); + EntityGraph entityGraph; @BeforeEach void beforeEach() { @@ -48,21 +45,14 @@ void beforeEach() { when(em.createEntityGraph(DummyEntity.class)).thenReturn(entityGraph); } - // GH-2329 - @Test - void emptySetOfPropertiesDoesNotCreateEntityGraph() { - new QuerydslProjector(em).apply(DummyEntity.class, jpaQuery, emptySet()); - } - // GH-2329 @Test void simpleSetOfPropertiesGetRegistered() { - final HashSet properties = new HashSet<>(asList("one", "two")); + HashSet properties = new HashSet<>(asList("one", "two")); - new QuerydslProjector(em).apply(DummyEntity.class, jpaQuery, properties); + entityGraph = EntityGraphFactory.create(em, DummyEntity.class, properties); - verify(jpaQuery).setHint("javax.persistence.fetchgraph", entityGraph); verify(entityGraph).addAttributeNodes("one"); verify(entityGraph).addAttributeNodes("two"); } @@ -71,20 +61,18 @@ void simpleSetOfPropertiesGetRegistered() { @Test void setOfCompositePropertiesGetRegisteredPiecewise() { - final HashSet properties = new HashSet<>(asList("one.two", "eins.zwei.drei")); - - new QuerydslProjector(em).apply(DummyEntity.class, jpaQuery, properties); + HashSet properties = new HashSet<>(asList("one.two", "eins.zwei.drei")); - verify(jpaQuery).setHint("javax.persistence.fetchgraph", entityGraph); + entityGraph = EntityGraphFactory.create(em, DummyEntity.class, properties); verify(entityGraph).addSubgraph("one"); - Subgraph one = entityGraph.addSubgraph("one"); + Subgraph one = entityGraph.addSubgraph("one"); verify(one).addAttributeNodes("two"); verify(entityGraph).addSubgraph("eins"); - Subgraph eins = entityGraph.addSubgraph("eins"); + Subgraph eins = entityGraph.addSubgraph("eins"); verify(eins).addSubgraph("zwei"); - Subgraph zwei = eins.addSubgraph("zwei"); + Subgraph zwei = eins.addSubgraph("zwei"); verify(zwei).addAttributeNodes("drei"); } From c3c3743fcd7c2a80fe885334a73ff4786e74c27d Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 4 Nov 2021 09:57:30 -0500 Subject: [PATCH 076/821] Disable EclipseLink test class that freezes entire test suite. For some reason, EclipseLinkNamespaceUserRepositoryTests freezes. Can't figure out the source, so we are disabling it for now. See #2329 Original pull request: #2345. --- .../jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java index 5cb2263a0c..7378afeeaa 100644 --- a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java @@ -35,6 +35,7 @@ * @author Andrey Kovalev */ @ContextConfiguration(value = "classpath:eclipselink.xml") +@Disabled("hsqldb seems to hang on this test class without leaving a surefire report") class EclipseLinkNamespaceUserRepositoryTests extends NamespaceUserRepositoryTests { /** From 6a7d79b5da2104c94dbf5b1ea71ab6865e0a9493 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 12 Nov 2021 10:49:15 +0100 Subject: [PATCH 077/821] Prepare 2.6 GA (2021.1.0). See #2340 --- pom.xml | 8 ++++---- src/main/resources/notice.txt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 27f5fb3f0e..9d594a9d05 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0-SNAPSHOT + 2.6.0 @@ -25,7 +25,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.6.0-SNAPSHOT + 2.6.0 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-release + https://repo.spring.io/libs-release diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 2132e61574..3d634c7b5b 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data JPA 2.6 RC1 (2021.1.0) +Spring Data JPA 2.6 GA (2021.1.0) Copyright (c) [2011-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -29,5 +29,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From 792d0fe75ae33b17fced5bf481ba7710bc141683 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 12 Nov 2021 10:49:36 +0100 Subject: [PATCH 078/821] Release version 2.6 GA (2021.1.0). See #2340 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9d594a9d05..066b3b9005 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0-SNAPSHOT + 2.6.0 Spring Data JPA Spring Data module for JPA repositories. From 317c2eaa56c5fac5d7f2e0521d137adc67497331 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 12 Nov 2021 10:59:41 +0100 Subject: [PATCH 079/821] Prepare next development iteration. See #2340 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 066b3b9005..27a0c3a8e8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.6.0 + 2.7.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. From dccc58040b2eb59f1ed425df491e6de87b130897 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 12 Nov 2021 10:59:43 +0100 Subject: [PATCH 080/821] After release cleanups. See #2340 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 27a0c3a8e8..906fa1e409 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.6.0 + 2.7.0-SNAPSHOT @@ -25,7 +25,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.6.0 + 2.7.0-SNAPSHOT 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-release - https://repo.spring.io/libs-release + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 8cd1388318032ddcdeeeadf347f691ed3a03e583 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 24 Nov 2021 19:19:07 +0100 Subject: [PATCH 081/821] Add support for DTO projections on derived query methods. In case a derived query uses a DTO, we now create a select clause that uses a constructor expression for the DTO type. This wasn't supported before and expexted either an interface-based projection or an explicit query using a constructor expression. Fixes #2363. --- .../data/jpa/repository/query/JpaQueryCreator.java | 7 ++++++- .../data/jpa/repository/UserRepositoryTests.java | 8 ++++++++ .../data/jpa/repository/sample/UserRepository.java | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 825d8a3415..3eb0762eef 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -176,7 +176,12 @@ protected CriteriaQuery complete(@Nullable Predicate predicate selections.add(toExpressionRecursively(root, path, true).alias(property)); } - query = query.multiselect(selections); + Class typeToRead = returnedType.getTypeToRead(); + + query = typeToRead.isInterface() + ? query.multiselect(selections) + : query.select((Selection) builder.construct(typeToRead, + selections.toArray(new Selection[0]))); } else if (tree.isExistsProjection()) { diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 623bb41fa0..c2f7c4b529 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -2599,6 +2599,14 @@ void findByElementCollectionInAttributeIgnoreCaseWithNulls() { assertThat(result).containsOnly(firstUser); } + @Test // #2363 + void readsDtoProjections() { + + flushTestUsers(); + + assertThat(repository.findAllDtoProjectedBy()).hasSize(4); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index ab03b79675..721123ca19 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -611,6 +611,9 @@ Page findAllOrderedBySpecialNameMultipleParams(@Param("name") String name, // DATAJPA-1303 Page findByAttributesIgnoreCaseIn(Pageable pageable, String... attributes); + // #2363 + List findAllDtoProjectedBy(); + interface RolesAndFirstname { String getFirstname(); From fda5c5a8df56f04e8166e20d5c5d25a5fe2fa585 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Wed, 1 Dec 2021 00:30:54 +0100 Subject: [PATCH 082/821] Fixed typos in JpaRepositoryExtension class. Fixed one typo in the JavaDoc of JpaRepositoryExtension#afterBeanDiscovery and JpaRepositoryExtension#processBean. Original pull request #2367 --- .../data/jpa/repository/cdi/JpaRepositoryExtension.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index d6d866cab5..af386eb930 100644 --- a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -56,7 +56,7 @@ public JpaRepositoryExtension() { } /** - * Implementation of a an observer which checks for EntityManager beans and stores them in {@link #entityManagers} for + * Implementation of an observer which checks for EntityManager beans and stores them in {@link #entityManagers} for * later association with corresponding repository beans. * * @param The type. @@ -78,8 +78,7 @@ void processBean(@Observes ProcessBean processBean) { } /** - * Implementation of a an observer which registers beans to the CDI container for the detected Spring Data - * repositories. + * Implementation of an observer which registers beans to the CDI container for the detected Spring Data repositories. *

* The repository beans are associated to the EntityManagers using their qualifiers. * From 45b860fd6d701b0394fc3f21510eb97ce3cb0bc2 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Fri, 3 Dec 2021 12:22:03 +0800 Subject: [PATCH 083/821] Optimize QuerydslJpaPredicateExecutor.exists. Fixes #2333 Original pull request #2373 --- .../jpa/repository/support/QuerydslJpaPredicateExecutor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index d748761ddb..2d9628331e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -40,6 +40,7 @@ import com.querydsl.core.types.EntityPath; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.PathBuilder; import com.querydsl.jpa.JPQLQuery; import com.querydsl.jpa.impl.AbstractJPAQuery; @@ -225,7 +226,7 @@ public long count(Predicate predicate) { */ @Override public boolean exists(Predicate predicate) { - return createQuery(predicate).fetchCount() > 0; + return createQuery(predicate).select(Expressions.ONE).fetchFirst() != null; } /** From b6a2cea2451ed92056725da5c7fd608f83fde8b4 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 3 Dec 2021 11:11:22 +0100 Subject: [PATCH 084/821] Polishing. Adding author tag. See #2333 Original pull request #2373 --- .../jpa/repository/support/QuerydslJpaPredicateExecutor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 2d9628331e..3b78605c7b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -56,6 +56,7 @@ * @author Christoph Strobl * @author Jens Schauder * @author Greg Turnquist + * @author Yanming Zhou */ public class QuerydslJpaPredicateExecutor implements QuerydslPredicateExecutor { From 910f02d1ad5d0126221aee48140ef3d52f0b48d5 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Wed, 1 Dec 2021 10:47:15 +0800 Subject: [PATCH 085/821] Optimize SimpleJpaRepository.exists(Example) for better performance. 1. Set max results to 1 to avoid full scan 2. Select 1 to reduce size of ResultSet Original pull request #2368 --- .../data/jpa/repository/support/SimpleJpaRepository.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index f5f2fe1ef8..adfc5e76b5 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -530,8 +530,12 @@ public long count(Example example) { */ @Override public boolean exists(Example example) { - return !getQuery(new ExampleSpecification(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) - .getResultList().isEmpty(); + Specification spec = new ExampleSpecification<>(example, this.escapeCharacter); + CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(Integer.class); + cq.select(this.em.getCriteriaBuilder().literal(1)); + applySpecificationToCriteria(spec, example.getProbeType(), cq); + TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); + return query.setMaxResults(1).getSingleResult() != null; } /* From bfa7be315a9ca69dcd4cbf76a539347bbec88b3f Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 3 Dec 2021 11:27:11 +0100 Subject: [PATCH 086/821] Correctly handle exists when it should return false. In the previous implementation it would throw an exception. Original pull request #2368 --- .../repository/support/SimpleJpaRepository.java | 4 +++- .../data/jpa/repository/UserRepositoryTests.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index adfc5e76b5..5f8a82715a 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -80,6 +80,7 @@ * @author Sander Krabbenborg * @author Jesse Wouters * @author Greg Turnquist + * @author Yanming Zhou */ @Repository @Transactional(readOnly = true) @@ -530,12 +531,13 @@ public long count(Example example) { */ @Override public boolean exists(Example example) { + Specification spec = new ExampleSpecification<>(example, this.escapeCharacter); CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(Integer.class); cq.select(this.em.getCriteriaBuilder().literal(1)); applySpecificationToCriteria(spec, example.getProbeType(), cq); TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); - return query.setMaxResults(1).getSingleResult() != null; + return query.setMaxResults(1).getResultList().size() == 1; } /* diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index c2f7c4b529..a4a438e857 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -2318,6 +2318,20 @@ void existsByExampleWithExcludedAttributes() { assertThat(exists).isEqualTo(true); } + @Test // GH-2368 + void existsByExampleNegative() { + + flushTestUsers(); + + User prototype = new User(); + prototype.setAge(4711); // there is none with that age + + Example example = Example.of(prototype, matching().withIgnorePaths("createdAt")); + boolean exists = repository.exists(example); + + assertThat(exists).isEqualTo(false); + } + @Test // DATAJPA-905 void executesPagedSpecificationSettingAnOrder() { From 4630fba49900352aedbe5545922f802fb26e6548 Mon Sep 17 00:00:00 2001 From: heowc Date: Sat, 4 Dec 2021 01:05:32 +0900 Subject: [PATCH 087/821] Polishing. Move off deprecated classes. Add Nullable annotations. Correct mistakes in documentation. Original pull request #2374 --- .../repository/query/JpaParametersParameterAccessor.java | 2 ++ .../data/jpa/repository/query/JpaQueryExecution.java | 6 +++--- .../data/jpa/repository/query/ParameterBinder.java | 3 ++- .../jpa/repository/query/QueryParameterSetterFactory.java | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java index 679614ecb2..d32465a8d7 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java @@ -19,6 +19,7 @@ import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.lang.Nullable; /** * {@link org.springframework.data.repository.query.ParameterAccessor} based on an {@link Parameters} instance. It also @@ -39,6 +40,7 @@ public class JpaParametersParameterAccessor extends ParametersParameterAccessor super(parameters, values); } + @Nullable public T getValue(Parameter parameter) { return super.getValue(parameter.getIndex()); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index cb2bc61b4d..8c62807386 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -34,7 +34,7 @@ import org.springframework.data.domain.SliceImpl; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor; -import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.data.support.PageableExecutionUtils; import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.StreamUtils; import org.springframework.lang.Nullable; @@ -75,7 +75,7 @@ public abstract class JpaQueryExecution { * Executes the given {@link AbstractStringBasedJpaQuery} with the given {@link ParameterBinder}. * * @param query must not be {@literal null}. - * @param values must not be {@literal null}. + * @param accessor must not be {@literal null}. * @return */ @Nullable @@ -112,7 +112,7 @@ public Object execute(AbstractJpaQuery query, JpaParametersParameterAccessor acc * Method to implement {@link AbstractStringBasedJpaQuery} executions by single enum values. * * @param query - * @param values + * @param accessor * @return */ @Nullable diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 203d59f145..5132797ad6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -87,7 +87,8 @@ public void bind(QueryParameterSetter.BindableQuery query, JpaParametersParamete * Binds the parameters to the given query and applies special parameter types (e.g. pagination). * * @param query must not be {@literal null}. - * @param values values of method parameters to be assigned to the query parameters. + * @param metadata must not be {@literal null}. + * @param accessor must not be {@literal null}. */ Query bindAndPrepare(Query query, QueryParameterSetter.QueryMetadata metadata, JpaParametersParameterAccessor accessor) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index 4b2a12d2a0..fb002f8507 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -168,7 +168,7 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla * Evaluates the given {@link Expression} against the given values. * * @param expression must not be {@literal null}. - * @param values must not be {@literal null}. + * @param accessor must not be {@literal null}. * @return the result of the evaluation. */ @Nullable @@ -250,6 +250,7 @@ private JpaParameter findParameterForBinding(ParameterBinding binding) { return null; } + @Nullable private Object getValue(JpaParametersParameterAccessor accessor, Parameter parameter) { return accessor.getValue(parameter); } From f0e8a289d0432e381a9720d4af154e5409e6237e Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Mon, 6 Dec 2021 10:52:03 +0800 Subject: [PATCH 088/821] Fix obsoleted package name of Querydsl. Original pull request #2378 --- Spring Data JPA.sonargraph | 2 +- .../support/QuerydslJpaPredicateExecutor.java | 14 +++++++------- .../repository/support/QuerydslJpaRepository.java | 15 ++++++++------- .../jpa/repository/sample/RoleRepository.java | 3 ++- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Spring Data JPA.sonargraph b/Spring Data JPA.sonargraph index d41bcefa36..821ad1beee 100644 --- a/Spring Data JPA.sonargraph +++ b/Spring Data JPA.sonargraph @@ -108,7 +108,7 @@ - + diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 3b78605c7b..f6e63d1bc3 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -87,7 +87,7 @@ public QuerydslJpaPredicateExecutor(JpaEntityInformation entityInformation /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.mysema.query.types.Predicate) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.querydsl.core.types.Predicate) */ @Override public Optional findOne(Predicate predicate) { @@ -103,7 +103,7 @@ public Optional findOne(Predicate predicate) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate) */ @Override public List findAll(Predicate predicate) { @@ -115,7 +115,7 @@ public List findAll(Predicate predicate) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate, com.mysema.query.types.OrderSpecifier[]) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, com.querydsl.core.types.OrderSpecifier[]) */ @Override public List findAll(Predicate predicate, OrderSpecifier... orders) { @@ -128,7 +128,7 @@ public List findAll(Predicate predicate, OrderSpecifier... orders) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate, org.springframework.data.domain.Sort) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort) */ @Override public List findAll(Predicate predicate, Sort sort) { @@ -141,7 +141,7 @@ public List findAll(Predicate predicate, Sort sort) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.OrderSpecifier[]) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[]) */ @Override public List findAll(OrderSpecifier... orders) { @@ -214,7 +214,7 @@ public R findBy(Predicate predicate, Function entityInformation, Enti /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.mysema.query.types.Predicate) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.querydsl.core.types.Predicate) */ @Override public Optional findOne(Predicate predicate) { @@ -112,7 +113,7 @@ public Optional findOne(Predicate predicate) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate) */ @Override public List findAll(Predicate predicate) { @@ -121,7 +122,7 @@ public List findAll(Predicate predicate) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate, com.mysema.query.types.OrderSpecifier[]) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, com.querydsl.core.types.OrderSpecifier[]) */ @Override public List findAll(Predicate predicate, OrderSpecifier... orders) { @@ -130,7 +131,7 @@ public List findAll(Predicate predicate, OrderSpecifier... orders) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.Predicate, org.springframework.data.domain.Sort) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort) */ @Override public List findAll(Predicate predicate, Sort sort) { @@ -142,7 +143,7 @@ public List findAll(Predicate predicate, Sort sort) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.mysema.query.types.OrderSpecifier[]) + * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[]) */ @Override public List findAll(OrderSpecifier... orders) { @@ -176,7 +177,7 @@ public R findBy(Predicate predicate, /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.mysema.query.types.Predicate) + * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.querydsl.core.types.Predicate) */ @Override public long count(Predicate predicate) { @@ -185,7 +186,7 @@ public long count(Predicate predicate) { /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.mysema.query.types.Predicate) + * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.querydsl.core.types.Predicate) */ @Override public boolean exists(Predicate predicate) { diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java index 24a3dd1471..93f331956e 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java @@ -33,6 +33,7 @@ * * @author Oliver Gierke * @author Thomas Darimont + * @author Yanming Zhou */ public interface RoleRepository extends CrudRepository, QuerydslPredicateExecutor { @@ -56,7 +57,7 @@ public interface RoleRepository extends CrudRepository, QuerydslP /* * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findOne(com.mysema.query.types.Predicate) + * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findOne(com.querydsl.core.types.Predicate) */ @Override @Lock(LockModeType.READ) From d49f0ca29d1bca412ab3e34548d0b594d1bec1df Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Mon, 6 Dec 2021 15:05:27 +0800 Subject: [PATCH 089/821] Destroy docker container properly. Since JdbcContaieners are AutoClosables Spring will call `close()` on shut down. Original pull request #2379 --- .../MySqlStoredProcedureIntegrationTests.java | 26 ++++++++----------- ...stgresStoredProcedureIntegrationTests.java | 25 ++++++++---------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java index 2bde920383..53fccd746b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -63,6 +63,7 @@ * * @author Gabriel Basilio * @author Greg Turnquist + * @author Yanming Zhou */ @Transactional @ExtendWith(SpringExtension.class) @@ -190,26 +191,21 @@ public interface EmployeeRepositoryWithNoCursor extends JpaRepository MYSQL_CONTAINER; - - @Bean - public DataSource dataSource() { - - if (MYSQL_CONTAINER == null) { - - MYSQL_CONTAINER = new MySQLContainer<>("mysql:8.0.24") // + @SuppressWarnings("resource") + @Bean(initMethod = "start") + public MySQLContainer container() { + return new MySQLContainer<>("mysql:8.0.24") // .withUsername("test") // .withPassword("test") // .withConfigurationOverride(""); - MYSQL_CONTAINER.start(); - } + } + @Bean + public DataSource dataSource(MySQLContainer container) { MysqlDataSource dataSource = new MysqlDataSource(); - dataSource.setUrl(MYSQL_CONTAINER.getJdbcUrl()); - dataSource.setUser("root"); - dataSource.setPassword(MYSQL_CONTAINER.getPassword()); - dataSource.setDatabaseName(MYSQL_CONTAINER.getDatabaseName()); - + dataSource.setUrl(container.getJdbcUrl()); + dataSource.setUser(container.getUsername()); + dataSource.setPassword(container.getPassword()); return dataSource; } diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index ae6b42366e..b67a53b6f2 100644 --- a/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -65,6 +65,7 @@ * * @author Gabriel Basilio * @author Greg Turnquist + * @author Yanming Zhou */ @Transactional @ExtendWith(SpringExtension.class) @@ -193,23 +194,19 @@ public interface EmployeeRepositoryWithRefCursor extends JpaRepository POSTGRESQL_CONTAINER; + @SuppressWarnings("resource") + @Bean(initMethod = "start") + public PostgreSQLContainer container() { + return new PostgreSQLContainer<>("postgres:9.6.12") // + .withUsername("postgres"); + } @Bean - public DataSource dataSource() { - - if (POSTGRESQL_CONTAINER == null) { - - POSTGRESQL_CONTAINER = new PostgreSQLContainer<>("postgres:9.6.12") // - .withUsername("postgres"); - POSTGRESQL_CONTAINER.start(); - } - + public DataSource dataSource(PostgreSQLContainer container) { PGSimpleDataSource dataSource = new PGSimpleDataSource(); - dataSource.setUrl(POSTGRESQL_CONTAINER.getJdbcUrl()); - dataSource.setUser(POSTGRESQL_CONTAINER.getUsername()); - dataSource.setPassword(POSTGRESQL_CONTAINER.getPassword()); - + dataSource.setUrl(container.getJdbcUrl()); + dataSource.setUser(container.getUsername()); + dataSource.setPassword(container.getPassword()); return dataSource; } From 5c7a00e2f6e3733f8b6ea98a45347c5d67ab6ce4 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 6 Dec 2021 10:28:06 +0100 Subject: [PATCH 090/821] Polishing. Code formatting. Original pull request #2379 --- .../MySqlStoredProcedureIntegrationTests.java | 12 ++++++++---- .../PostgresStoredProcedureIntegrationTests.java | 8 ++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java index 53fccd746b..2b93979b5b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -186,7 +186,9 @@ public interface EmployeeRepositoryWithNoCursor extends JpaRepository entityListFromNamedProcedure(); } - @EnableJpaRepositories(considerNestedRepositories = true, basePackageClasses = Config.class, + @EnableJpaRepositories( // + considerNestedRepositories = true, // + basePackageClasses = Config.class, // includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = EmployeeRepositoryWithNoCursor.class)) @EnableTransactionManagement static class Config { @@ -194,14 +196,16 @@ static class Config { @SuppressWarnings("resource") @Bean(initMethod = "start") public MySQLContainer container() { + return new MySQLContainer<>("mysql:8.0.24") // - .withUsername("test") // - .withPassword("test") // - .withConfigurationOverride(""); + .withUsername("test") // + .withPassword("test") // + .withConfigurationOverride(""); } @Bean public DataSource dataSource(MySQLContainer container) { + MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUrl(container.getJdbcUrl()); dataSource.setUser(container.getUsername()); diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index b67a53b6f2..8598d84069 100644 --- a/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -152,8 +152,10 @@ void testEntityListFromNamedProcedure() { @Entity @AllArgsConstructor @NoArgsConstructor - @NamedStoredProcedureQuery(name = "get_employees_postgres", procedureName = "get_employees", - parameters = { @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class) }, + @NamedStoredProcedureQuery( // + name = "get_employees_postgres", // + procedureName = "get_employees", // + parameters = { @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class) }, // resultClasses = Employee.class) public static class Employee { @@ -197,12 +199,14 @@ static class Config { @SuppressWarnings("resource") @Bean(initMethod = "start") public PostgreSQLContainer container() { + return new PostgreSQLContainer<>("postgres:9.6.12") // .withUsername("postgres"); } @Bean public DataSource dataSource(PostgreSQLContainer container) { + PGSimpleDataSource dataSource = new PGSimpleDataSource(); dataSource.setUrl(container.getJdbcUrl()); dataSource.setUser(container.getUsername()); From 2b37dc22a030c78c9196c50e22200f36cc861876 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Sat, 4 Dec 2021 09:18:30 +0100 Subject: [PATCH 091/821] Removed deprecated function call in `JpaQueryMethod`. The method `assertParameterNamesInAnnotatedQuery()` used the deprecated function `StringUtils.isEmpty(String)`. This has been changed to a function call that is not deprecated and has the same logic. Original pull request #2375 --- .../data/jpa/repository/query/JpaQueryMethod.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index 37a3cbdced..f8eaebb5d6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -165,7 +165,7 @@ private void assertParameterNamesInAnnotatedQuery() { continue; } - if (StringUtils.isEmpty(annotatedQuery) + if (!StringUtils.hasText(annotatedQuery) || !annotatedQuery.contains(String.format(":%s", parameter.getName().get())) && !annotatedQuery.contains(String.format("#%s", parameter.getName().get()))) { throw new IllegalStateException( From 519ed8171e3e67d6fc95c1fdc1a5590f74645f1b Mon Sep 17 00:00:00 2001 From: FanYuliang Date: Wed, 24 Nov 2021 14:30:35 -0600 Subject: [PATCH 092/821] Fixing Flakiness in UserRepositoryStoredProcedureIntegrationTests Original pull request #2364 --- .../UserRepositoryStoredProcedureIntegrationTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java index cc755a5d67..ace4f14ff6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java @@ -96,7 +96,7 @@ void entityAnnotatedCustomNamedProcedurePlus1IO2() { Map result = repository.entityAnnotatedCustomNamedProcedurePlus1IO2(1); - assertThat(result).containsExactly(entry("res", 2), entry("res2", 3)); + assertThat(result).containsOnly(entry("res", 2), entry("res2", 3)); } @Test // DATAJPA-1579 @@ -104,7 +104,7 @@ void entityAnnotatedCustomNamedProcedurePlus1IOoptional() { Map result = repository.entityAnnotatedCustomNamedProcedurePlus1IOoptional(1); - assertThat(result).containsExactly(entry("res", 2), entry("res2", null)); + assertThat(result).containsOnly(entry("res", 2), entry("res2", null)); } @Test // DATAJPA-455 From a052065c43f1f2834dd83d9ec50271dab74b4c30 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 8 Dec 2021 10:37:35 +0100 Subject: [PATCH 093/821] Unwrap user type when checking if type is JPA-managed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now unwrap the user type in JpaMetamodelMappingContext.shouldCreatePersistentEntityFor(…) to enable usage of proxy types when requesting a PersistentEntity. Previously, we checked against the proxy type which is not managed by the JPA meta-model. Closes #2383 --- .../mapping/JpaMetamodelMappingContext.java | 2 +- ...tamodelMappingContextIntegrationTests.java | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index cbd73ed7d3..4ea38a1355 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -86,7 +86,7 @@ protected JpaPersistentProperty createPersistentProperty(Property property, JpaP */ @Override protected boolean shouldCreatePersistentEntityFor(TypeInformation type) { - return models.isMetamodelManagedType(type); + return models.isMetamodelManagedType(type.getUserTypeInformation()); } /** diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java index 15b36ceaab..81d4b0981b 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java @@ -22,6 +22,7 @@ import javax.persistence.EntityManager; import org.hibernate.proxy.HibernateProxy; +import org.hibernate.proxy.LazyInitializer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -51,6 +52,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Jens Schauder + * @author Mark Paluch * @since 1.3 */ @ExtendWith(SpringExtension.class) @@ -75,6 +77,16 @@ void setsUpMappingContextCorrectly() { assertThat(entity).isNotNull(); } + @Test // GH-2383 + void considersProxiedTypes() { + + JpaPersistentEntityImpl directEntity = context.getRequiredPersistentEntity(User.class); + assertThat(directEntity).isNotNull(); + + JpaPersistentEntityImpl unproxiedEntitx = context.getRequiredPersistentEntity(UserProxy.class); + assertThat(unproxiedEntitx).isNotNull().isSameAs(directEntity); + } + @Test void detectsIdProperty() { @@ -183,4 +195,17 @@ void traversesEmbeddablesButNoOtherMappingAnnotations() { static class Config { } + + static class UserProxy extends User implements HibernateProxy { + + @Override + public Object writeReplace() { + return null; + } + + @Override + public LazyInitializer getHibernateLazyInitializer() { + return null; + } + } } From fde0cf7cf1f890458c60a0a7d9949a8943188409 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 8 Dec 2021 10:25:49 +0100 Subject: [PATCH 094/821] Polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trim trailing white spaces. Introduce getRequiredMetamodel(…) to refine nullability handling. Use diamond syntax and method handles where possible. See #2383 --- .../mapping/JpaMetamodelMappingContext.java | 35 +++++++++++++++---- ...tamodelMappingContextIntegrationTests.java | 2 +- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index 4ea38a1355..7fa79ceb88 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -67,7 +67,8 @@ public JpaMetamodelMappingContext(Set models) { */ @Override protected JpaPersistentEntityImpl createPersistentEntity(TypeInformation typeInformation) { - return new JpaPersistentEntityImpl(typeInformation, persistenceProvider, models.getMetamodel(typeInformation)); + return new JpaPersistentEntityImpl<>(typeInformation, persistenceProvider, + models.getRequiredMetamodel(typeInformation)); } /* @@ -91,17 +92,17 @@ protected boolean shouldCreatePersistentEntityFor(TypeInformation type) { /** * We customize the lookup of {@link PersistentPropertyPaths} by also traversing properties that are embeddables. - * + * * @see org.springframework.data.mapping.context.AbstractMappingContext#findPersistentPropertyPaths(java.lang.Class, * java.util.function.Predicate) */ @Override public PersistentPropertyPaths findPersistentPropertyPaths(Class type, Predicate predicate) { - return doFindPersistentPropertyPaths(type, predicate, it -> it.isEmbeddable()); + return doFindPersistentPropertyPaths(type, predicate, JpaPersistentProperty::isEmbeddable); } - /* + /* * (non-Javadoc) * @see org.springframework.data.mapping.context.AbstractMappingContext#hasPersistentEntityFor(java.lang.Class) */ @@ -126,7 +127,7 @@ private Metamodels(Set metamodels) { /** * Returns the {@link JpaMetamodel} for the given type. - * + * * @param type must not be {@literal null}. * @return */ @@ -138,9 +139,29 @@ public JpaMetamodel getMetamodel(TypeInformation type) { return metamodel == null ? null : JpaMetamodel.of(metamodel); } + /** + * Returns the required {@link JpaMetamodel} for the given type or throw {@link IllegalArgumentException} if the + * {@code type} is not JPA-managed. + * + * @param type must not be {@literal null}. + * @return + * @throws IllegalArgumentException if {@code type} is not JPA-managed. + * @since 2.6.1 + */ + public JpaMetamodel getRequiredMetamodel(TypeInformation type) { + + JpaMetamodel metamodel = getMetamodel(type); + + if (metamodel == null) { + throw new IllegalArgumentException(String.format("Required JpaMetamodel not found for %s!", type)); + } + + return metamodel; + } + /** * Returns whether the given type is managed by one of the underlying {@link Metamodel} instances. - * + * * @param type must not be {@literal null}. * @return */ @@ -150,7 +171,7 @@ public boolean isMetamodelManagedType(TypeInformation type) { /** * Returns whether the given type is managed by one of the underlying {@link Metamodel} instances. - * + * * @param type must not be {@literal null}. * @return */ diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java index 81d4b0981b..e09b9044c2 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java @@ -57,7 +57,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class JpaMetamodelMappingContextIntegrationTests { +class JpaMetamodelMappingContextIntegrationTests { private JpaMetamodelMappingContext context; @Autowired ProductRepository products; From 2231d574def4e47261ff2d45ed6204f27afa380f Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 16 Nov 2021 14:00:16 +0100 Subject: [PATCH 095/821] Migrate from Slf4J to JCL. Closes #2359 --- .../repository/cdi/JpaRepositoryExtension.java | 10 +++++----- .../JpaMetamodelMappingContextFactoryBean.java | 6 +++--- .../query/JpaQueryLookupStrategy.java | 6 +++--- .../data/jpa/repository/query/NamedQuery.java | 18 +++++++++--------- .../repository/query/QueryParameterSetter.java | 8 ++++---- .../support/JpaRepositoryFactory.java | 8 ++++---- ...thScanningPersistenceUnitPostProcessor.java | 10 +++++----- .../support/MergingPersistenceUnitManager.java | 12 ++++++------ .../cdi/CdiExtensionIntegrationTests.java | 6 +++--- .../repository/sample/UserRepositoryImpl.java | 6 +++--- 10 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index af386eb930..50bcda0f3d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -32,8 +32,8 @@ import javax.enterprise.inject.spi.ProcessBean; import javax.persistence.EntityManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.data.repository.cdi.CdiRepositoryBean; import org.springframework.data.repository.cdi.CdiRepositoryExtensionSupport; @@ -47,7 +47,7 @@ */ public class JpaRepositoryExtension extends CdiRepositoryExtensionSupport { - private static final Logger LOGGER = LoggerFactory.getLogger(JpaRepositoryExtension.class); + private static final Log LOGGER = LogFactory.getLog(JpaRepositoryExtension.class); private final Map, Bean> entityManagers = new HashMap, Bean>(); @@ -70,7 +70,7 @@ void processBean(@Observes ProcessBean processBean) { if (type instanceof Class && EntityManager.class.isAssignableFrom((Class) type)) { Set qualifiers = new HashSet(bean.getQualifiers()); if (bean.isAlternative() || !entityManagers.containsKey(qualifiers)) { - LOGGER.debug("Discovered '{}' with qualifiers {}.", EntityManager.class.getName(), qualifiers); + LOGGER.debug(String.format("Discovered '%s' with qualifiers %s.", EntityManager.class.getName(), qualifiers)); entityManagers.put(qualifiers, (Bean) bean); } } @@ -93,7 +93,7 @@ void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanMan // Create the bean representing the repository. CdiRepositoryBean repositoryBean = createRepositoryBean(repositoryType, qualifiers, beanManager); - LOGGER.info("Registering bean for '{}' with qualifiers {}.", repositoryType.getName(), qualifiers); + LOGGER.info(String.format("Registering bean for '%s' with qualifiers %s.", repositoryType.getName(), qualifiers)); // Register the bean to the extension and the container. registerBean(repositoryBean); diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index ce3cdd6f34..ca000690d4 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -21,8 +21,8 @@ import javax.persistence.EntityManagerFactory; import javax.persistence.metamodel.Metamodel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.FactoryBean; @@ -44,7 +44,7 @@ public class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean implements ApplicationContextAware { - private static final Logger LOG = LoggerFactory.getLogger(JpaMetamodelMappingContextFactoryBean.class); + private static final Log LOG = LogFactory.getLog(JpaMetamodelMappingContextFactoryBean.class); private @Nullable ListableBeanFactory beanFactory; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 9c3db738a8..2c3ac135e6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -19,8 +19,8 @@ import javax.persistence.EntityManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.data.jpa.repository.Query; import org.springframework.data.projection.ProjectionFactory; @@ -44,7 +44,7 @@ */ public final class JpaQueryLookupStrategy { - private static final Logger LOG = LoggerFactory.getLogger(JpaQueryLookupStrategy.class); + private static final Log LOG = LogFactory.getLog(JpaQueryLookupStrategy.class); /** * Private constructor to prevent instantiation. diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index b6d473a116..8927619ecb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -20,9 +20,8 @@ import javax.persistence.Tuple; import javax.persistence.TypedQuery; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryCreationException; @@ -45,7 +44,7 @@ final class NamedQuery extends AbstractJpaQuery { + "have a JpaDialect configured at your EntityManagerFactoryBean as this affects " + "discovering the concrete persistence provider."; - private static final Logger LOG = LoggerFactory.getLogger(NamedQuery.class); + private static final Log LOG = LogFactory.getLog(NamedQuery.class); private final String queryName; private final String countQueryName; @@ -89,8 +88,9 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { } if (parameters.hasPageableParameter()) { - LOG.warn("Finder method {} is backed by a NamedQuery" + " but contains a Pageable parameter! Sorting delivered " - + "via this Pageable will not be applied!", method); + LOG.warn(String.format( + "Finder method %s is backed by a NamedQuery but contains a Pageable parameter! Sorting delivered via this Pageable will not be applied!", + method)); } this.metadataCache = new QueryParameterSetter.QueryMetadataCache(); @@ -115,7 +115,7 @@ static boolean hasNamedQuery(EntityManager em, String queryName) { lookupEm.createNamedQuery(queryName); return true; } catch (IllegalArgumentException e) { - LOG.debug("Did not find named query {}", queryName); + LOG.debug(String.format("Did not find named query %s", queryName)); return false; } finally { lookupEm.close(); @@ -134,7 +134,7 @@ public static RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em final String queryName = method.getNamedQueryName(); - LOG.debug("Looking up named query {}", queryName); + LOG.debug(String.format("Looking up named query %s", queryName)); if (!hasNamedQuery(em, queryName)) { return null; @@ -142,7 +142,7 @@ public static RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em try { RepositoryQuery query = new NamedQuery(method, em); - LOG.debug("Found named query {}!", queryName); + LOG.debug(String.format("Found named query %s!", queryName)); return query; } catch (IllegalArgumentException e) { return null; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index c645b6ce81..6e84647108 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -30,8 +30,8 @@ import javax.persistence.TemporalType; import javax.persistence.criteria.ParameterExpression; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -155,7 +155,7 @@ public void execute(Runnable block) { } }; - private static final Logger LOG = LoggerFactory.getLogger(ErrorHandling.class); + private static final Log LOG = LogFactory.getLog(ErrorHandling.class); abstract void execute(Runnable block); } @@ -280,7 +280,7 @@ private static Class unwrapClass(Query query) { } catch (RuntimeException e) { - LoggerFactory.getLogger(QueryMetadata.class).warn("Failed to unwrap actual class for Query proxy.", e); + LogFactory.getLog(QueryMetadata.class).warn("Failed to unwrap actual class for Query proxy.", e); return queryType; } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index d4773baab2..3cd9ec88df 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -25,7 +25,8 @@ import javax.persistence.EntityManager; import javax.persistence.Tuple; -import org.slf4j.Logger; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.jpa.projection.CollectionAwareProjectionFactory; @@ -304,8 +305,7 @@ private static class EclipseLinkProjectionQueryCreationListener implements Query private static final String ECLIPSELINK_PROJECTIONS = "Usage of Spring Data projections detected on persistence provider EclipseLink. Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs:"; - private static final Logger log = org.slf4j.LoggerFactory - .getLogger(EclipseLinkProjectionQueryCreationListener.class); + private static final Log log = LogFactory.getLog(EclipseLinkProjectionQueryCreationListener.class); private final JpaMetamodel metamodel; @@ -340,7 +340,7 @@ public void onCreation(AbstractJpaQuery query) { this.warningLogged = true; } - log.info(" - {}", queryMethod); + log.info(String.format(" - %s", queryMethod)); } } } diff --git a/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java b/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java index 44929f4f12..8d471f692d 100644 --- a/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java @@ -24,8 +24,8 @@ import javax.persistence.Entity; import javax.persistence.MappedSuperclass; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; @@ -58,7 +58,7 @@ public class ClasspathScanningPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor, ResourceLoaderAware, EnvironmentAware { - private static final Logger LOG = LoggerFactory.getLogger(ClasspathScanningPersistenceUnitPostProcessor.class); + private static final Log LOG = LogFactory.getLog(ClasspathScanningPersistenceUnitPostProcessor.class); private final String basePackage; @@ -133,7 +133,7 @@ public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { for (BeanDefinition definition : provider.findCandidateComponents(basePackage)) { - LOG.debug("Registering classpath-scanned entity {} in persistence unit info!", definition.getBeanClassName()); + LOG.debug(String.format("Registering classpath-scanned entity %s in persistence unit info!", definition.getBeanClassName())); if (definition.getBeanClassName() != null) { pui.addManagedClassName(definition.getBeanClassName()); @@ -142,7 +142,7 @@ public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { for (String location : scanForMappingFileLocations()) { - LOG.debug("Registering classpath-scanned entity mapping file {} in persistence unit info!", location); + LOG.debug(String.format("Registering classpath-scanned entity mapping file %s in persistence unit info!", location)); pui.addMappingFileName(location); } diff --git a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index 93c3d242e8..b8bf05bb15 100644 --- a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -20,8 +20,8 @@ import javax.persistence.spi.PersistenceUnitInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager; import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo; @@ -34,7 +34,7 @@ */ public class MergingPersistenceUnitManager extends DefaultPersistenceUnitManager { - private static final Logger LOG = LoggerFactory.getLogger(MergingPersistenceUnitManager.class); + private static final Log LOG = LogFactory.getLog(MergingPersistenceUnitManager.class); /* * (non-Javadoc) @@ -68,21 +68,21 @@ void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui, PersistenceU for (URL url : oldPui.getJarFileUrls()) { if (!pui.getJarFileUrls().contains(url)) { - LOG.debug("Adding JAR file URL {} to persistence unit {}.", url, persistenceUnitName); + LOG.debug(String.format("Adding JAR file URL %s to persistence unit %s.", url, persistenceUnitName)); pui.addJarFileUrl(url); } } for (String className : oldPui.getManagedClassNames()) { if (!pui.getManagedClassNames().contains(className)) { - LOG.debug("Adding class {} to PersistenceUnit {}", className, persistenceUnitName); + LOG.debug(String.format("Adding class %s to PersistenceUnit %s", className, persistenceUnitName)); pui.addManagedClassName(className); } } for (String mappingFileName : oldPui.getMappingFileNames()) { if (!pui.getMappingFileNames().contains(mappingFileName)) { - LOG.debug("Adding mapping file to persistence unit {}.", mappingFileName, persistenceUnitName); + LOG.debug(String.format("Adding mapping file to persistence unit %s.", mappingFileName, persistenceUnitName)); pui.addMappingFileName(mappingFileName); } } diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java index ecf7242afc..5502778df7 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java @@ -26,8 +26,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Integration tests for Spring Data JPA CDI extension. @@ -40,7 +40,7 @@ class CdiExtensionIntegrationTests { private static SeContainer container; - private static Logger LOGGER = LoggerFactory.getLogger(CdiExtensionIntegrationTests.class); + private static Log LOGGER = LogFactory.getLog(CdiExtensionIntegrationTests.class); @BeforeAll static void setUp() { diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java index 0c530d2a2a..648bb7da8d 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.repository.sample; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.repository.JpaContext; @@ -29,7 +29,7 @@ */ public class UserRepositoryImpl implements UserRepositoryCustom { - private static final Logger LOG = LoggerFactory.getLogger(UserRepositoryImpl.class); + private static final Log LOG = LogFactory.getLog(UserRepositoryImpl.class); @Autowired public UserRepositoryImpl(JpaContext context) { From bca2adc32a635e8381951a1e044fd4c5bd8b989d Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 17 Nov 2021 13:50:56 +0100 Subject: [PATCH 096/821] Polishing. Guard String.format in debug logs with isDebugEnabled. See #2359 --- .../repository/cdi/JpaRepositoryExtension.java | 7 ++++--- .../JpaMetamodelMappingContextFactoryBean.java | 2 +- .../data/jpa/repository/query/NamedQuery.java | 14 +++++++++++--- .../support/MergingPersistenceUnitManager.java | 15 ++++++++++++--- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index 50bcda0f3d..23fab9896f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -49,7 +49,7 @@ public class JpaRepositoryExtension extends CdiRepositoryExtensionSupport { private static final Log LOGGER = LogFactory.getLog(JpaRepositoryExtension.class); - private final Map, Bean> entityManagers = new HashMap, Bean>(); + private final Map, Bean> entityManagers = new HashMap<>(); public JpaRepositoryExtension() { LOGGER.info("Activating CDI extension for Spring Data JPA repositories."); @@ -64,11 +64,12 @@ public JpaRepositoryExtension() { */ @SuppressWarnings("unchecked") void processBean(@Observes ProcessBean processBean) { + Bean bean = processBean.getBean(); for (Type type : bean.getTypes()) { // Check if the bean is an EntityManager. if (type instanceof Class && EntityManager.class.isAssignableFrom((Class) type)) { - Set qualifiers = new HashSet(bean.getQualifiers()); + Set qualifiers = new HashSet<>(bean.getQualifiers()); if (bean.isAlternative() || !entityManagers.containsKey(qualifiers)) { LOGGER.debug(String.format("Discovered '%s' with qualifiers %s.", EntityManager.class.getName(), qualifiers)); entityManagers.put(qualifiers, (Bean) bean); @@ -121,7 +122,7 @@ private CdiRepositoryBean createRepositoryBean(Class repositoryType, S } // Construct and return the repository bean. - return new JpaRepositoryBean(beanManager, entityManagerBean, qualifiers, repositoryType, + return new JpaRepositoryBean<>(beanManager, entityManagerBean, qualifiers, repositoryType, Optional.of(getCustomImplementationDetector())); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index ca000690d4..d7f69b8612 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -71,7 +71,7 @@ public Class getObjectType() { * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() */ @Override - protected JpaMetamodelMappingContext createInstance() throws Exception { + protected JpaMetamodelMappingContext createInstance() { if (LOG.isDebugEnabled()) { LOG.debug("Initializing JpaMetamodelMappingContext…"); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 8927619ecb..f9a5816712 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -115,7 +115,10 @@ static boolean hasNamedQuery(EntityManager em, String queryName) { lookupEm.createNamedQuery(queryName); return true; } catch (IllegalArgumentException e) { - LOG.debug(String.format("Did not find named query %s", queryName)); + + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Did not find named query %s", queryName)); + } return false; } finally { lookupEm.close(); @@ -134,15 +137,20 @@ public static RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em final String queryName = method.getNamedQueryName(); - LOG.debug(String.format("Looking up named query %s", queryName)); + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Looking up named query %s", queryName)); + } if (!hasNamedQuery(em, queryName)) { return null; } try { + RepositoryQuery query = new NamedQuery(method, em); - LOG.debug(String.format("Found named query %s!", queryName)); + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Found named query %s!", queryName)); + } return query; } catch (IllegalArgumentException e) { return null; diff --git a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index b8bf05bb15..f6877b8a1d 100644 --- a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -68,21 +68,30 @@ void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui, PersistenceU for (URL url : oldPui.getJarFileUrls()) { if (!pui.getJarFileUrls().contains(url)) { - LOG.debug(String.format("Adding JAR file URL %s to persistence unit %s.", url, persistenceUnitName)); + + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Adding JAR file URL %s to persistence unit %s.", url, persistenceUnitName)); + } pui.addJarFileUrl(url); } } for (String className : oldPui.getManagedClassNames()) { if (!pui.getManagedClassNames().contains(className)) { - LOG.debug(String.format("Adding class %s to PersistenceUnit %s", className, persistenceUnitName)); + + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Adding class %s to PersistenceUnit %s", className, persistenceUnitName)); + } pui.addManagedClassName(className); } } for (String mappingFileName : oldPui.getMappingFileNames()) { if (!pui.getMappingFileNames().contains(mappingFileName)) { - LOG.debug(String.format("Adding mapping file to persistence unit %s.", mappingFileName, persistenceUnitName)); + + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Adding mapping file to persistence unit %s.", mappingFileName, persistenceUnitName)); + } pui.addMappingFileName(mappingFileName); } } From 27f6866e6086c361cafa8c1747e70fc9287a3583 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 4 Jan 2022 11:56:01 -0600 Subject: [PATCH 097/821] Properly handle ignoreCase for aliased fields. When writing a select with a field alias, properly handle Sort.Order's ignoreCase. Closes #2280 Original pull request #2399 --- .../data/jpa/repository/query/QueryUtils.java | 8 ++++++-- .../jpa/repository/query/QueryUtilsUnitTests.java | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 84a8b70e0e..213e21f03d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -38,11 +38,11 @@ import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.metamodel.Attribute; +import javax.persistence.metamodel.Attribute.PersistentAttributeType; import javax.persistence.metamodel.Bindable; import javax.persistence.metamodel.ManagedType; import javax.persistence.metamodel.PluralAttribute; import javax.persistence.metamodel.SingularAttribute; -import javax.persistence.metamodel.Attribute.PersistentAttributeType; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.dao.InvalidDataAccessApiUsageException; @@ -73,6 +73,7 @@ * @author Mohammad Hewedy * @author Andriy Redko * @author Peter Großmann + * @author Greg Turnquist */ public abstract class QueryUtils { @@ -291,7 +292,9 @@ private static String getOrderClause(Set joinAliases, Set select checkSortExpression(order); if (selectionAlias.contains(property)) { - return String.format("%s %s", property, toJpaDirection(order)); + return String.format("%s %s", // + order.isIgnoreCase() ? String.format("lower(%s)", property) : property, // + toJpaDirection(order)); } boolean qualifyReference = !property.contains("("); // ( indicates a function @@ -465,6 +468,7 @@ public static String createCountQueryFor(String originalQuery) { * @param originalQuery must not be {@literal null}. * @param countProjection may be {@literal null}. * @return a query String to be used a count query for pagination. Guaranteed to be not {@literal null}. + * @return a query String to be used a count query for pagination. Guaranteed to be not {@literal null}. * @since 1.6 * @deprecated use {@link DeclaredQuery#deriveCountQuery(String, String)} instead. */ diff --git a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 9040cfabf5..00794830ae 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -39,6 +39,7 @@ * @author Florian Lüdiger * @author Grégoire Druant * @author Mohammad Hewedy + * @author Greg Turnquist */ class QueryUtilsUnitTests { @@ -444,6 +445,18 @@ void appliesSortCorrectlyForFieldAliases() { assertThat(fullQuery).endsWith("order by authorName asc"); } + @Test // GH-2280 + void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { + + String query = "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer"; + Sort sort = Sort.by(Order.by("name").ignoreCase()); + + String fullQuery = applySorting(query, sort); + + assertThat(fullQuery).isEqualTo( + "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(name) asc"); + } + @Test // DATAJPA-1061 void appliesSortCorrectlyForFunctionAliases() { From 8c4123f6b2ac40dd2a97a7c267668e888c73e022 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 5 Jan 2022 09:41:56 +0100 Subject: [PATCH 098/821] Polishing. Minor formatting. See #2280 Original pull request #2399 --- .../springframework/data/jpa/repository/query/QueryUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 213e21f03d..e24a871ceb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -292,6 +292,7 @@ private static String getOrderClause(Set joinAliases, Set select checkSortExpression(order); if (selectionAlias.contains(property)) { + return String.format("%s %s", // order.isIgnoreCase() ? String.format("lower(%s)", property) : property, // toJpaDirection(order)); @@ -300,7 +301,9 @@ private static String getOrderClause(Set joinAliases, Set select boolean qualifyReference = !property.contains("("); // ( indicates a function for (String joinAlias : joinAliases) { + if (property.startsWith(joinAlias.concat("."))) { + qualifyReference = false; break; } @@ -342,6 +345,7 @@ static Set getOuterJoinAliases(String query) { * @return a {@literal Set} containing all found aliases. Guaranteed to be not {@literal null}. */ private static Set getFieldAliases(String query) { + Set result = new HashSet<>(); Matcher matcher = FIELD_ALIAS_PATTERN.matcher(query); From 0926d4bfb634ac626817be9410822e1fa2aa6d1a Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 4 Jan 2022 09:18:56 -0600 Subject: [PATCH 099/821] Introduce JpaRepository.getReferenceById. Introduce a repository method that makes it clear the return value is a reference. Deprecate the previous methods. Closes #2232 Original pull request #2398 --- .../data/jpa/repository/JpaRepository.java | 23 ++++++++++++++++--- .../support/SimpleJpaRepository.java | 20 +++++++++++----- .../AbstractPersistableIntegrationTests.java | 14 ++++++++++- .../jpa/repository/UserRepositoryTests.java | 11 ++++++++- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index cbc36e210b..fb5a437f8e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -33,6 +33,7 @@ * @author Mark Paluch * @author Sander Krabbenborg * @author Jesse Wouters + * @author Greg Turnquist */ @NoRepositoryBean public interface JpaRepository extends PagingAndSortingRepository, QueryByExampleExecutor { @@ -96,7 +97,9 @@ public interface JpaRepository extends PagingAndSortingRepository, * @deprecated Use {@link #deleteAllInBatch(Iterable)} instead. */ @Deprecated - default void deleteInBatch(Iterable entities){deleteAllInBatch(entities);} + default void deleteInBatch(Iterable entities) { + deleteAllInBatch(entities); + } /** * Deletes the given entities in a batch which means it will create a single query. This kind of operation leaves JPAs @@ -108,7 +111,6 @@ public interface JpaRepository extends PagingAndSortingRepository, */ void deleteAllInBatch(Iterable entities); - /** * Deletes the entities identified by the given ids using a single query. This kind of operation leaves JPAs first * level cache and the database out of sync. Consider flushing the {@link EntityManager} before calling this method. @@ -132,7 +134,7 @@ public interface JpaRepository extends PagingAndSortingRepository, * @param id must not be {@literal null}. * @return a reference to the entity with the given identifier. * @see EntityManager#getReference(Class, Object) for details on when an exception is thrown. - * @deprecated use {@link JpaRepository#getById(ID)} instead. + * @deprecated use {@link JpaRepository#getReferenceById(ID)} instead. */ @Deprecated T getOne(ID id); @@ -146,10 +148,25 @@ public interface JpaRepository extends PagingAndSortingRepository, * @param id must not be {@literal null}. * @return a reference to the entity with the given identifier. * @see EntityManager#getReference(Class, Object) for details on when an exception is thrown. + * @deprecated use {@link JpaRepository#getReferenceById(ID)} instead. * @since 2.5 */ + @Deprecated T getById(ID id); + /** + * Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is + * implemented this is very likely to always return an instance and throw an + * {@link javax.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers + * immediately. + * + * @param id must not be {@literal null}. + * @return a reference to the entity with the given identifier. + * @see EntityManager#getReference(Class, Object) for details on when an exception is thrown. + * @since 2.7 + */ + T getReferenceById(ID id); + /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 5f8a82715a..0fbc14f20e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -331,26 +331,34 @@ protected QueryHints getQueryHints() { @Deprecated @Override public T getOne(ID id) { - - Assert.notNull(id, ID_MUST_NOT_BE_NULL); - return em.getReference(getDomainClass(), id); + return getReferenceById(id); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#getById(java.io.Serializable) */ + @Deprecated @Override public T getById(ID id) { + return getReferenceById(id); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.JpaRepository#getReferenceById(java.io.Serializable) + */ + @Override + public T getReferenceById(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); return em.getReference(getDomainClass(), id); } /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#existsById(java.io.Serializable) - */ + * (non-Javadoc) + * @see org.springframework.data.repository.CrudRepository#existsById(java.io.Serializable) + */ @Override public boolean existsById(ID id) { diff --git a/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java index 1931937673..2f9dadf2b3 100644 --- a/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java @@ -21,7 +21,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.sample.CustomAbstractPersistable; @@ -37,6 +36,7 @@ * @author Oliver Gierke * @author Jens Schauder * @author Jesse Wouters + * @author Greg Turnquist */ @Transactional @ExtendWith(SpringExtension.class) @@ -79,4 +79,16 @@ void equalsWorksForProxiedEntitiesUsingGetById() { assertThat(proxy).isEqualTo(proxy); } + + @Test // gh-1697 + void equalsWorksForProxiedEntitiesUsingGetReferenceById() { + + CustomAbstractPersistable entity = repository.saveAndFlush(new CustomAbstractPersistable()); + + em.clear(); + + CustomAbstractPersistable proxy = repository.getReferenceById(entity.getId()); + + assertThat(proxy).isEqualTo(proxy); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index a4a438e857..113ae1a5ad 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -1011,7 +1011,16 @@ void looksUpEntityReferenceUsingGetById() { assertThat(result).isEqualTo(firstUser); } - @Test // DATAJPA-415 + @Test // gh-1697 + void looksUpEntityReferenceUsingGetReferenceById() { + + flushTestUsers(); + + User result = repository.getReferenceById(firstUser.getId()); + assertThat(result).isEqualTo(firstUser); + } + + @Test // DATAJPA-415 void invokesQueryWithVarargsParametersCorrectly() { flushTestUsers(); From 08bcf5571f706a3867b910f277b983ebc1e01038 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 5 Jan 2022 10:29:54 +0100 Subject: [PATCH 100/821] Polishing. Corrected issue number comments. See #2232 Original pull request #2398 --- .../jpa/repository/AbstractPersistableIntegrationTests.java | 4 ++-- .../data/jpa/repository/UserRepositoryTests.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java index 2f9dadf2b3..ebb2daf5e5 100644 --- a/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java @@ -68,7 +68,7 @@ void equalsWorksForProxiedEntities() { assertThat(proxy).isEqualTo(proxy); } - @Test // gh-1697 + @Test // GH-1697 void equalsWorksForProxiedEntitiesUsingGetById() { CustomAbstractPersistable entity = repository.saveAndFlush(new CustomAbstractPersistable()); @@ -80,7 +80,7 @@ void equalsWorksForProxiedEntitiesUsingGetById() { assertThat(proxy).isEqualTo(proxy); } - @Test // gh-1697 + @Test // GH-2232 void equalsWorksForProxiedEntitiesUsingGetReferenceById() { CustomAbstractPersistable entity = repository.saveAndFlush(new CustomAbstractPersistable()); diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 113ae1a5ad..841f7b4667 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -1002,7 +1002,7 @@ void looksUpEntityReference() { assertThat(result).isEqualTo(firstUser); } - @Test // gh-1697 + @Test // GH-1697 void looksUpEntityReferenceUsingGetById() { flushTestUsers(); @@ -1011,7 +1011,7 @@ void looksUpEntityReferenceUsingGetById() { assertThat(result).isEqualTo(firstUser); } - @Test // gh-1697 + @Test // GH-2232 void looksUpEntityReferenceUsingGetReferenceById() { flushTestUsers(); From 65aa03f31c72b14308a90e19ea2689b8b1eec4ca Mon Sep 17 00:00:00 2001 From: dk2k Date: Mon, 4 Oct 2021 17:26:26 +0300 Subject: [PATCH 101/821] Minor changes based on FindBugs findings. Original pull request #2324 --- .../data/jpa/convert/QueryByExamplePredicateBuilder.java | 2 +- .../data/jpa/mapping/JpaPersistentPropertyImpl.java | 1 + .../data/jpa/repository/query/StringQuery.java | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java index 1b4e332501..be9b899d80 100644 --- a/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java +++ b/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java @@ -160,7 +160,7 @@ static List getPredicates(String path, CriteriaBuilder cb, Path fr PathNode node = currentNode.add(attribute.getName(), attributeValue); if (node.spansCycle()) { throw new InvalidDataAccessApiUsageException( - String.format("Path '%s' from root %s must not span a cyclic property reference!\r\n%s", currentPath, + String.format("Path '%s' from root %s must not span a cyclic property reference!%n%s", currentPath, ClassUtils.getShortName(probeType), node)); } diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index a0ac87b813..4e9c06795a 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -333,6 +333,7 @@ private boolean detectUpdatability() { continue; } + // may cause NullPointerException if the returned value is null return (boolean) AnnotationUtils.getValue(annotation, "updatable"); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 23c82cf6e8..dbac54211d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -266,8 +266,6 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St String expression = spelExtractor.getParameter(parameterName == null ? parameterIndexString : parameterName); String replacement = null; - Assert.isTrue(parameterIndexString != null || parameterName != null, () -> String.format("We need either a name or an index! Offending query string: %s", query)); - expressionParameterIndex++; if ("".equals(parameterIndexString)) { @@ -293,7 +291,7 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St } else { checkAndRegister(new LikeParameterBinding(parameterName, likeType, expression), bindings); - replacement = expression != null ? ":" + parameterName : matcher.group(5); + replacement = ":" + parameterName; } break; From 6bae3c5a48a96c4c1585e6716ec1937949102e69 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 12 Jan 2022 15:44:48 +0100 Subject: [PATCH 102/821] Polishing. Original pull request #2324 --- .../data/jpa/mapping/JpaPersistentPropertyImpl.java | 4 ++-- .../jpa/repository/config/JpaRepositoryConfigExtension.java | 4 ++-- .../data/jpa/repository/query/StringQuery.java | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index 4e9c06795a..f32dee706a 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -333,8 +333,8 @@ private boolean detectUpdatability() { continue; } - // may cause NullPointerException if the returned value is null - return (boolean) AnnotationUtils.getValue(annotation, "updatable"); + final Object updatable = AnnotationUtils.getValue(annotation, "updatable"); + return (boolean) (updatable == null ? true : updatable); } return true; diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 7cdba8957d..2de91cdde6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository.config; +import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.*; + import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Collection; @@ -52,8 +54,6 @@ import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; -import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.*; - /** * JPA specific configuration extension parsing custom attributes from the XML namespace and * {@link EnableJpaRepositories} annotation. Also, it registers bean definitions for a diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index dbac54211d..3760477ae6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -263,6 +263,7 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St Integer parameterIndex = getParameterIndex(parameterIndexString); String typeSource = matcher.group(COMPARISION_TYPE_GROUP); + Assert.isTrue(parameterIndexString != null || parameterName != null, () -> String.format("We need either a name or an index! Offending query string: %s", query)); String expression = spelExtractor.getParameter(parameterName == null ? parameterIndexString : parameterName); String replacement = null; From cd0f6212e36583e3770ebcf192af029f8183fb1a Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 14 Jan 2022 10:57:50 +0100 Subject: [PATCH 103/821] Prepare 2.7 M1 (2021.2.0). See #2358 --- pom.xml | 8 ++++---- src/main/resources/notice.txt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 906fa1e409..a99607d6db 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.7.0-SNAPSHOT + 2.7.0-M1 @@ -25,7 +25,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.7.0-SNAPSHOT + 2.7.0-M1 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 3d634c7b5b..a436711ec9 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data JPA 2.6 GA (2021.1.0) +Spring Data JPA 2.7 M1 (2021.2.0) Copyright (c) [2011-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -30,5 +30,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From ea300487a5a2c148976dbdd1e356bec523a38bc7 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 14 Jan 2022 10:58:16 +0100 Subject: [PATCH 104/821] Release version 2.7 M1 (2021.2.0). See #2358 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a99607d6db..6e7ef0f2f3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.7.0-SNAPSHOT + 2.7.0-M1 Spring Data JPA Spring Data module for JPA repositories. From 183360818f3d43e8ea6a268039074de09f6fa49f Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 14 Jan 2022 11:07:59 +0100 Subject: [PATCH 105/821] Prepare next development iteration. See #2358 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6e7ef0f2f3..a99607d6db 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.7.0-M1 + 2.7.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. From 7fdee2fd29134786f0028aca2293520546ffc824 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 14 Jan 2022 11:08:02 +0100 Subject: [PATCH 106/821] After release cleanups. See #2358 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index a99607d6db..906fa1e409 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.7.0-M1 + 2.7.0-SNAPSHOT @@ -25,7 +25,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.7.0-M1 + 2.7.0-SNAPSHOT 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From c6a7b78553e794fa5b61dc69cf933ef30f8577be Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 18 Jan 2022 09:08:59 +0100 Subject: [PATCH 107/821] Prepare 2.7 M2 (2021.2.0). See #2406 --- pom.xml | 8 ++++---- src/main/resources/notice.txt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 906fa1e409..6d66e2da6d 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.7.0-SNAPSHOT + 2.7.0-M2 @@ -25,7 +25,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.7.0-SNAPSHOT + 2.7.0-M2 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index a436711ec9..4a28e2e5fc 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data JPA 2.7 M1 (2021.2.0) +Spring Data JPA 2.7 M2 (2021.2.0) Copyright (c) [2011-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -31,5 +31,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file. + From a9635ec23e99a355e673a515ca1ca4ff8d7a7e1a Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 18 Jan 2022 09:09:34 +0100 Subject: [PATCH 108/821] Release version 2.7 M2 (2021.2.0). See #2406 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6d66e2da6d..868227f015 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.7.0-SNAPSHOT + 2.7.0-M2 Spring Data JPA Spring Data module for JPA repositories. From 57efb53b7ed620a3f0ec93dbe5f537ea784b61cb Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 18 Jan 2022 09:21:29 +0100 Subject: [PATCH 109/821] Prepare next development iteration. See #2406 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 868227f015..6d66e2da6d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa - 2.7.0-M2 + 2.7.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. From 1a3ba4c415ab593dc461fb94c7988172c1418635 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 18 Jan 2022 09:21:32 +0100 Subject: [PATCH 110/821] After release cleanups. See #2406 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 6d66e2da6d..906fa1e409 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.data.build spring-data-parent - 2.7.0-M2 + 2.7.0-SNAPSHOT @@ -25,7 +25,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.7.0-M2 + 2.7.0-SNAPSHOT 0.10.3 org.hibernate @@ -555,8 +555,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 680cc4dec3c5821d095224dda42449d9e154e1c1 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 18 Jan 2022 15:26:30 +0100 Subject: [PATCH 111/821] Use IdentifiableType.hasSingleIdAttribute() to detect IdClass presence to obtain IdClass attributes. We now use IdentifiableType.hasSingleIdAttribute() to detect whether a type should have an IdClass. hasSingleIdAttribute is defined to return false if an IdClass is being used. It could also return false in case multiple identifier attributes are in place (composite Id) which is a bit of a grey area. At least we avoid using Hibernate-specific API. Original pull request #2412 See #2330 Closes #2391 --- .../data/jpa/provider/PersistenceProvider.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 4f9323c7df..23e0d9ea0e 100644 --- a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,8 +32,8 @@ import org.eclipse.persistence.queries.ScrollableCursor; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; -import org.hibernate.metamodel.model.domain.spi.IdentifiableTypeDescriptor; import org.hibernate.proxy.HibernateProxy; + import org.springframework.data.util.CloseableIterator; import org.springframework.lang.Nullable; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -103,9 +103,7 @@ public Object getIdentifierFrom(Object entity) { */ @Override public Set> getIdClassAttributes(IdentifiableType type) { - return type instanceof IdentifiableTypeDescriptor && ((IdentifiableTypeDescriptor) type).hasIdClass() - ? super.getIdClassAttributes(type) - : Collections.emptySet(); + return type.hasSingleIdAttribute() ? Collections.emptySet() : super.getIdClassAttributes(type); } /* From 7f0ec6f2f3bafcf8752b7db67d03d15929923b15 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 21 Sep 2021 15:27:54 +0200 Subject: [PATCH 112/821] Remove usage of deprecated code. Closes #2310 --- .../data/jpa/mapping/JpaPersistentPropertyImpl.java | 9 --------- .../data/jpa/repository/query/AbstractJpaQuery.java | 2 +- .../jpa/repository/support/QuerydslJpaRepository.java | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index f32dee706a..f8160131a1 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -124,15 +124,6 @@ public Class getActualType() { : super.getActualType(); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.PersistentProperty#getPersistentEntityTypes() - */ - @Override - public Iterable> getPersistentEntityTypes() { - return getPersistentEntityTypeInformation(); - } - /* * (non-Javadoc) * @see org.springframework.data.mapping.model.AbstractPersistentProperty#getPersistentEntityTypeInformation() diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index c17b9a79ed..f072a8b939 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -69,7 +69,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { private final PersistenceProvider provider; private final Lazy execution; - final Lazy parameterBinder = new Lazy<>(this::createBinder); + final Lazy parameterBinder = Lazy.of(this::createBinder); /** * Creates a new {@link AbstractJpaQuery} from the given {@link JpaQueryMethod}. diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index aea00507a0..843a287f67 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -32,7 +32,7 @@ import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.querydsl.SimpleEntityPathResolver; import org.springframework.data.repository.query.FluentQuery; -import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.data.support.PageableExecutionUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; From 0842ff8c5671d9ccf679e966c8f6dbcb1458d63c Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 22 Sep 2021 13:19:05 +0200 Subject: [PATCH 113/821] Remove ThreeTenBackPort support. Closes #2311 --- pom.xml | 3 +- .../ThreeTenBackPortJpaConverters.java | 127 ------------------ .../convert/threetenbp/DateTimeSample.java | 41 ------ ...BackPortJpaConvertersIntegrationTests.java | 84 ------------ 4 files changed, 2 insertions(+), 253 deletions(-) delete mode 100644 src/main/java/org/springframework/data/jpa/convert/threetenbp/ThreeTenBackPortJpaConverters.java delete mode 100644 src/test/java/org/springframework/data/jpa/convert/threetenbp/DateTimeSample.java delete mode 100644 src/test/java/org/springframework/data/jpa/convert/threetenbp/ThreeTenBackPortJpaConvertersIntegrationTests.java diff --git a/pom.xml b/pom.xml index 906fa1e409..c02ae0f6f1 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 diff --git a/src/main/java/org/springframework/data/jpa/convert/threetenbp/ThreeTenBackPortJpaConverters.java b/src/main/java/org/springframework/data/jpa/convert/threetenbp/ThreeTenBackPortJpaConverters.java deleted file mode 100644 index 8aa31ce113..0000000000 --- a/src/main/java/org/springframework/data/jpa/convert/threetenbp/ThreeTenBackPortJpaConverters.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2015-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.convert.threetenbp; - -import java.util.Date; - -import javax.persistence.AttributeConverter; -import javax.persistence.Converter; - -import org.springframework.data.convert.ThreeTenBackPortConverters.DateToInstantConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.DateToLocalDateConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.DateToLocalDateTimeConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.DateToLocalTimeConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.InstantToDateConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.LocalDateTimeToDateConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.LocalDateToDateConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.LocalTimeToDateConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.StringToZoneIdConverter; -import org.springframework.data.convert.ThreeTenBackPortConverters.ZoneIdToStringConverter; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.threeten.bp.Instant; -import org.threeten.bp.LocalDate; -import org.threeten.bp.LocalDateTime; -import org.threeten.bp.LocalTime; -import org.threeten.bp.ZoneId; - -/** - * JPA 2.1 converters to turn ThreeTen back port types into legacy {@link Date}s. To activate these converters make sure - * your persistence provider detects them by including this class in the list of mapped classes. In Spring environments, - * you can simply register the package of this class (i.e. {@code org.springframework.data.jpa.convert.threetenbp}) as - * package to be scanned on e.g. the {@link LocalContainerEntityManagerFactoryBean}. - * - * @author Oliver Gierke - * @see https://www.threeten.org/threetenbp - * @since 1.8 - * @deprecated since 2.4, use JSR-310 types as replacement for ThreeTenBackport. - */ -public class ThreeTenBackPortJpaConverters { - - @Converter(autoApply = true) - @Deprecated - public static class LocalDateConverter implements AttributeConverter { - - @Override - public Date convertToDatabaseColumn(LocalDate date) { - return LocalDateToDateConverter.INSTANCE.convert(date); - } - - @Override - public LocalDate convertToEntityAttribute(Date date) { - return DateToLocalDateConverter.INSTANCE.convert(date); - } - } - - @Converter(autoApply = true) - @Deprecated - public static class LocalTimeConverter implements AttributeConverter { - - @Override - public Date convertToDatabaseColumn(LocalTime time) { - return LocalTimeToDateConverter.INSTANCE.convert(time); - } - - @Override - public LocalTime convertToEntityAttribute(Date date) { - return DateToLocalTimeConverter.INSTANCE.convert(date); - } - } - - @Converter(autoApply = true) - @Deprecated - public static class LocalDateTimeConverter implements AttributeConverter { - - @Override - public Date convertToDatabaseColumn(LocalDateTime date) { - return LocalDateTimeToDateConverter.INSTANCE.convert(date); - } - - @Override - public LocalDateTime convertToEntityAttribute(Date date) { - return DateToLocalDateTimeConverter.INSTANCE.convert(date); - } - } - - @Converter(autoApply = true) - @Deprecated - public static class InstantConverter implements AttributeConverter { - - @Override - public Date convertToDatabaseColumn(Instant instant) { - return InstantToDateConverter.INSTANCE.convert(instant); - } - - @Override - public Instant convertToEntityAttribute(Date date) { - return DateToInstantConverter.INSTANCE.convert(date); - } - } - - @Converter(autoApply = true) - @Deprecated - public static class ZoneIdConverter implements AttributeConverter { - - @Override - public String convertToDatabaseColumn(ZoneId zoneId) { - return ZoneIdToStringConverter.INSTANCE.convert(zoneId); - } - - @Override - public ZoneId convertToEntityAttribute(String zoneId) { - return StringToZoneIdConverter.INSTANCE.convert(zoneId); - } - } -} diff --git a/src/test/java/org/springframework/data/jpa/convert/threetenbp/DateTimeSample.java b/src/test/java/org/springframework/data/jpa/convert/threetenbp/DateTimeSample.java deleted file mode 100644 index 4826327802..0000000000 --- a/src/test/java/org/springframework/data/jpa/convert/threetenbp/DateTimeSample.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.convert.threetenbp; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; - -import org.threeten.bp.Instant; -import org.threeten.bp.LocalDate; -import org.threeten.bp.LocalDateTime; -import org.threeten.bp.LocalTime; -import org.threeten.bp.ZoneId; - -/** - * @author Oliver Gierke - */ -@Entity -public class DateTimeSample { - - @Id @GeneratedValue Long id; - - Instant instant; - LocalDate localDate; - LocalTime localTime; - LocalDateTime localDateTime; - ZoneId zoneId; -} diff --git a/src/test/java/org/springframework/data/jpa/convert/threetenbp/ThreeTenBackPortJpaConvertersIntegrationTests.java b/src/test/java/org/springframework/data/jpa/convert/threetenbp/ThreeTenBackPortJpaConvertersIntegrationTests.java deleted file mode 100644 index 93e91f6d55..0000000000 --- a/src/test/java/org/springframework/data/jpa/convert/threetenbp/ThreeTenBackPortJpaConvertersIntegrationTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2015-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.convert.threetenbp; - -import static org.assertj.core.api.Assertions.*; -import static org.junit.Assume.*; -import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; - -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.jpa.domain.support.AbstractAttributeConverterIntegrationTests; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.transaction.annotation.Transactional; -import org.threeten.bp.Instant; -import org.threeten.bp.LocalDate; -import org.threeten.bp.LocalDateTime; -import org.threeten.bp.LocalTime; -import org.threeten.bp.ZoneId; - -/** - * Integration tests for {@link ThreeTenBackPortJpaConverters}. - * - * @author Oliver Gierke - * @author Jens Schauder - * @since 1.8 - */ -@ContextConfiguration -@Transactional -public class ThreeTenBackPortJpaConvertersIntegrationTests extends AbstractAttributeConverterIntegrationTests { - - @PersistenceContext EntityManager em; - - @Test // DATAJPA-650 - void usesThreeTenBackPortJpaConverters() { - - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); - - DateTimeSample sample = new DateTimeSample(); - - sample.instant = Instant.now(); - sample.localDate = LocalDate.now(); - sample.localTime = LocalTime.now(); - sample.localDateTime = LocalDateTime.now(); - sample.zoneId = ZoneId.of("Europe/Berlin"); - - em.persist(sample); - em.flush(); - em.clear(); - - DateTimeSample result = em.find(DateTimeSample.class, sample.id); - - assertThat(result).isNotNull(); - assertThat(result.instant).isEqualTo(sample.instant); - assertThat(result.localDate).isEqualTo(sample.localDate); - assertThat(result.localTime).isEqualTo(sample.localTime); - assertThat(result.localDateTime).isEqualTo(sample.localDateTime); - assertThat(result.zoneId).isEqualTo(sample.zoneId); - } - - @Configuration - static class Config extends InfrastructureConfig { - - @Override - protected String getPackageName() { - return getClass().getPackage().getName(); - } - } -} From f95aa6c1b86bbf3323b62f8c76dc44f57f11ef10 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 21 Sep 2021 15:09:27 +0200 Subject: [PATCH 114/821] Prepare Spring Data 3.0 branch. Moves to spring-data-commons 3.0.0 Moves of deprecated and now removed AuditHandler constructor. Set source version to 16 since the AspectJ-Maven-Plugin does not support Java 17 source yet. Closes #2309 --- pom.xml | 14 ++++---------- .../repository/config/JpaAuditingRegistrar.java | 6 +++++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index c02ae0f6f1..f229c10641 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,10 +15,11 @@ org.springframework.data.build spring-data-parent - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT + 16 DATAJPA @@ -26,7 +27,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 2.7.0-SNAPSHOT + 3.0.0-SNAPSHOT 0.10.3 org.hibernate @@ -274,13 +275,6 @@ test - - org.threeten - threetenbp - ${threetenbp} - true - - io.vavr vavr diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java index 42bbd65545..9d90bde77e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java @@ -34,6 +34,7 @@ import org.springframework.data.config.ParsingUtils; import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.data.repository.config.PersistentEntitiesFactoryBean; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -71,8 +72,11 @@ protected String getAuditingHandlerBeanName() { @Override protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) { + BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class); + definition.addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME); + BeanDefinitionBuilder builder = super.getAuditHandlerBeanDefinitionBuilder(configuration); - return builder.addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME); + return builder.addConstructorArgValue(definition.getBeanDefinition()); } /* From 696c8be00aff51895c3d8456705bf299ce9d2064 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 24 Sep 2021 11:00:22 +0200 Subject: [PATCH 115/821] Move baseline build to JDK17. Closes #2312 --- Jenkinsfile | 81 ++++++++++++++++++++--------------------------------- 1 file changed, 30 insertions(+), 51 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 254514b07f..24e4496530 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,7 +3,7 @@ pipeline { triggers { pollSCM 'H/10 * * * *' - upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS) + upstream(upstreamProjects: "spring-data-commons/3.0.x", threshold: hudson.model.Result.SUCCESS) } options { @@ -12,7 +12,7 @@ pipeline { } stages { - stage("test: baseline (jdk8)") { + stage("test: baseline (jdk17)") { when { beforeAgent(true) anyOf { @@ -31,7 +31,7 @@ pipeline { steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('adoptopenjdk/openjdk8:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') { + docker.image('openjdk:17-bullseye').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') { sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}" sh 'PROFILE=all-dbs ci/test.sh' sh "ci/clean.sh" @@ -41,62 +41,43 @@ pipeline { } } - stage("Test other configurations") { + stage('Release to artifactory') { when { beforeAgent(true) - allOf { + anyOf { branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP") not { triggeredBy 'UpstreamCause' } } } - parallel { - stage("test: baseline (jdk11)") { - agent { - label 'data' - } - options { timeout(time: 30, unit: 'MINUTES') } - environment { - ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') - } - steps { - script { - docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('adoptopenjdk/openjdk11:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pjava11 clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' - } - } - } - } - } + agent { + label 'data' + } + options { timeout(time: 20, unit: 'MINUTES') } - stage("test: baseline (jdk17)") { - agent { - label 'data' - } - options { timeout(time: 30, unit: 'MINUTES') } - environment { - ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') - } - steps { - script { - docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('openjdk:17-bullseye').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pjava11 clean dependency:list test -Dsort -Dbundlor.enabled=false -U -B' - } - } + environment { + ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + } + + steps { + script { + docker.withRegistry('', 'hub.docker.com-springbuildmaster') { + docker.image('openjdk:17-bullseye').inside('-v $HOME:/tmp/jenkins-home') { + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + + '-Dartifactory.server=https://repo.spring.io ' + + "-Dartifactory.username=${ARTIFACTORY_USR} " + + "-Dartifactory.password=${ARTIFACTORY_PSW} " + + "-Dartifactory.staging-repository=libs-snapshot-local " + + "-Dartifactory.build-name=spring-data-jpa " + + "-Dartifactory.build-number=${BUILD_NUMBER} " + + '-Dmaven.test.skip=true clean deploy -U -B' } } } } } - - stage('Release to artifactory') { + stage('Publish documentation') { when { - beforeAgent(true) - anyOf { - branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP") - not { triggeredBy 'UpstreamCause' } - } + branch 'main' } agent { label 'data' @@ -110,14 +91,12 @@ pipeline { steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + + docker.image('openjdk:17-bullseye').inside('-v $HOME:/tmp/jenkins-home') { + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " + - "-Dartifactory.staging-repository=libs-snapshot-local " + - "-Dartifactory.build-name=spring-data-jpa " + - "-Dartifactory.build-number=${BUILD_NUMBER} " + + "-Dartifactory.distribution-repository=temp-private-local " + '-Dmaven.test.skip=true clean deploy -U -B' } } From bf2ae2baee497b398f4a4555dd6a73ca8df76b1d Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 24 Sep 2021 11:14:05 +0200 Subject: [PATCH 116/821] Fix JavaDoc. Closes #2314 --- pom.xml | 2 +- .../data/jpa/convert/threetenbp/package-info.java | 5 ----- .../data/jpa/repository/utils/package-info.java | 5 ----- 3 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 src/main/java/org/springframework/data/jpa/convert/threetenbp/package-info.java delete mode 100644 src/main/java/org/springframework/data/jpa/repository/utils/package-info.java diff --git a/pom.xml b/pom.xml index f229c10641..e64190f394 100644 --- a/pom.xml +++ b/pom.xml @@ -335,7 +335,7 @@ org.apache.geronimo.specs geronimo-jcdi_2.0_spec 1.0.1 - test + optional diff --git a/src/main/java/org/springframework/data/jpa/convert/threetenbp/package-info.java b/src/main/java/org/springframework/data/jpa/convert/threetenbp/package-info.java deleted file mode 100644 index be0419148c..0000000000 --- a/src/main/java/org/springframework/data/jpa/convert/threetenbp/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Spring Data JPA specific ThreeTenBp converters. - */ -@org.springframework.lang.NonNullApi -package org.springframework.data.jpa.convert.threetenbp; diff --git a/src/main/java/org/springframework/data/jpa/repository/utils/package-info.java b/src/main/java/org/springframework/data/jpa/repository/utils/package-info.java deleted file mode 100644 index a2e33f2ace..0000000000 --- a/src/main/java/org/springframework/data/jpa/repository/utils/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * JPA specific utility functions. - */ -package org.springframework.data.jpa.repository.utils; - From 4e3464fa5f89dacd47d95232e65d14a32e5b50f5 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 24 Sep 2021 11:42:28 +0200 Subject: [PATCH 117/821] Fix JavaDoc. Closes #2314 --- .../data/jpa/repository/query/JpaQueryExecution.java | 5 ++--- .../jpa/repository/query/JpaQueryLookupStrategy.java | 11 ++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 8c62807386..48cfb00b70 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -111,9 +111,8 @@ public Object execute(AbstractJpaQuery query, JpaParametersParameterAccessor acc /** * Method to implement {@link AbstractStringBasedJpaQuery} executions by single enum values. * - * @param query - * @param accessor - * @return + * @param query must not be {@literal null}. + * @param accessor must not be {@literal null}. */ @Nullable protected abstract Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 2c3ac135e6..225f917cbe 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -129,10 +129,9 @@ private static class DeclaredQueryLookupStrategy extends AbstractQueryLookupStra /** * Creates a new {@link DeclaredQueryLookupStrategy}. * - * @param em - * @param extractor - * @param queryMethodFactory - * @param evaluationContextProvider + * @param em must not be {@literal null}. + * @param queryMethodFactory must not be {@literal null}. + * @param evaluationContextProvider must not be {@literal null}. */ public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory, QueryMethodEvaluationContextProvider evaluationContextProvider) { @@ -263,9 +262,7 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, * @param queryMethodFactory must not be {@literal null}. * @param key may be {@literal null}. * @param evaluationContextProvider must not be {@literal null}. - * @param escape - * @param extractor must not be {@literal null}. - * @return + * @param escape must not be {@literal null}. */ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory queryMethodFactory, @Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider, EscapeCharacter escape) { From ece2cc5d0a7765b2a67db24fef87926f131340b8 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 28 Sep 2021 11:48:08 +0200 Subject: [PATCH 118/821] Migrate to Jakarta EE 9. Closes #2305 --- pom.xml | 62 ++++++++++++++----- src/main/asciidoc/jpa.adoc | 2 +- .../QueryByExamplePredicateBuilder.java | 20 +++--- .../convert/threeten/Jsr310JpaConverters.java | 4 +- .../data/jpa/domain/AbstractAuditable.java | 8 +-- .../data/jpa/domain/AbstractPersistable.java | 8 +-- .../data/jpa/domain/JpaSort.java | 4 +- .../data/jpa/domain/Specification.java | 8 +-- .../jpa/domain/SpecificationComposition.java | 8 +-- .../AuditingBeanFactoryPostProcessor.java | 2 +- .../support/AuditingEntityListener.java | 4 +- .../mapping/JpaMetamodelMappingContext.java | 4 +- .../jpa/mapping/JpaPersistentEntityImpl.java | 2 +- .../mapping/JpaPersistentPropertyImpl.java | 4 +- .../data/jpa/provider/JpaClassUtils.java | 4 +- .../jpa/provider/PersistenceProvider.java | 18 +++--- .../data/jpa/provider/QueryExtractor.java | 2 +- .../data/jpa/repository/EntityGraph.java | 12 ++-- .../data/jpa/repository/JpaContext.java | 2 +- .../data/jpa/repository/JpaRepository.java | 6 +- .../data/jpa/repository/Lock.java | 2 +- .../data/jpa/repository/Query.java | 4 +- .../data/jpa/repository/QueryHints.java | 2 +- .../data/jpa/repository/Temporal.java | 2 +- .../jpa/repository/cdi/JpaRepositoryBean.java | 10 +-- .../cdi/JpaRepositoryExtension.java | 14 ++--- .../config/EnableJpaRepositories.java | 2 +- ...JpaMetamodelMappingContextFactoryBean.java | 4 +- .../config/JpaRepositoryConfigExtension.java | 12 ++-- .../repository/query/AbstractJpaQuery.java | 16 ++--- .../query/AbstractStringBasedJpaQuery.java | 4 +- .../query/DefaultJpaEntityMetadata.java | 2 +- .../data/jpa/repository/query/Jpa21Utils.java | 12 ++-- .../query/JpaCountQueryCreator.java | 14 ++--- .../jpa/repository/query/JpaParameters.java | 2 +- .../jpa/repository/query/JpaQueryCreator.java | 20 +++--- .../repository/query/JpaQueryExecution.java | 8 +-- .../jpa/repository/query/JpaQueryFactory.java | 2 +- .../query/JpaQueryLookupStrategy.java | 6 +- .../jpa/repository/query/JpaQueryMethod.java | 4 +- .../data/jpa/repository/query/NamedQuery.java | 10 +-- .../jpa/repository/query/NativeJpaQuery.java | 6 +- .../jpa/repository/query/ParameterBinder.java | 2 +- .../query/ParameterBinderFactory.java | 8 +-- .../query/ParameterMetadataProvider.java | 6 +- .../repository/query/PartTreeJpaQuery.java | 10 +-- .../repository/query/ProcedureParameter.java | 4 +- .../query/QueryParameterSetter.java | 10 +-- .../query/QueryParameterSetterFactory.java | 16 ++--- .../data/jpa/repository/query/QueryUtils.java | 56 ++++++++--------- .../jpa/repository/query/SimpleJpaQuery.java | 4 +- .../query/StoredProcedureAttributeSource.java | 8 +-- .../query/StoredProcedureAttributes.java | 2 +- .../query/StoredProcedureJpaQuery.java | 10 +-- .../support/CrudMethodMetadata.java | 2 +- .../CrudMethodMetadataPostProcessor.java | 4 +- .../repository/support/DefaultJpaContext.java | 4 +- .../repository/support/DefaultQueryHints.java | 2 +- ...rBeanDefinitionRegistrarPostProcessor.java | 4 +- .../FetchableFluentQueryByExample.java | 4 +- .../support/JpaEntityInformation.java | 2 +- .../support/JpaEntityInformationSupport.java | 4 +- .../JpaMetamodelEntityInformation.java | 18 +++--- .../JpaPersistableEntityInformation.java | 2 +- .../support/JpaRepositoryFactory.java | 4 +- .../support/JpaRepositoryFactoryBean.java | 4 +- .../repository/support/MutableQueryHints.java | 4 +- .../jpa/repository/support/QueryHints.java | 10 +-- .../data/jpa/repository/support/Querydsl.java | 2 +- .../support/QuerydslJpaPredicateExecutor.java | 4 +- .../support/QuerydslJpaRepository.java | 4 +- .../support/QuerydslRepositorySupport.java | 5 +- .../support/SimpleJpaRepository.java | 30 ++++----- ...hScanningPersistenceUnitPostProcessor.java | 8 +-- .../MergingPersistenceUnitManager.java | 2 +- .../data/jpa/util/BeanDefinitionUtils.java | 2 +- .../data/jpa/util/JpaMetamodel.java | 8 +-- ...> jakarta.enterprise.inject.spi.Extension} | 0 ...eryByExamplePredicateBuilderUnitTests.java | 32 +++++----- .../jpa/convert/threeten/DateTimeSample.java | 6 +- .../Jsr310JpaConvertersIntegrationTests.java | 4 +- .../Jsr310JpaConvertersUnitTests.java | 2 +- .../data/jpa/domain/JpaSortTests.java | 6 +- .../jpa/domain/SpecificationUnitTests.java | 8 +-- .../sample/AbstractAnnotatedAuditable.java | 12 ++-- .../jpa/domain/sample/AbstractMappedType.java | 8 +-- .../data/jpa/domain/sample/Account.java | 2 +- .../data/jpa/domain/sample/Address.java | 2 +- .../domain/sample/AnnotatedAuditableUser.java | 2 +- .../data/jpa/domain/sample/AuditableRole.java | 2 +- .../data/jpa/domain/sample/AuditableUser.java | 8 +-- .../data/jpa/domain/sample/Category.java | 10 +-- .../data/jpa/domain/sample/Child.java | 8 +-- .../data/jpa/domain/sample/ConcreteType1.java | 2 +- .../data/jpa/domain/sample/ConcreteType2.java | 2 +- .../sample/CustomAbstractPersistable.java | 4 +- .../data/jpa/domain/sample/Customer.java | 4 +- .../data/jpa/domain/sample/Dummy.java | 14 ++--- .../sample/EmbeddedIdExampleDepartment.java | 4 +- .../sample/EmbeddedIdExampleEmployee.java | 10 +-- .../sample/EmbeddedIdExampleEmployeePK.java | 4 +- .../domain/sample/EntityWithAssignedId.java | 10 +-- .../sample/IdClassExampleDepartment.java | 4 +- .../domain/sample/IdClassExampleEmployee.java | 10 +-- .../data/jpa/domain/sample/Invoice.java | 8 +-- .../data/jpa/domain/sample/InvoiceItem.java | 8 +-- .../data/jpa/domain/sample/Item.java | 12 ++-- .../data/jpa/domain/sample/ItemSite.java | 10 +-- .../data/jpa/domain/sample/MailMessage.java | 10 +-- .../data/jpa/domain/sample/MailSender.java | 10 +-- .../data/jpa/domain/sample/MailUser.java | 6 +- .../data/jpa/domain/sample/Order.java | 8 +-- .../data/jpa/domain/sample/Parent.java | 10 +-- .../domain/sample/PersistableWithIdClass.java | 6 +- .../sample/PersistableWithSingleIdClass.java | 6 +- .../sample/PrimitiveVersionProperty.java | 8 +-- .../data/jpa/domain/sample/Product.java | 6 +- .../data/jpa/domain/sample/Role.java | 6 +- .../data/jpa/domain/sample/SampleEntity.java | 4 +- .../jpa/domain/sample/SampleEntityPK.java | 4 +- .../jpa/domain/sample/SampleWithIdClass.java | 10 +-- .../SampleWithIdClassIncludingEntity.java | 8 +-- .../domain/sample/SampleWithPrimitiveId.java | 4 +- .../sample/SampleWithTimestampVersion.java | 6 +- .../data/jpa/domain/sample/Site.java | 8 +-- .../data/jpa/domain/sample/SpecialUser.java | 2 +- .../data/jpa/domain/sample/User.java | 2 +- .../jpa/domain/sample/UserSpecifications.java | 8 +-- .../data/jpa/domain/sample/VersionedUser.java | 8 +-- ...actAttributeConverterIntegrationTests.java | 2 +- ...tingBeanFactoryPostProcessorUnitTests.java | 2 +- ...tingBeanFactoryPostProcessorUnitTests.java | 2 +- .../infrastructure/HibernateTestUtils.java | 2 +- .../MetamodelIntegrationTests.java | 28 ++++----- ...tamodelMappingContextIntegrationTests.java | 2 +- .../JpaMetamodelMappingContextUnitTests.java | 2 +- .../JpaPersistentPropertyImplUnitTests.java | 22 +++---- .../PersistenceProviderIntegrationTests.java | 2 +- .../PersistenceProviderUnitTests.java | 2 +- .../AbstractPersistableIntegrationTests.java | 2 +- .../CrudMethodMetadataUnitTests.java | 16 ++--- ...lipseLinkNamespaceUserRepositoryTests.java | 2 +- ...raphRepositoryMethodsIntegrationTests.java | 14 ++--- .../JavaConfigUserRepositoryTests.java | 4 +- .../MappedTypeRepositoryIntegrationTests.java | 2 +- .../OpenJpaNamespaceUserRepositoryTests.java | 14 ++--- .../ParentRepositoryIntegrationTests.java | 10 +-- .../RepositoryWithIdClassKeyTests.java | 2 +- .../SimpleJpaParameterBindingTests.java | 14 ++--- .../StoredProcedureIntegrationTests.java | 4 +- ...sitoryStoredProcedureIntegrationTests.java | 8 +-- .../jpa/repository/UserRepositoryTests.java | 14 ++--- .../cdi/CdiExtensionIntegrationTests.java | 8 +-- .../cdi/EntityManagerFactoryProducer.java | 10 +-- .../cdi/JpaRepositoryExtensionUnitTests.java | 6 +- .../data/jpa/repository/cdi/Person.java | 8 +-- .../data/jpa/repository/cdi/PersonDB.java | 2 +- .../cdi/QualifiedEntityManagerProducer.java | 8 +-- .../repository/cdi/RepositoryConsumer.java | 2 +- .../jpa/repository/cdi/Transactional.java | 2 +- .../cdi/TransactionalInterceptor.java | 14 ++--- .../cdi/UnqualifiedEntityManagerProducer.java | 8 +-- .../data/jpa/repository/cdi/UserDB.java | 2 +- ...uditingViaJavaConfigRepositoriesTests.java | 2 +- .../config/InfrastructureConfig.java | 2 +- ...RepositoriesRegistrarIntegrationTests.java | 2 +- ...JpaRepositoryConfigExtensionUnitTests.java | 4 +- .../custom/CustomGenericJpaRepository.java | 2 +- .../CustomGenericJpaRepositoryFactory.java | 4 +- ...CustomGenericJpaRepositoryFactoryBean.java | 2 +- .../MySqlStoredProcedureIntegrationTests.java | 12 ++-- ...stgresStoredProcedureIntegrationTests.java | 16 ++--- .../ProjectionJoinIntegrationTests.java | 18 +++--- .../ProjectionsIntegrationTests.java | 16 ++--- .../query/AbstractJpaQueryTests.java | 20 +++--- ...ctStringBasedJpaQueryIntegrationTests.java | 6 +- .../jpa/repository/query/Jpa21UtilsTests.java | 8 +-- .../repository/query/Jpa21UtilsUnitTests.java | 4 +- .../JpaCountQueryCreatorIntegrationTests.java | 6 +- .../query/JpaParametersUnitTests.java | 4 +- .../query/JpaQueryExecutionUnitTests.java | 6 +- .../JpaQueryLookupStrategyUnitTests.java | 6 +- .../query/JpaQueryMethodUnitTests.java | 4 +- ...rIndexedQueryParameterSetterUnitTests.java | 10 +-- .../repository/query/NamedQueryUnitTests.java | 8 +-- .../query/ParameterBinderUnitTests.java | 10 +-- .../ParameterExpressionProviderTests.java | 8 +-- ...meterMetadataProviderIntegrationTests.java | 4 +- .../ParameterMetadataProviderUnitTests.java | 2 +- .../PartTreeJpaQueryIntegrationTests.java | 8 +-- .../query/QueryUtilsIntegrationTests.java | 36 +++++------ .../query/SimpleJpaQueryUnitTests.java | 12 ++-- ...oredProcedureAttributeSourceUnitTests.java | 4 +- .../StoredProcedureAttributesUnitTests.java | 2 +- .../query/TupleConverterUnitTests.java | 4 +- .../jpa/repository/sample/RoleRepository.java | 4 +- .../jpa/repository/sample/UserRepository.java | 4 +- ...aPopulatingMethodInterceptorUnitTests.java | 2 +- .../DefaultJpaContextIntegrationTests.java | 4 +- .../support/DefaultJpaContextUnitTests.java | 2 +- .../DefaultJpaEntityMetadataUnitTest.java | 2 +- ...tTransactionDisablingIntegrationTests.java | 2 +- ...egistrarPostProcessorIntegrationTests.java | 2 +- ...nitionRegistrarPostProcessorUnitTests.java | 2 +- .../support/EntityManagerFactoryRefTests.java | 2 +- .../EntityManagerFactoryRefUnitTests.java | 2 +- .../JpaEntityInformationSupportUnitTests.java | 8 +-- ...odelEntityInformationIntegrationTests.java | 4 +- ...paMetamodelEntityInformationUnitTests.java | 8 +-- ...PersistableEntityInformationUnitTests.java | 6 +- .../JpaRepositoryFactoryBeanUnitTests.java | 4 +- .../JpaRepositoryFactoryUnitTests.java | 6 +- .../support/JpaRepositoryTests.java | 4 +- ...MailMessageRepositoryIntegrationTests.java | 4 +- .../support/QuerydslIntegrationTests.java | 4 +- ...QuerydslJpaPredicateExecutorUnitTests.java | 4 +- .../support/QuerydslJpaRepositoryTests.java | 4 +- ...ydslRepositorySupportIntegrationTests.java | 4 +- .../QuerydslRepositorySupportTests.java | 4 +- .../support/SimpleJpaRepositoryUnitTests.java | 10 +-- ...PersistenceUnitPostProcessorUnitTests.java | 2 +- .../jpa/support/EntityManagerTestUtils.java | 2 +- ...ergingPersistenceUnitManagerUnitTests.java | 2 +- ...MetamodelCacheCleanupIntegrationTests.java | 2 +- .../data/jpa/util/JpaMetamodelUnitTests.java | 4 +- src/test/resources/META-INF/persistence.xml | 8 +-- .../config/jpa-context-with-jndi.xml | 2 +- src/test/resources/eclipselink.xml | 10 +-- .../multiple-entity-manager-context.xml | 4 +- template.mf | 6 +- 230 files changed, 821 insertions(+), 786 deletions(-) rename src/main/resources/META-INF/services/{javax.enterprise.inject.spi.Extension => jakarta.enterprise.inject.spi.Extension} (100%) diff --git a/pom.xml b/pom.xml index e64190f394..4ff3ce005f 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ DATAJPA - 2.7.9 + 3.0.2 5.6.0.Final 8.0.23 42.2.19 @@ -293,30 +293,37 @@ ${hibernate.groupId} - hibernate-core + hibernate-core-jakarta ${hibernate} true ${hibernate.groupId} - hibernate-jpamodelgen + hibernate-jpamodelgen-jakarta ${hibernate} provided + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation-api} + + com.querydsl querydsl-apt ${querydsl} - jpa + jakarta provided com.querydsl querydsl-jpa + jakarta ${querydsl} true @@ -339,31 +346,58 @@ - javax.interceptor - javax.interceptor-api - 1.2.1 + jakarta.interceptor + jakarta.interceptor-api + 2.0.0 test - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api ${cdi} provided true + - javax.annotation - javax.annotation-api - ${javax-annotation-api} + org.apache.openwebbeans + openwebbeans-se + ${webbeans} + jakarta test + + + org.apache.openwebbeans + openwebbeans-impl + + + org.apache.openwebbeans + openwebbeans-spi + + - org.apache.openwebbeans - openwebbeans-se + openwebbeans-impl + ${webbeans} + jakarta + test + + + org.apache.openwebbeans + openwebbeans-spi ${webbeans} + jakarta test diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index f57182a3f3..7fd3d8bfa5 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -573,7 +573,7 @@ int setFixedFirstnameFor(String firstname, String lastname); ---- ==== -Doing so triggers the query annotated to the method as an updating query instead of a selecting one. As the `EntityManager` might contain outdated entities after the execution of the modifying query, we do not automatically clear it (see the https://docs.oracle.com/javaee/7/api/javax/persistence/EntityManager.html[JavaDoc] of `EntityManager.clear()` for details), since this effectively drops all non-flushed changes still pending in the `EntityManager`. +Doing so triggers the query annotated to the method as an updating query instead of a selecting one. As the `EntityManager` might contain outdated entities after the execution of the modifying query, we do not automatically clear it (see the https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/entitymanager[JavaDoc] of `EntityManager.clear()` for details), since this effectively drops all non-flushed changes still pending in the `EntityManager`. If you wish the `EntityManager` to be cleared automatically, you can set the `@Modifying` annotation's `clearAutomatically` attribute to `true`. The `@Modifying` annotation is only relevant in combination with the `@Query` annotation. diff --git a/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java index be9b899d80..c896e87ee2 100644 --- a/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java +++ b/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java @@ -21,16 +21,16 @@ import java.util.Optional; import java.util.Set; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.Attribute.PersistentAttributeType; -import javax.persistence.metamodel.ManagedType; -import javax.persistence.metamodel.SingularAttribute; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.From; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.Attribute.PersistentAttributeType; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.SingularAttribute; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Example; diff --git a/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java b/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java index f7cd8734e9..3ab2374ea4 100644 --- a/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java +++ b/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java @@ -22,8 +22,8 @@ import java.time.ZoneId; import java.util.Date; -import javax.persistence.AttributeConverter; -import javax.persistence.Converter; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; import org.springframework.data.convert.Jsr310Converters.DateToInstantConverter; import org.springframework.data.convert.Jsr310Converters.DateToLocalDateConverter; diff --git a/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java b/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java index b40f7ff20a..a4fa689179 100644 --- a/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java +++ b/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java @@ -21,10 +21,10 @@ import java.util.Date; import java.util.Optional; -import javax.persistence.ManyToOne; -import javax.persistence.MappedSuperclass; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; import org.springframework.data.domain.Auditable; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java b/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java index 31bd12e93e..ff4f86f443 100644 --- a/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java +++ b/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java @@ -17,10 +17,10 @@ import java.io.Serializable; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.MappedSuperclass; -import javax.persistence.Transient; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Transient; import org.springframework.data.domain.Persistable; import org.springframework.data.util.ProxyUtils; diff --git a/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index 13a10d3bee..4e131e46f9 100644 --- a/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -21,8 +21,8 @@ import java.util.Collections; import java.util.List; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.PluralAttribute; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.PluralAttribute; import org.springframework.data.domain.Sort; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/domain/Specification.java b/src/main/java/org/springframework/data/jpa/domain/Specification.java index e7a2aa8f98..15fcd9173a 100644 --- a/src/main/java/org/springframework/data/jpa/domain/Specification.java +++ b/src/main/java/org/springframework/data/jpa/domain/Specification.java @@ -17,10 +17,10 @@ import java.io.Serializable; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java b/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java index e2db0ae6df..0dc3a976a3 100644 --- a/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java +++ b/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java @@ -17,10 +17,10 @@ import java.io.Serializable; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java b/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java index 678db5b3b1..d35cfc6d5d 100644 --- a/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java @@ -26,7 +26,7 @@ /** * {@link BeanFactoryPostProcessor} that ensures that the {@link AnnotationBeanConfigurerAspect} aspect is up and - * running before the {@link javax.persistence.EntityManagerFactory} gets created as this already instantiates + * running before the {@link jakarta.persistence.EntityManagerFactory} gets created as this already instantiates * entity listeners and we need to get injection into {@link org.springframework.beans.factory.annotation.Configurable} * to work in them. * diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java index 8a57115b50..8d453ca09f 100644 --- a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java +++ b/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.domain.support; -import javax.persistence.PrePersist; -import javax.persistence.PreUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreUpdate; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Configurable; diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index 7fa79ceb88..feb9176995 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -18,8 +18,8 @@ import java.util.Set; import java.util.function.Predicate; -import javax.persistence.metamodel.ManagedType; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.Metamodel; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.util.JpaMetamodel; diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index f9d2d3033e..0d389d851e 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -41,7 +41,7 @@ class JpaPersistentEntityImpl extends BasicPersistentEntity executeQueryWithResultStream(Query jpaQuery) { @@ -148,7 +148,7 @@ public Object getIdentifierFrom(Object entity) { /* * (non-Javadoc) - * @see org.springframework.data.jpa.provider.PersistenceProvider#executeQueryWithResultStream(javax.persistence.Query) + * @see org.springframework.data.jpa.provider.PersistenceProvider#executeQueryWithResultStream(jakarta.persistence.Query) */ @Override public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { @@ -163,7 +163,7 @@ public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.QueryExtractor#extractQueryString(javax.persistence.Query) + * @see org.springframework.data.jpa.repository.query.QueryExtractor#extractQueryString(jakarta.persistence.Query) */ @Nullable @Override @@ -326,7 +326,7 @@ public boolean canExtractQuery() { */ interface Constants { - String GENERIC_JPA_ENTITY_MANAGER_INTERFACE = "javax.persistence.EntityManager"; + String GENERIC_JPA_ENTITY_MANAGER_INTERFACE = "jakarta.persistence.EntityManager"; String ECLIPSELINK_ENTITY_MANAGER_INTERFACE = "org.eclipse.persistence.jpa.JpaEntityManager"; // needed as Spring only exposes that interface via the EM proxy String HIBERNATE_ENTITY_MANAGER_INTERFACE = "org.hibernate.jpa.HibernateEntityManager"; diff --git a/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java b/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java index 169b053b56..9f6779c5c9 100644 --- a/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java +++ b/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.provider; -import javax.persistence.Query; +import jakarta.persistence.Query; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java index cbc7263189..2c319b87aa 100644 --- a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java +++ b/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java @@ -24,7 +24,7 @@ import org.springframework.data.jpa.repository.query.JpaQueryMethod; /** - * Annotation to configure the JPA 2.1 {@link javax.persistence.EntityGraph}s that should be used on repository methods. + * Annotation to configure the JPA 2.1 {@link jakarta.persistence.EntityGraph}s that should be used on repository methods. * Since 1.9 we support the definition of dynamic {@link EntityGraph}s by allowing to customize the fetch-graph via * {@link #attributePaths()} ad-hoc fetch-graph configuration. * @@ -66,7 +66,7 @@ String[] attributePaths() default {}; /** - * Enum for JPA 2.1 {@link javax.persistence.EntityGraph} types. + * Enum for JPA 2.1 {@link jakarta.persistence.EntityGraph} types. * * @author Thomas Darimont * @since 1.6 @@ -74,24 +74,24 @@ public enum EntityGraphType { /** - * When the javax.persistence.loadgraph property is used to specify an entity graph, attributes that are specified + * When the jakarta.persistence.loadgraph property is used to specify an entity graph, attributes that are specified * by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are * treated according to their specified or default FetchType. * * @see JPA 2.1 * Specification: 3.7.4.2 Load Graph Semantics */ - LOAD("javax.persistence.loadgraph"), + LOAD("jakarta.persistence.loadgraph"), /** - * When the javax.persistence.fetchgraph property is used to specify an entity graph, attributes that are specified + * When the jakarta.persistence.fetchgraph property is used to specify an entity graph, attributes that are specified * by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are * treated as FetchType.LAZY * * @see JPA 2.1 * Specification: 3.7.4.1 Fetch Graph Semantics */ - FETCH("javax.persistence.fetchgraph"); + FETCH("jakarta.persistence.fetchgraph"); private final String key; diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaContext.java b/src/main/java/org/springframework/data/jpa/repository/JpaContext.java index 30baf2df42..f40d4cab1e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/JpaContext.java +++ b/src/main/java/org/springframework/data/jpa/repository/JpaContext.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; /** * Interface for components to provide useful information about the current JPA setup within the current diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index fb5a437f8e..f8f8c0d5fe 100644 --- a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -17,7 +17,7 @@ import java.util.List; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; @@ -128,7 +128,7 @@ default void deleteInBatch(Iterable entities) { /** * Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is * implemented this is very likely to always return an instance and throw an - * {@link javax.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers + * {@link jakarta.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers * immediately. * * @param id must not be {@literal null}. @@ -142,7 +142,7 @@ default void deleteInBatch(Iterable entities) { /** * Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is * implemented this is very likely to always return an instance and throw an - * {@link javax.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers + * {@link jakarta.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers * immediately. * * @param id must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/jpa/repository/Lock.java b/src/main/java/org/springframework/data/jpa/repository/Lock.java index dcb6bd892f..0a9035d590 100644 --- a/src/main/java/org/springframework/data/jpa/repository/Lock.java +++ b/src/main/java/org/springframework/data/jpa/repository/Lock.java @@ -21,7 +21,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.persistence.LockModeType; +import jakarta.persistence.LockModeType; /** * Annotation used to specify the {@link LockModeType} to be used when executing the query. It will be evaluated when diff --git a/src/main/java/org/springframework/data/jpa/repository/Query.java b/src/main/java/org/springframework/data/jpa/repository/Query.java index a3eddd2c50..9193ceafd4 100644 --- a/src/main/java/org/springframework/data/jpa/repository/Query.java +++ b/src/main/java/org/springframework/data/jpa/repository/Query.java @@ -64,13 +64,13 @@ boolean nativeQuery() default false; /** - * The named query to be used. If not defined, a {@link javax.persistence.NamedQuery} with name of + * The named query to be used. If not defined, a {@link jakarta.persistence.NamedQuery} with name of * {@code $ domainClass}.${queryMethodName}} will be used. */ String name() default ""; /** - * Returns the name of the {@link javax.persistence.NamedQuery} to be used to execute count queries when pagination is + * Returns the name of the {@link jakarta.persistence.NamedQuery} to be used to execute count queries when pagination is * used. Will default to the named query name configured suffixed by {@code .count}. * * @see #name() diff --git a/src/main/java/org/springframework/data/jpa/repository/QueryHints.java b/src/main/java/org/springframework/data/jpa/repository/QueryHints.java index 0204a0f7b6..38447f2ae5 100644 --- a/src/main/java/org/springframework/data/jpa/repository/QueryHints.java +++ b/src/main/java/org/springframework/data/jpa/repository/QueryHints.java @@ -21,7 +21,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.persistence.QueryHint; +import jakarta.persistence.QueryHint; /** * Wrapper annotation to allow {@link QueryHint} annotations to be bound to methods. It will be evaluated when using diff --git a/src/main/java/org/springframework/data/jpa/repository/Temporal.java b/src/main/java/org/springframework/data/jpa/repository/Temporal.java index c2bbd3d996..7e8b6b22fc 100644 --- a/src/main/java/org/springframework/data/jpa/repository/Temporal.java +++ b/src/main/java/org/springframework/data/jpa/repository/Temporal.java @@ -22,7 +22,7 @@ import java.lang.annotation.Target; import java.util.Date; -import javax.persistence.TemporalType; +import jakarta.persistence.TemporalType; /** * Annotation to declare an appropriate {@code TemporalType} on query method parameters. Note that this annotation can diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index 93882db2a1..9645fe2f16 100644 --- a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -19,10 +19,10 @@ import java.util.Optional; import java.util.Set; -import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.inject.spi.Bean; -import javax.enterprise.inject.spi.BeanManager; -import javax.persistence.EntityManager; +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.persistence.EntityManager; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.repository.cdi.CdiRepositoryBean; @@ -62,7 +62,7 @@ class JpaRepositoryBean extends CdiRepositoryBean { /* * (non-Javadoc) - * @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(javax.enterprise.context.spi.CreationalContext, java.lang.Class) + * @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(jakarta.enterprise.context.spi.CreationalContext, java.lang.Class) */ @Override protected T create(CreationalContext creationalContext, Class repositoryType) { diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index 23fab9896f..01b1ed4b53 100644 --- a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -24,13 +24,13 @@ import java.util.Optional; import java.util.Set; -import javax.enterprise.event.Observes; -import javax.enterprise.inject.UnsatisfiedResolutionException; -import javax.enterprise.inject.spi.AfterBeanDiscovery; -import javax.enterprise.inject.spi.Bean; -import javax.enterprise.inject.spi.BeanManager; -import javax.enterprise.inject.spi.ProcessBean; -import javax.persistence.EntityManager; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.UnsatisfiedResolutionException; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.enterprise.inject.spi.ProcessBean; +import jakarta.persistence.EntityManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java b/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java index 7353152524..80347540bc 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.context.annotation.ComponentScan.Filter; diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index d7f69b8612..8afdf1fc90 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -18,8 +18,8 @@ import java.util.Collection; import java.util.Set; -import javax.persistence.EntityManagerFactory; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.metamodel.Metamodel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 2de91cdde6..b92348c052 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -26,10 +26,10 @@ import java.util.Optional; import java.util.Set; -import javax.persistence.Entity; -import javax.persistence.MappedSuperclass; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceUnit; +import jakarta.persistence.Entity; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceUnit; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -242,8 +242,8 @@ protected ClassLoader getConfigurationInspectionClassLoader(ResourceLoader loade } /** - * Creates an anonymous factory to extract the actual {@link javax.persistence.EntityManager} from the - * {@link javax.persistence.EntityManagerFactory} bean name reference. + * Creates an anonymous factory to extract the actual {@link jakarta.persistence.EntityManager} from the + * {@link jakarta.persistence.EntityManagerFactory} bean name reference. * * @param config * @param source diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index f072a8b939..52af0614a5 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -23,13 +23,13 @@ import java.util.Set; import java.util.stream.Collectors; -import javax.persistence.EntityManager; -import javax.persistence.LockModeType; -import javax.persistence.Query; -import javax.persistence.QueryHint; -import javax.persistence.Tuple; -import javax.persistence.TupleElement; -import javax.persistence.TypedQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; +import jakarta.persistence.Query; +import jakarta.persistence.QueryHint; +import jakarta.persistence.Tuple; +import jakarta.persistence.TupleElement; +import jakarta.persistence.TypedQuery; import org.springframework.core.convert.converter.Converter; import org.springframework.data.jpa.provider.PersistenceProvider; @@ -228,7 +228,7 @@ protected Query createQuery(JpaParametersParameterAccessor parameters) { } /** - * Configures the {@link javax.persistence.EntityGraph} to use for the given {@link JpaQueryMethod} if the + * Configures the {@link jakarta.persistence.EntityGraph} to use for the given {@link JpaQueryMethod} if the * {@link EntityGraph} annotation is present. * * @param query must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index d8b19a925f..5aaa71c0f6 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.repository.query; -import javax.persistence.EntityManager; -import javax.persistence.Query; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ResultProcessor; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java b/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java index 2778d8960a..f469b17ee3 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import javax.persistence.Entity; +import jakarta.persistence.Entity; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.Assert; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java index 046d6a5be5..a86a362e6a 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java @@ -20,11 +20,11 @@ import java.util.Collections; import java.util.List; -import javax.persistence.AttributeNode; -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.Query; -import javax.persistence.Subgraph; +import jakarta.persistence.AttributeNode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.Subgraph; import org.springframework.data.jpa.repository.support.MutableQueryHints; import org.springframework.data.jpa.repository.support.QueryHints; @@ -48,7 +48,7 @@ public class Jpa21Utils { private static final @Nullable Method GET_ENTITY_GRAPH_METHOD; - private static final boolean JPA21_AVAILABLE = ClassUtils.isPresent("javax.persistence.NamedEntityGraph", + private static final boolean JPA21_AVAILABLE = ClassUtils.isPresent("jakarta.persistence.NamedEntityGraph", Jpa21Utils.class.getClassLoader()); static { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index d02c1ff1f7..7105d349bb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.repository.query; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.ReturnedType; @@ -50,7 +50,7 @@ public JpaCountQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder bu /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryCreator#createCriteriaQuery(javax.persistence.criteria.CriteriaBuilder, org.springframework.data.repository.query.ReturnedType) + * @see org.springframework.data.jpa.repository.query.JpaQueryCreator#createCriteriaQuery(jakarta.persistence.criteria.CriteriaBuilder, org.springframework.data.repository.query.ReturnedType) */ @Override protected CriteriaQuery createCriteriaQuery(CriteriaBuilder builder, ReturnedType type) { @@ -59,7 +59,7 @@ protected CriteriaQuery createCriteriaQuery(CriteriaBuilder bu /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryCreator#complete(javax.persistence.criteria.Predicate, org.springframework.data.domain.Sort, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder, javax.persistence.criteria.Root) + * @see org.springframework.data.jpa.repository.query.JpaQueryCreator#complete(jakarta.persistence.criteria.Predicate, org.springframework.data.domain.Sort, jakarta.persistence.criteria.CriteriaQuery, jakarta.persistence.criteria.CriteriaBuilder, jakarta.persistence.criteria.Root) */ @Override @SuppressWarnings("unchecked") diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index b3578121d3..cdb59d8833 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -19,7 +19,7 @@ import java.util.Date; import java.util.List; -import javax.persistence.TemporalType; +import jakarta.persistence.TemporalType; import org.springframework.core.MethodParameter; import org.springframework.data.jpa.repository.Temporal; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 3eb0762eef..dcf06f9d91 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -24,15 +24,15 @@ import java.util.List; import java.util.stream.Collectors; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.ParameterExpression; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.persistence.criteria.Selection; -import javax.persistence.metamodel.SingularAttribute; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.ParameterExpression; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Selection; +import jakarta.persistence.metamodel.SingularAttribute; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; @@ -106,7 +106,7 @@ protected CriteriaQuery createCriteriaQuery(CriteriaBuilder bu } /** - * Returns all {@link javax.persistence.criteria.ParameterExpression} created when creating the query. + * Returns all {@link jakarta.persistence.criteria.ParameterExpression} created when creating the query. * * @return the parameterExpressions */ diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 48cfb00b70..dfb98ff139 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -20,10 +20,10 @@ import java.util.List; import java.util.Optional; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.Query; -import javax.persistence.StoredProcedureQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NoResultException; +import jakarta.persistence.Query; +import jakarta.persistence.StoredProcedureQuery; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.ConfigurableConversionService; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 00232af63f..a66d0c76f5 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 225f917cbe..12d61cb9fc 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -17,7 +17,7 @@ import java.lang.reflect.Method; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -143,7 +143,7 @@ public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory query /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy.AbstractQueryLookupStrategy#resolveQuery(org.springframework.data.jpa.repository.query.JpaQueryMethod, javax.persistence.EntityManager, org.springframework.data.repository.core.NamedQueries) + * @see org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy.AbstractQueryLookupStrategy#resolveQuery(org.springframework.data.jpa.repository.query.JpaQueryMethod, jakarta.persistence.EntityManager, org.springframework.data.repository.core.NamedQueries) */ @Override protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { @@ -242,7 +242,7 @@ public CreateIfNotFoundQueryLookupStrategy(EntityManager em, JpaQueryMethodFacto /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy.AbstractQueryLookupStrategy#resolveQuery(org.springframework.data.jpa.repository.query.JpaQueryMethod, javax.persistence.EntityManager, org.springframework.data.repository.core.NamedQueries) + * @see org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy.AbstractQueryLookupStrategy#resolveQuery(org.springframework.data.jpa.repository.query.JpaQueryMethod, jakarta.persistence.EntityManager, org.springframework.data.repository.core.NamedQueries) */ @Override protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index f8eaebb5d6..63f6a546e3 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -24,8 +24,8 @@ import java.util.Optional; import java.util.Set; -import javax.persistence.LockModeType; -import javax.persistence.QueryHint; +import jakarta.persistence.LockModeType; +import jakarta.persistence.QueryHint; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index f9a5816712..7754d62cbb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.repository.query; -import javax.persistence.EntityManager; -import javax.persistence.Query; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -31,7 +31,7 @@ import org.springframework.lang.Nullable; /** - * Implementation of {@link RepositoryQuery} based on {@link javax.persistence.NamedQuery}s. + * Implementation of {@link RepositoryQuery} based on {@link jakarta.persistence.NamedQuery}s. * * @author Oliver Gierke * @author Thomas Darimont diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index b31753f2a7..6cbc0ae874 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -15,9 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import javax.persistence.EntityManager; -import javax.persistence.Query; -import javax.persistence.Tuple; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.Tuple; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 5132797ad6..bac36d1f3b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import javax.persistence.Query; +import jakarta.persistence.Query; import org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling; import org.springframework.util.Assert; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java index de24ab188c..f3e6ccd1ee 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java @@ -40,7 +40,7 @@ class ParameterBinderFactory { * * @param parameters method parameters that are available for binding, must not be {@literal null}. * @return a {@link ParameterBinder} that can assign values for the method parameters to query parameters of a - * {@link javax.persistence.Query} + * {@link jakarta.persistence.Query} */ static ParameterBinder createBinder(JpaParameters parameters) { @@ -54,12 +54,12 @@ static ParameterBinder createBinder(JpaParameters parameters) { /** * Creates a {@link ParameterBinder} that just matches method parameter to parameters of a - * {@link javax.persistence.criteria.CriteriaQuery}. + * {@link jakarta.persistence.criteria.CriteriaQuery}. * * @param parameters method parameters that are available for binding, must not be {@literal null}. * @param metadata must not be {@literal null}. * @return a {@link ParameterBinder} that can assign values for the method parameters to query parameters of a - * {@link javax.persistence.criteria.CriteriaQuery} + * {@link jakarta.persistence.criteria.CriteriaQuery} */ static ParameterBinder createCriteriaBinder(JpaParameters parameters, List> metadata) { @@ -82,7 +82,7 @@ static ParameterBinder createCriteriaBinder(JpaParameters parameters, List metada } } - private static class ParameterImpl implements javax.persistence.Parameter { + private static class ParameterImpl implements jakarta.persistence.Parameter { private final Class parameterType; private final @Nullable String name; @@ -336,9 +336,9 @@ private static class ParameterImpl implements javax.persistence.Parameter * * @param parameter can be {@literal null}. * @param binding must not be {@literal null}. - * @return a {@link javax.persistence.Parameter} object based on the information from the arguments. + * @return a {@link jakarta.persistence.Parameter} object based on the information from the arguments. */ - static javax.persistence.Parameter of(@Nullable JpaParameter parameter, ParameterBinding binding) { + static jakarta.persistence.Parameter of(@Nullable JpaParameter parameter, ParameterBinding binding) { Class type = parameter == null ? Object.class : parameter.getType(); @@ -361,7 +361,7 @@ private ParameterImpl(Class parameterType, @Nullable String name, @Nullable I /* * (non-Javadoc) - * @see javax.persistence.Parameter#getName() + * @see jakarta.persistence.Parameter#getName() */ @Nullable @Override @@ -371,7 +371,7 @@ public String getName() { /* * (non-Javadoc) - * @see javax.persistence.Parameter#getPosition() + * @see jakarta.persistence.Parameter#getPosition() */ @Nullable @Override @@ -381,7 +381,7 @@ public Integer getPosition() { /* * (non-Javadoc) - * @see javax.persistence.Parameter#getParameterType() + * @see jakarta.persistence.Parameter#getParameterType() */ @Override public Class getParameterType() { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index e24a871ceb..c9010d9d74 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -15,8 +15,26 @@ */ package org.springframework.data.jpa.repository.query; +import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.*; import static java.util.regex.Pattern.*; -import static javax.persistence.metamodel.Attribute.PersistentAttributeType.*; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Parameter; +import jakarta.persistence.Query; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Fetch; +import jakarta.persistence.criteria.From; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.Attribute.PersistentAttributeType; +import jakarta.persistence.metamodel.Bindable; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.PluralAttribute; +import jakarta.persistence.metamodel.SingularAttribute; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; @@ -26,24 +44,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.persistence.EntityManager; -import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; -import javax.persistence.Parameter; -import javax.persistence.Query; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.Fetch; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.Attribute.PersistentAttributeType; -import javax.persistence.metamodel.Bindable; -import javax.persistence.metamodel.ManagedType; -import javax.persistence.metamodel.PluralAttribute; -import javax.persistence.metamodel.SingularAttribute; - import org.springframework.core.annotation.AnnotationUtils; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; @@ -540,14 +540,14 @@ static boolean hasNamedParameter(@Nullable String query) { } /** - * Turns the given {@link Sort} into {@link javax.persistence.criteria.Order}s. + * Turns the given {@link Sort} into {@link jakarta.persistence.criteria.Order}s. * - * @param sort the {@link Sort} instance to be transformed into JPA {@link javax.persistence.criteria.Order}s. + * @param sort the {@link Sort} instance to be transformed into JPA {@link jakarta.persistence.criteria.Order}s. * @param from must not be {@literal null}. * @param cb must not be {@literal null}. - * @return a {@link List} of {@link javax.persistence.criteria.Order}s. + * @return a {@link List} of {@link jakarta.persistence.criteria.Order}s. */ - public static List toOrders(Sort sort, From from, CriteriaBuilder cb) { + public static List toOrders(Sort sort, From from, CriteriaBuilder cb) { if (sort.isUnsorted()) { return Collections.emptyList(); @@ -556,7 +556,7 @@ public static List toOrders(Sort sort, From orders = new ArrayList<>(); + List orders = new ArrayList<>(); for (org.springframework.data.domain.Sort.Order order : sort) { orders.add(toJpaOrder(order, from, cb)); @@ -596,15 +596,15 @@ public static String getProjection(String query) { } /** - * Creates a criteria API {@link javax.persistence.criteria.Order} from the given {@link Order}. + * Creates a criteria API {@link jakarta.persistence.criteria.Order} from the given {@link Order}. * - * @param order the order to transform into a JPA {@link javax.persistence.criteria.Order} + * @param order the order to transform into a JPA {@link jakarta.persistence.criteria.Order} * @param from the {@link From} the {@link Order} expression is based on - * @param cb the {@link CriteriaBuilder} to build the {@link javax.persistence.criteria.Order} with + * @param cb the {@link CriteriaBuilder} to build the {@link jakarta.persistence.criteria.Order} with * @return Guaranteed to be not {@literal null}. */ @SuppressWarnings("unchecked") - private static javax.persistence.criteria.Order toJpaOrder(Order order, From from, CriteriaBuilder cb) { + private static jakarta.persistence.criteria.Order toJpaOrder(Order order, From from, CriteriaBuilder cb) { PropertyPath property = PropertyPath.from(order.getProperty(), from.getJavaType()); Expression expression = toExpressionRecursively(from, property); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index 9fcff18664..4acacfad41 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.repository.query; -import javax.persistence.EntityManager; -import javax.persistence.Query; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java index 010ceb38fd..e560209df5 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java @@ -22,10 +22,10 @@ import java.util.List; import java.util.stream.Collectors; -import javax.persistence.NamedStoredProcedureQueries; -import javax.persistence.NamedStoredProcedureQuery; -import javax.persistence.ParameterMode; -import javax.persistence.StoredProcedureParameter; +import jakarta.persistence.NamedStoredProcedureQueries; +import jakarta.persistence.NamedStoredProcedureQuery; +import jakarta.persistence.ParameterMode; +import jakarta.persistence.StoredProcedureParameter; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java index fc89d2dc5e..077a7f8501 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java @@ -20,7 +20,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import javax.persistence.StoredProcedureQuery; +import jakarta.persistence.StoredProcedureQuery; import org.springframework.util.Assert; import org.springframework.util.StringUtils; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index d8f276c0dc..7927b1e8c8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -19,11 +19,11 @@ import java.util.List; import java.util.Map; -import javax.persistence.EntityManager; -import javax.persistence.NamedStoredProcedureQuery; -import javax.persistence.ParameterMode; -import javax.persistence.StoredProcedureQuery; -import javax.persistence.TypedQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NamedStoredProcedureQuery; +import jakarta.persistence.ParameterMode; +import jakarta.persistence.StoredProcedureQuery; +import jakarta.persistence.TypedQuery; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.Parameter; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java index 8466195ffd..3933c51815 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java @@ -18,7 +18,7 @@ import java.lang.reflect.Method; import java.util.Optional; -import javax.persistence.LockModeType; +import jakarta.persistence.LockModeType; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 28f7c9a9d4..d9bc85ca57 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -23,8 +23,8 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Predicate; -import javax.persistence.LockModeType; -import javax.persistence.QueryHint; +import jakarta.persistence.LockModeType; +import jakarta.persistence.QueryHint; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java b/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java index 5133d962e0..53331763fe 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java @@ -18,8 +18,8 @@ import java.util.List; import java.util.Set; -import javax.persistence.EntityManager; -import javax.persistence.metamodel.ManagedType; +import jakarta.persistence.EntityManager; +import jakarta.persistence.metamodel.ManagedType; import org.springframework.data.jpa.repository.JpaContext; import org.springframework.util.Assert; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java b/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java index 7545611977..2589bcb326 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java @@ -18,7 +18,7 @@ import java.util.Optional; import java.util.function.BiConsumer; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.query.Jpa21Utils; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index f86ac330bb..35e9c75b86 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -17,8 +17,8 @@ import static org.springframework.data.jpa.util.BeanDefinitionUtils.*; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index cb2645bfc1..a70f846c47 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -22,8 +22,8 @@ import java.util.function.Function; import java.util.stream.Stream; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Example; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java index 46504b3dee..75bdb5fa80 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.support; -import javax.persistence.metamodel.SingularAttribute; +import jakarta.persistence.metamodel.SingularAttribute; import org.springframework.data.jpa.repository.query.JpaEntityMetadata; import org.springframework.data.repository.core.EntityInformation; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java index ecad195196..d7532f1c25 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.repository.support; -import javax.persistence.EntityManager; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManager; +import jakarta.persistence.metamodel.Metamodel; import org.springframework.data.domain.Persistable; import org.springframework.data.jpa.repository.query.DefaultJpaEntityMetadata; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index fe344c8fd5..6717ad32d5 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -22,15 +22,15 @@ import java.util.Optional; import java.util.Set; -import javax.persistence.IdClass; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.EntityType; -import javax.persistence.metamodel.IdentifiableType; -import javax.persistence.metamodel.ManagedType; -import javax.persistence.metamodel.Metamodel; -import javax.persistence.metamodel.SingularAttribute; -import javax.persistence.metamodel.Type; -import javax.persistence.metamodel.Type.PersistenceType; +import jakarta.persistence.IdClass; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.IdentifiableType; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.SingularAttribute; +import jakarta.persistence.metamodel.Type; +import jakarta.persistence.metamodel.Type.PersistenceType; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java index 1fcae07074..562961c777 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.support; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.Metamodel; import org.springframework.data.domain.Persistable; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 3cd9ec88df..7fe9b0115c 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -22,8 +22,8 @@ import java.util.Optional; import java.util.stream.Stream; -import javax.persistence.EntityManager; -import javax.persistence.Tuple; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index 9bb247f255..b33ace36a8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.repository.support; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java b/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java index d55929b22f..0bf32a19db 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java @@ -19,7 +19,7 @@ import java.util.Map; import java.util.function.BiConsumer; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -39,7 +39,7 @@ public class MutableQueryHints implements QueryHints { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#withFetchGraphs(javax.persistence.EntityManager) + * @see org.springframework.data.jpa.repository.support.QueryHints#withFetchGraphs(jakarta.persistence.EntityManager) */ @Override public QueryHints withFetchGraphs(EntityManager em) { diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java b/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java index b18098c8ee..223c8a0f56 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java @@ -17,13 +17,13 @@ import java.util.function.BiConsumer; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.util.Assert; /** * QueryHints provides access to query hints defined via {@link CrudMethodMetadata#getQueryHints()} QueryHintList()} by - * default excluding JPA {@link javax.persistence.EntityGraph}. The object allows to switch between query hints for + * default excluding JPA {@link jakarta.persistence.EntityGraph}. The object allows to switch between query hints for * count queries with or without fetch graph hints. * * @author Christoph Strobl @@ -54,7 +54,7 @@ static QueryHints from(QueryHints... sources) { } /** - * Creates and returns a new {@link QueryHints} instance including {@link javax.persistence.EntityGraph}. + * Creates and returns a new {@link QueryHints} instance including {@link jakarta.persistence.EntityGraph}. * * @param em must not be {@literal null}. * @return new instance of {@link QueryHints}. @@ -94,7 +94,7 @@ enum NoHints implements QueryHints { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#withFetchGraphs(javax.persistence.EntityManager) + * @see org.springframework.data.jpa.repository.support.QueryHints#withFetchGraphs(jakarta.persistence.EntityManager) */ @Override public QueryHints withFetchGraphs(EntityManager em) { @@ -103,7 +103,7 @@ public QueryHints withFetchGraphs(EntityManager em) { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#forCounts(javax.persistence.EntityManager) + * @see org.springframework.data.jpa.repository.support.QueryHints#forCounts(jakarta.persistence.EntityManager) */ @Override public QueryHints forCounts() { diff --git a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 484827c58c..cb19fd8e8b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -17,7 +17,7 @@ import java.util.List; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index f6e63d1bc3..0ccefd508d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -20,8 +20,8 @@ import java.util.function.BiFunction; import java.util.function.Function; -import javax.persistence.EntityManager; -import javax.persistence.LockModeType; +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index 843a287f67..7570675340 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -20,8 +20,8 @@ import java.util.Optional; import java.util.function.Function; -import javax.persistence.EntityManager; -import javax.persistence.LockModeType; +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java index a326470ee4..a4b45ff2c3 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java @@ -15,8 +15,9 @@ */ package org.springframework.data.jpa.repository.support; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.Nullable; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 0fbc14f20e..fd412f8ebc 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -26,19 +26,19 @@ import java.util.Optional; import java.util.function.Function; -import javax.persistence.EntityManager; -import javax.persistence.LockModeType; -import javax.persistence.NoResultException; -import javax.persistence.Parameter; -import javax.persistence.Query; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.ParameterExpression; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; +import jakarta.persistence.NoResultException; +import jakarta.persistence.Parameter; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Order; +import jakarta.persistence.criteria.ParameterExpression; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Example; @@ -931,7 +931,7 @@ private static final class ByIdsSpecification implements Specification { /* * (non-Javadoc) - * @see org.springframework.data.jpa.domain.Specification#toPredicate(javax.persistence.criteria.Root, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder) + * @see org.springframework.data.jpa.domain.Specification#toPredicate(jakarta.persistence.criteria.Root, jakarta.persistence.criteria.CriteriaQuery, jakarta.persistence.criteria.CriteriaBuilder) */ @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { @@ -974,7 +974,7 @@ private static class ExampleSpecification implements Specification { /* * (non-Javadoc) - * @see org.springframework.data.jpa.domain.Specification#toPredicate(javax.persistence.criteria.Root, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder) + * @see org.springframework.data.jpa.domain.Specification#toPredicate(jakarta.persistence.criteria.Root, jakarta.persistence.criteria.CriteriaQuery, jakarta.persistence.criteria.CriteriaBuilder) */ @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { diff --git a/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java b/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java index 8d471f692d..85e8aff1e2 100644 --- a/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java @@ -21,8 +21,8 @@ import java.util.HashSet; import java.util.Set; -import javax.persistence.Entity; -import javax.persistence.MappedSuperclass; +import jakarta.persistence.Entity; +import jakarta.persistence.MappedSuperclass; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -47,7 +47,7 @@ /** * {@link PersistenceUnitPostProcessor} that will scan for classes annotated with {@link Entity} or - * {@link MappedSuperclass} and add them to the {@link javax.persistence.PersistenceUnit} post processed. Beyond that + * {@link MappedSuperclass} and add them to the {@link jakarta.persistence.PersistenceUnit} post processed. Beyond that * JPA XML mapping files can be scanned as well by configuring a file name pattern. * * @author Oliver Gierke @@ -153,7 +153,7 @@ public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { * an empty {@link Set} in case no {@link ResourceLoader} or mapping file name pattern was configured. Resulting paths * are resource-loadable from the application classpath according to the JPA spec. * - * @see javax.persistence.spi.PersistenceUnitInfo#getMappingFileNames() + * @see jakarta.persistence.spi.PersistenceUnitInfo#getMappingFileNames() * @return */ private Set scanForMappingFileLocations() { diff --git a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index f6877b8a1d..c51f385673 100644 --- a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -18,7 +18,7 @@ import java.net.URISyntaxException; import java.net.URL; -import javax.persistence.spi.PersistenceUnitInfo; +import jakarta.persistence.spi.PersistenceUnitInfo; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java index 448cb889fb..63c1f03e00 100644 --- a/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java +++ b/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java @@ -25,7 +25,7 @@ import java.util.List; import java.util.Set; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; diff --git a/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java b/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java index e3e025fc2a..8331db055c 100644 --- a/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java +++ b/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java @@ -20,10 +20,10 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import javax.persistence.metamodel.EntityType; -import javax.persistence.metamodel.ManagedType; -import javax.persistence.metamodel.Metamodel; -import javax.persistence.metamodel.SingularAttribute; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.SingularAttribute; import org.springframework.data.util.Lazy; import org.springframework.data.util.StreamUtils; diff --git a/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension similarity index 100% rename from src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension rename to src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension diff --git a/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java b/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java index 47c27a516d..bb906d07c9 100644 --- a/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java @@ -24,18 +24,18 @@ import java.util.LinkedHashSet; import java.util.Set; -import javax.persistence.Id; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.persistence.metamodel.Attribute.PersistentAttributeType; -import javax.persistence.metamodel.EntityType; -import javax.persistence.metamodel.ManagedType; -import javax.persistence.metamodel.SingularAttribute; -import javax.persistence.metamodel.Type; +import jakarta.persistence.Id; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.Attribute.PersistentAttributeType; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.SingularAttribute; +import jakarta.persistence.metamodel.Type; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -307,12 +307,12 @@ static class SingularAttributeStub implements SingularAttribute { private Type type; SingularAttributeStub(String name, - javax.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class javaType) { + jakarta.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class javaType) { this(name, attributeType, javaType, null); } SingularAttributeStub(String name, - javax.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class javaType, Type type) { + jakarta.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class javaType, Type type) { this.name = name; this.attributeType = attributeType; this.javaType = javaType; @@ -325,7 +325,7 @@ public String getName() { } @Override - public javax.persistence.metamodel.Attribute.PersistentAttributeType getPersistentAttributeType() { + public jakarta.persistence.metamodel.Attribute.PersistentAttributeType getPersistentAttributeType() { return attributeType; } @@ -356,7 +356,7 @@ public boolean isCollection() { } @Override - public javax.persistence.metamodel.Bindable.BindableType getBindableType() { + public jakarta.persistence.metamodel.Bindable.BindableType getBindableType() { return BindableType.SINGULAR_ATTRIBUTE; } diff --git a/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java b/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java index bd6517fd13..c72d859eb5 100644 --- a/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java +++ b/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java @@ -21,9 +21,9 @@ import java.time.LocalTime; import java.time.ZoneId; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java b/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java index c68bb2decc..494284c806 100644 --- a/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java @@ -26,8 +26,8 @@ import java.time.ZoneId; import java.time.temporal.ChronoUnit; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Configuration; diff --git a/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java b/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java index d236a54ca5..0d22dc2dbd 100644 --- a/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java @@ -19,7 +19,7 @@ import java.util.Arrays; -import javax.persistence.AttributeConverter; +import jakarta.persistence.AttributeConverter; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java b/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java index bd63a19900..54d3015d8d 100644 --- a/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java @@ -19,9 +19,9 @@ import static org.springframework.data.domain.Sort.Direction.*; import static org.springframework.data.jpa.domain.JpaSort.*; -import javax.persistence.EntityManagerFactory; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.PluralAttribute; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.PluralAttribute; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java b/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java index dd82eed6e4..2c903de6e4 100644 --- a/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java @@ -23,10 +23,10 @@ import java.io.Serializable; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java b/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java index 3492a90c08..c6727e55b3 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java @@ -17,12 +17,12 @@ import java.util.Date; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.ManyToOne; -import javax.persistence.MappedSuperclass; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java b/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java index 28f53eb2c6..97f219b42e 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java @@ -17,10 +17,10 @@ import lombok.Getter; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.MappedSuperclass; -import javax.persistence.Version; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Version; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Account.java b/src/test/java/org/springframework/data/jpa/domain/sample/Account.java index be45b4ccf1..260d1f2999 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Account.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Account.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; +import jakarta.persistence.Entity; import org.springframework.data.jpa.domain.AbstractPersistable; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Address.java b/src/test/java/org/springframework/data/jpa/domain/sample/Address.java index 6ca54e0fd2..218bc4da99 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Address.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Address.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Embeddable; +import jakarta.persistence.Embeddable; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java b/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java index 699695589d..aabebee038 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; +import jakarta.persistence.Entity; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java b/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java index a60f31f163..278f7ebd6f 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; +import jakarta.persistence.Entity; import org.springframework.data.jpa.domain.AbstractAuditable; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java b/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java index b048ba1425..1f387058db 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java @@ -18,10 +18,10 @@ import java.util.HashSet; import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.ManyToMany; -import javax.persistence.NamedQuery; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.NamedQuery; import org.springframework.data.jpa.domain.AbstractAuditable; import org.springframework.lang.Nullable; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Category.java b/src/test/java/org/springframework/data/jpa/domain/sample/Category.java index 96230ebbe1..ea7e7fcc26 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Category.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Category.java @@ -1,10 +1,10 @@ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.ManyToOne; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; @Entity public class Category { diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Child.java b/src/test/java/org/springframework/data/jpa/domain/sample/Child.java index ace64482fd..b241f4c62d 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Child.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Child.java @@ -18,10 +18,10 @@ import java.util.HashSet; import java.util.Set; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.ManyToMany; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; @Entity public class Child { diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java b/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java index 7d13f508be..0cc846b0ef 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; +import jakarta.persistence.Entity; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java b/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java index f5f219d13c..4ad450e502 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; +import jakarta.persistence.Entity; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java b/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java index bd2eb7cd32..bb1991cac6 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import org.springframework.data.jpa.domain.AbstractPersistable; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java b/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java index 4db1982f43..d1cca062ec 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java b/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java index 3b7cd734df..58171bb36d 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java @@ -15,13 +15,13 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.NamedStoredProcedureQueries; -import javax.persistence.NamedStoredProcedureQuery; -import javax.persistence.ParameterMode; -import javax.persistence.StoredProcedureParameter; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.NamedStoredProcedureQueries; +import jakarta.persistence.NamedStoredProcedureQuery; +import jakarta.persistence.ParameterMode; +import jakarta.persistence.StoredProcedureParameter; import org.springframework.util.ObjectUtils; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java b/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java index cbe11fe321..817e87c969 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java b/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java index 36dd054a60..fd661477a6 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.CascadeType; -import javax.persistence.EmbeddedId; -import javax.persistence.Entity; -import javax.persistence.ManyToOne; -import javax.persistence.MapsId; +import jakarta.persistence.CascadeType; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java b/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java index 3cea9941a7..4aa52ae03f 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java @@ -17,8 +17,8 @@ import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Embeddable; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java b/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java index acd1f5843f..181f4fc63c 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java @@ -17,11 +17,11 @@ import java.util.UUID; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.PostLoad; -import javax.persistence.PrePersist; -import javax.persistence.Transient; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.PostLoad; +import jakarta.persistence.PrePersist; +import jakarta.persistence.Transient; import org.springframework.data.domain.Persistable; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java b/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java index ddc4f339f5..e9e34b9408 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java b/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java index 18a25486c8..a20587a4a8 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; -import javax.persistence.ManyToOne; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.ManyToOne; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java b/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java index eceed11d99..8015e09f2b 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.ManyToOne; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; /** * @author Patrice Blanchardie diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java b/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java index 21b6cd10e5..7c3c93f384 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.ManyToOne; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; /** * @author Patrice Blanchardie diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Item.java b/src/test/java/org/springframework/data/jpa/domain/sample/Item.java index d6dcf067ba..dc50b4396e 100755 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Item.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Item.java @@ -15,12 +15,12 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; -import javax.persistence.JoinColumn; -import javax.persistence.Table; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Table; /** * @author Mark Paluch diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java b/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java index 932c96bf11..79436f221d 100755 --- a/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; -import javax.persistence.ManyToOne; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; /** * @author Mark Paluch diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java b/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java index 1f033fd2c0..25688a7a27 100755 --- a/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.OneToOne; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java b/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java index 8f7ad77a77..fecf304ec9 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.ManyToOne; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; /** * @author Thomas Darimont diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java b/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java index 76aba8f4d3..00f68fd7fc 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java @@ -15,9 +15,9 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; /** * Represents a user in a Mail-System. diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Order.java b/src/test/java/org/springframework/data/jpa/domain/sample/Order.java index 9e87d1d583..18bc41b148 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Order.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Order.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.ManyToOne; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java b/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java index bac8c106cc..ebe99ad1cc 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java @@ -18,11 +18,11 @@ import java.util.HashSet; import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.ManyToMany; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; @Entity public class Parent { diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java b/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java index c10e88b262..9c3292a50b 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java @@ -15,9 +15,9 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; import org.springframework.data.domain.Persistable; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java b/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java index c1531ce0bd..48f3e569f6 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java @@ -15,9 +15,9 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; /** * Sample entity using {@link IdClass} annotation to demarcate ids. diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java b/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java index 6eac680b31..96f4936b56 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Version; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Version; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Product.java b/src/test/java/org/springframework/data/jpa/domain/sample/Product.java index d63e78dfbe..304d4d52fd 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Product.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Product.java @@ -1,8 +1,8 @@ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; @Entity public class Product { diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Role.java b/src/test/java/org/springframework/data/jpa/domain/sample/Role.java index 4a60840dc0..4e88344349 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Role.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Role.java @@ -15,9 +15,9 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; /** * Sample domain class representing roles. Mapped with XML. diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java b/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java index 43ce41a8e2..a64dc68c7f 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.EmbeddedId; -import javax.persistence.Entity; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java b/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java index 62423ab89c..14ac29e774 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java @@ -17,8 +17,8 @@ import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Embeddable; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; import org.springframework.util.Assert; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java b/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java index a1af2fdfcf..b8678b8f30 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java @@ -2,11 +2,11 @@ import java.io.Serializable; -import javax.persistence.Access; -import javax.persistence.AccessType; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; @Entity @IdClass(SampleWithIdClass.SampleWithIdClassPK.class) diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java b/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java index c50f4b24dc..4dc08b50cf 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java @@ -4,10 +4,10 @@ import java.io.Serializable; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; -import javax.persistence.ManyToOne; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.ManyToOne; /** * Sample class for integration testing diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java b/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java index c8dd184314..de6877e77a 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java b/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java index 76041312a2..31af57b649 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java @@ -17,9 +17,9 @@ import java.sql.Timestamp; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Version; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; @Entity public class SampleWithTimestampVersion { diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Site.java b/src/test/java/org/springframework/data/jpa/domain/sample/Site.java index 0cc2bac29e..17cd65d319 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Site.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Site.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; /** * @author Mark Paluch diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java b/src/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java index 1cf1c82924..08829f5730 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java @@ -1,6 +1,6 @@ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; +import jakarta.persistence.Entity; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/src/test/java/org/springframework/data/jpa/domain/sample/User.java index 3031dfc303..0ae5b0ee92 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -20,7 +20,7 @@ import java.util.HashSet; import java.util.Set; -import javax.persistence.*; +import jakarta.persistence.*; /** * Domain class representing a person emphasizing the use of {@code AbstractEntity}. No declaration of an id is diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java b/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java index 1c45fc597a..eabe86dfc8 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.springframework.data.jpa.domain.Specification; diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java b/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java index a242a0980e..558723b715 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.domain.sample; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Version; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Version; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java index 3bd2646052..d20b4f084f 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.domain.support; -import javax.persistence.AttributeConverter; +import jakarta.persistence.AttributeConverter; import javax.sql.DataSource; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java index cc63a7f04c..38dd803a31 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java @@ -17,7 +17,7 @@ import static org.mockito.Mockito.*; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java index 493292581b..8923c240a8 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.*; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java b/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java index f803794a88..4a7b9651bc 100644 --- a/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java +++ b/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java @@ -18,7 +18,7 @@ import java.util.Arrays; import java.util.List; -import javax.persistence.spi.PersistenceProvider; +import jakarta.persistence.spi.PersistenceProvider; import org.springframework.util.ClassUtils; diff --git a/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java b/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java index 1739c2e1ac..116df91ba3 100644 --- a/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java @@ -19,20 +19,20 @@ import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.Query; -import javax.persistence.Tuple; -import javax.persistence.TupleElement; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Root; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.Bindable.BindableType; -import javax.persistence.metamodel.ManagedType; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import jakarta.persistence.Tuple; +import jakarta.persistence.TupleElement; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.Bindable.BindableType; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java index e09b9044c2..37426368f7 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java @@ -19,7 +19,7 @@ import java.util.Collections; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java index 573dd2e700..cd8d706cc7 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java @@ -19,7 +19,7 @@ import java.util.Collections; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Version; diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index 2c98720dce..376132cc26 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -20,16 +20,16 @@ import java.util.Collections; -import javax.persistence.Access; -import javax.persistence.AccessType; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Embedded; -import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; -import javax.persistence.Transient; -import javax.persistence.metamodel.ManagedType; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Transient; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.Metamodel; import org.jmolecules.ddd.types.AggregateRoot; import org.jmolecules.ddd.types.Association; @@ -293,7 +293,7 @@ private static class SpringDataVersioned { private static class JpaVersioned { - @javax.persistence.Version long version; + @jakarta.persistence.Version long version; } private static class SpecializedAssociation { diff --git a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java index ed9c261f04..c2564b7308 100644 --- a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.*; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java index 872de8b879..ee7cee3eeb 100644 --- a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java @@ -22,7 +22,7 @@ import java.util.ArrayList; import java.util.List; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.assertj.core.api.Assumptions; import org.hibernate.Version; diff --git a/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java index ebb2daf5e5..f54f2d25c5 100644 --- a/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.*; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java index 47417b79eb..24ac368f13 100644 --- a/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java @@ -21,13 +21,13 @@ import java.util.Collections; import java.util.Map; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.LockModeType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.LockModeType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -59,7 +59,7 @@ class CrudMethodMetadataUnitTests { @Mock CriteriaQuery criteriaQuery; @Mock JpaEntityInformation information; @Mock TypedQuery typedQuery; - @Mock javax.persistence.Query query; + @Mock jakarta.persistence.Query query; @Mock Metamodel metamodel; private RoleRepository repository; diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java index 7378afeeaa..98dd3b6c5f 100644 --- a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.*; -import javax.persistence.Query; +import jakarta.persistence.Query; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index 21fe72d9fe..4373fc0164 100644 --- a/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -20,13 +20,13 @@ import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.Persistence; -import javax.persistence.PersistenceUtil; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Persistence; +import jakarta.persistence.PersistenceUtil; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.assertj.core.api.SoftAssertions; import org.junit.Assume; diff --git a/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java index 4e75eb7b43..a29658513b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java @@ -18,8 +18,8 @@ import java.io.IOException; import java.util.Collections; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java index fd70af135e..ac111c308b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java @@ -19,7 +19,7 @@ import java.util.List; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java index 059ee90b72..f0e862ada0 100644 --- a/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java @@ -21,13 +21,13 @@ import java.util.Collection; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.ParameterExpression; -import javax.persistence.criteria.Root; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.ParameterExpression; +import jakarta.persistence.criteria.Root; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java index bf89a1c430..56884c4530 100644 --- a/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java @@ -20,11 +20,11 @@ import java.util.List; import java.util.Set; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java index 324507cc46..f6b56ce671 100644 --- a/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java @@ -40,7 +40,7 @@ import org.springframework.transaction.annotation.Transactional; /** - * Integration tests for Repositories using {@link javax.persistence.IdClass} identifiers. + * Integration tests for Repositories using {@link jakarta.persistence.IdClass} identifiers. * * @author Mark Paluch * @author Jens Schauder diff --git a/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java b/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java index d77ba468ee..61911d7cf2 100644 --- a/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java @@ -21,13 +21,13 @@ import java.util.Collection; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.ParameterExpression; -import javax.persistence.criteria.Root; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.ParameterExpression; +import jakarta.persistence.criteria.Root; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java index 11bf21f1e3..d11352f5bd 100644 --- a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -21,8 +21,8 @@ import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java index ace4f14ff6..2b62926c86 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java @@ -19,10 +19,10 @@ import java.util.Map; -import javax.persistence.EntityManager; -import javax.persistence.ParameterMode; -import javax.persistence.PersistenceContext; -import javax.persistence.StoredProcedureQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.ParameterMode; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.StoredProcedureQuery; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 841f7b4667..e481be31fa 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -36,13 +36,13 @@ import java.util.Set; import java.util.stream.Stream; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.Query; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.assertj.core.api.SoftAssertions; import org.hibernate.LazyInitializationException; diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java index 5502778df7..0301fd5ab9 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java @@ -19,10 +19,10 @@ import java.util.Set; -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.se.SeContainer; -import javax.enterprise.inject.se.SeContainerInitializer; -import javax.enterprise.inject.spi.Bean; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.se.SeContainer; +import jakarta.enterprise.inject.se.SeContainerInitializer; +import jakarta.enterprise.inject.spi.Bean; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java b/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java index 432853b98a..ede95017fb 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.repository.cdi; -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.Disposes; -import javax.enterprise.inject.Produces; -import javax.persistence.EntityManagerFactory; -import javax.persistence.Persistence; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Persistence; /** * Produces and {@link EntityManagerFactory}. diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java index c23c618c05..75b014c69c 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java @@ -24,9 +24,9 @@ import java.util.Map; import java.util.Set; -import javax.enterprise.inject.spi.Bean; -import javax.enterprise.inject.spi.ProcessBean; -import javax.persistence.EntityManager; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.ProcessBean; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java b/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java index 815d9723de..b6139d748b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.repository.cdi; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; @Entity class Person { diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java b/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java index 614fce42c3..9a6e6433b7 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java @@ -20,7 +20,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.inject.Qualifier; +import jakarta.inject.Qualifier; @Qualifier @Retention(RetentionPolicy.RUNTIME) diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java b/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java index 50585796ef..74301ef100 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.repository.cdi; -import javax.enterprise.inject.Disposes; -import javax.enterprise.inject.Produces; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java b/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java index ab9d1b6bca..e17a5dc04c 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.cdi; -import javax.inject.Inject; +import jakarta.inject.Inject; /** * @author Oliver Gierke diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java b/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java index c9a2f7d7a2..d8b31cbd62 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java @@ -20,7 +20,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.interceptor.InterceptorBinding; +import jakarta.interceptor.InterceptorBinding; @InterceptorBinding @Target({ ElementType.METHOD, ElementType.TYPE }) diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java b/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java index c111345a86..a0697c3d01 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java @@ -15,13 +15,13 @@ */ package org.springframework.data.jpa.repository.cdi; -import javax.enterprise.inject.Any; -import javax.inject.Inject; -import javax.interceptor.AroundInvoke; -import javax.interceptor.Interceptor; -import javax.interceptor.InvocationContext; -import javax.persistence.EntityManager; -import javax.persistence.EntityTransaction; +import jakarta.enterprise.inject.Any; +import jakarta.inject.Inject; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InvocationContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityTransaction; @Transactional @Interceptor diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java b/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java index 994c30ca6c..88dd3b669b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.repository.cdi; -import javax.enterprise.inject.Disposes; -import javax.enterprise.inject.Produces; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; class UnqualifiedEntityManagerProducer { diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java b/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java index 9731a95d58..7b38a02cfb 100644 --- a/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java +++ b/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java @@ -20,7 +20,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.inject.Qualifier; +import jakarta.inject.Qualifier; /** * @author Mark Paluch diff --git a/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java b/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java index 5bc8e274e5..b569d2a318 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java @@ -25,7 +25,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java b/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java index eab801318b..a308e2488e 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.config; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; diff --git a/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java index df4c11d507..774718fb22 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java @@ -20,7 +20,7 @@ import java.util.Arrays; import java.util.List; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java index 00c0e69f4d..11d65209b8 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java @@ -21,8 +21,8 @@ import java.util.Arrays; import java.util.Collections; -import javax.persistence.EntityManagerFactory; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java index bf97365981..f7e35a1835 100644 --- a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java @@ -17,7 +17,7 @@ import java.io.Serializable; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java index ba55243bbc..2a45fd3452 100644 --- a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java +++ b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java @@ -19,7 +19,7 @@ import java.io.Serializable; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; @@ -44,7 +44,7 @@ public CustomGenericJpaRepositoryFactory(EntityManager entityManager) { /* * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaRepositoryFactory#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata, javax.persistence.EntityManager) + * @see org.springframework.data.jpa.repository.support.JpaRepositoryFactory#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata, jakarta.persistence.EntityManager) */ @Override @SuppressWarnings("unchecked") diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java index 79a46fc0f8..9d7c19bbc9 100644 --- a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java +++ b/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java @@ -17,7 +17,7 @@ import java.io.Serializable; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java index 2b93979b5b..82dc54067b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -25,13 +25,13 @@ import java.util.List; import java.util.Properties; -import javax.persistence.Entity; -import javax.persistence.EntityManagerFactory; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.NamedStoredProcedureQuery; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.NamedStoredProcedureQuery; import javax.sql.DataSource; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; import org.hibernate.dialect.MySQL8Dialect; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index 8598d84069..b5f6a5c60d 100644 --- a/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -26,15 +26,15 @@ import java.util.List; import java.util.Properties; -import javax.persistence.Entity; -import javax.persistence.EntityManagerFactory; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.NamedStoredProcedureQuery; -import javax.persistence.ParameterMode; -import javax.persistence.StoredProcedureParameter; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.NamedStoredProcedureQuery; +import jakarta.persistence.ParameterMode; +import jakarta.persistence.StoredProcedureParameter; import javax.sql.DataSource; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; import org.hibernate.dialect.PostgreSQL91Dialect; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java index 7de723d9e4..b985357e88 100644 --- a/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java @@ -19,15 +19,15 @@ import lombok.Data; -import javax.persistence.Access; -import javax.persistence.AccessType; -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.OneToOne; -import javax.persistence.Table; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java index 5c701acfc1..f41d69d0e5 100644 --- a/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java @@ -23,14 +23,14 @@ import java.util.List; import java.util.Properties; -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.EntityManagerFactory; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.OneToMany; -import javax.persistence.Table; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import javax.sql.DataSource; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index 40365ecee7..bd175ef1f9 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -22,12 +22,12 @@ import java.lang.reflect.Method; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.LockModeType; -import javax.persistence.PersistenceContext; -import javax.persistence.Query; -import javax.persistence.QueryHint; -import javax.persistence.TypedQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import jakarta.persistence.QueryHint; +import jakarta.persistence.TypedQuery; import org.junit.Assume; import org.junit.jupiter.api.BeforeEach; @@ -122,12 +122,12 @@ void shouldAddEntityGraphHintForFetch() throws Exception { JpaQueryMethod queryMethod = getMethod("findAll"); - javax.persistence.EntityGraph entityGraph = em.getEntityGraph("User.overview"); + jakarta.persistence.EntityGraph entityGraph = em.getEntityGraph("User.overview"); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); Query result = jpaQuery.createQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[0])); - verify(result).setHint("javax.persistence.fetchgraph", entityGraph); + verify(result).setHint("jakarta.persistence.fetchgraph", entityGraph); } @Test // DATAJPA-466 @@ -138,13 +138,13 @@ void shouldAddEntityGraphHintForLoad() throws Exception { JpaQueryMethod queryMethod = getMethod("getById", Integer.class); - javax.persistence.EntityGraph entityGraph = em.getEntityGraph("User.detail"); + jakarta.persistence.EntityGraph entityGraph = em.getEntityGraph("User.detail"); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); Query result = jpaQuery .createQuery(new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { 1 })); - verify(result).setHint("javax.persistence.loadgraph", entityGraph); + verify(result).setHint("jakarta.persistence.loadgraph", entityGraph); } private JpaQueryMethod getMethod(String name, Class... parameterTypes) throws Exception { diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index 55feb14707..059bc58242 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -21,9 +21,9 @@ import java.lang.reflect.Method; import java.util.Set; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.Tuple; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Tuple; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java index f5cc0fe6a2..42436c314d 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java @@ -25,10 +25,10 @@ import java.util.Iterator; import java.util.List; -import javax.persistence.AttributeNode; -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.Subgraph; +import jakarta.persistence.AttributeNode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Subgraph; import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.Assertions; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java index 2e18da3553..03783c5631 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java @@ -18,8 +18,8 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import javax.persistence.EntityGraph; -import javax.persistence.Subgraph; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.Subgraph; import org.junit.jupiter.api.Test; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java index b8b762fef5..15b551e407 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java @@ -20,9 +20,9 @@ import java.lang.reflect.Method; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.TypedQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.TypedQuery; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java index ffdf396cef..229702392e 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java @@ -15,13 +15,13 @@ */ package org.springframework.data.jpa.repository.query; -import static javax.persistence.TemporalType.*; +import static jakarta.persistence.TemporalType.*; import static org.assertj.core.api.Assertions.*; import java.lang.reflect.Method; import java.util.Date; -import javax.persistence.TemporalType; +import jakarta.persistence.TemporalType; import org.junit.jupiter.api.Test; import org.springframework.data.jpa.repository.Temporal; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java index ebb562af60..4f0b8ab3a9 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java @@ -26,9 +26,9 @@ import java.util.Collections; import java.util.Optional; -import javax.persistence.EntityManager; -import javax.persistence.Query; -import javax.persistence.TypedQuery; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index 79699c4207..4f85e56985 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -22,9 +22,9 @@ import java.lang.reflect.Method; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 7b817f5cf5..7fc18fb564 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -25,8 +25,8 @@ import java.util.List; import java.util.Optional; -import javax.persistence.LockModeType; -import javax.persistence.QueryHint; +import jakarta.persistence.LockModeType; +import jakarta.persistence.QueryHint; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java index 33038c9631..4d60b34863 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java @@ -16,7 +16,7 @@ package org.springframework.data.jpa.repository.query; import static java.util.Arrays.*; -import static javax.persistence.TemporalType.*; +import static jakarta.persistence.TemporalType.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.*; @@ -29,10 +29,10 @@ import java.util.List; import java.util.function.Function; -import javax.persistence.Parameter; -import javax.persistence.Query; -import javax.persistence.TemporalType; -import javax.persistence.criteria.ParameterExpression; +import jakarta.persistence.Parameter; +import jakarta.persistence.Query; +import jakarta.persistence.TemporalType; +import jakarta.persistence.criteria.ParameterExpression; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java index 64945f658e..913cc64c93 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java @@ -21,10 +21,10 @@ import java.lang.reflect.Method; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.TypedQuery; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java index a4d33fc5ff..2c522cf8da 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java @@ -16,7 +16,7 @@ package org.springframework.data.jpa.repository.query; import static java.util.Collections.*; -import static javax.persistence.TemporalType.*; +import static jakarta.persistence.TemporalType.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @@ -27,10 +27,10 @@ import java.util.List; import java.util.Optional; -import javax.persistence.Embeddable; -import javax.persistence.Parameter; -import javax.persistence.Query; -import javax.persistence.TemporalType; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Parameter; +import jakarta.persistence.Query; +import jakarta.persistence.TemporalType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java b/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java index 013c6b87c8..7d501cf8cf 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java @@ -19,10 +19,10 @@ import java.lang.reflect.Method; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.ParameterExpression; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.ParameterExpression; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java index 8fb19d74da..2407d0ba57 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java @@ -20,8 +20,8 @@ import java.lang.reflect.Method; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java index ffd4fd34f1..19d626ec90 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java @@ -20,7 +20,7 @@ import java.util.Collections; -import javax.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaBuilder; import org.junit.jupiter.api.Test; import org.springframework.data.repository.query.Parameters; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 69afb6a598..58025e45e2 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -27,10 +27,10 @@ import java.util.Iterator; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.Query; -import javax.persistence.TemporalType; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import jakarta.persistence.TemporalType; import org.hibernate.Version; import org.hibernate.query.internal.QueryImpl; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index f8ccf02b17..a00f1763a6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -25,23 +25,23 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import javax.persistence.Entity; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.Id; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.Persistence; -import javax.persistence.PersistenceContext; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Root; -import javax.persistence.spi.PersistenceProvider; -import javax.persistence.spi.PersistenceProviderResolver; -import javax.persistence.spi.PersistenceProviderResolverHolder; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Persistence; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.From; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.spi.PersistenceProvider; +import jakarta.persistence.spi.PersistenceProviderResolver; +import jakarta.persistence.spi.PersistenceProviderResolverHolder; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -307,7 +307,7 @@ void toOrdersCanSortByJoinColumn() { Sort sort = Sort.by(Direction.ASC, "age"); - List orders = QueryUtils.toOrders(sort, join, builder); + List orders = QueryUtils.toOrders(sort, join, builder); assertThat(orders).hasSize(1); } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 71339337e6..2b3888ac05 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -23,11 +23,11 @@ import java.util.Collection; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -76,7 +76,7 @@ class SimpleJpaQueryUnitTests { @Mock EntityManager em; @Mock EntityManagerFactory emf; @Mock QueryExtractor extractor; - @Mock javax.persistence.Query query; + @Mock jakarta.persistence.Query query; @Mock TypedQuery typedQuery; @Mock RepositoryMetadata metadata; @Mock ParameterBinder binder; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java index d4388f0256..ab9c804c76 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java @@ -24,8 +24,8 @@ import java.util.List; import java.util.Map; -import javax.persistence.EntityManager; -import javax.persistence.ParameterMode; +import jakarta.persistence.EntityManager; +import jakarta.persistence.ParameterMode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java index 73a35ba0a4..10f09fbe14 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java @@ -18,7 +18,7 @@ import static org.assertj.core.api.Assertions.*; import static org.springframework.data.jpa.repository.query.StoredProcedureAttributes.*; -import javax.persistence.ParameterMode; +import jakarta.persistence.ParameterMode; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java index 63f48a4ce9..131fa07d46 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java @@ -23,8 +23,8 @@ import java.util.List; import java.util.Map; -import javax.persistence.Tuple; -import javax.persistence.TupleElement; +import jakarta.persistence.Tuple; +import jakarta.persistence.TupleElement; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java index 93f331956e..2889f95c37 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.repository.sample; -import javax.persistence.LockModeType; -import javax.persistence.QueryHint; +import jakarta.persistence.LockModeType; +import jakarta.persistence.QueryHint; import java.util.Optional; diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 721123ca19..32b0b36767 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -23,8 +23,8 @@ import java.util.Set; import java.util.stream.Stream; -import javax.persistence.EntityManager; -import javax.persistence.QueryHint; +import jakarta.persistence.EntityManager; +import jakarta.persistence.QueryHint; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java index cd35daf10e..80b1365611 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java @@ -20,7 +20,7 @@ import java.lang.reflect.Method; -import javax.persistence.LockModeType; +import jakarta.persistence.LockModeType; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java index e11879247d..fe8215e224 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java @@ -22,8 +22,8 @@ import java.util.HashSet; import javax.naming.NamingException; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.junit.jupiter.api.BeforeAll; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java index 9b76f68bfa..4be4afddb5 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java @@ -19,7 +19,7 @@ import java.util.Collections; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java index 6a93386d1c..89ad5f4817 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java @@ -20,7 +20,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import javax.persistence.Entity; +import jakarta.persistence.Entity; import org.junit.jupiter.api.Test; import org.springframework.core.annotation.AliasFor; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java index 96f2e28f6f..069791f7a7 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.*; -import javax.persistence.TransactionRequiredException; +import jakarta.persistence.TransactionRequiredException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java index 7526a8afeb..48762e1fb5 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import javax.sql.DataSource; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java index b63026f68a..9b92bc52d7 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java @@ -18,7 +18,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java index 3b0ed39a87..bcdfdee43a 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.support; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java index cfcfdc2597..38ca24a882 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.*; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanDefinition; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java index f8dea9ffa5..37ae21a4e1 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java @@ -21,10 +21,10 @@ import java.io.Serializable; import java.util.Collections; -import javax.persistence.Entity; -import javax.persistence.EntityManager; -import javax.persistence.metamodel.Metamodel; -import javax.persistence.metamodel.SingularAttribute; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.SingularAttribute; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java index 88dd673e3d..5b90488556 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java @@ -24,8 +24,8 @@ import java.sql.Timestamp; import java.util.Date; -import javax.persistence.*; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.*; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java index ff612d5751..ce1207c300 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java @@ -23,10 +23,10 @@ import java.util.HashSet; import java.util.Set; -import javax.persistence.metamodel.IdentifiableType; -import javax.persistence.metamodel.Metamodel; -import javax.persistence.metamodel.SingularAttribute; -import javax.persistence.metamodel.Type; +import jakarta.persistence.metamodel.IdentifiableType; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.SingularAttribute; +import jakarta.persistence.metamodel.Type; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java index 04caefc750..c379a031a6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java @@ -18,9 +18,9 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import javax.persistence.metamodel.EntityType; -import javax.persistence.metamodel.Metamodel; -import javax.persistence.metamodel.Type; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.Type; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java index ddbd759b6a..a06707e0f6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java @@ -23,8 +23,8 @@ import java.util.HashMap; import java.util.Map; -import javax.persistence.EntityManager; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManager; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index 5205f74c8d..11f466f56a 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -22,9 +22,9 @@ import java.io.Serializable; import java.util.Optional; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java index cdebc4cb05..159c1fdc4a 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java @@ -20,8 +20,8 @@ import java.util.Arrays; import java.util.Optional; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java index ae523276be..688ae55eba 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java @@ -20,8 +20,8 @@ import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java index 97260542e7..ba4d78bc01 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java @@ -19,8 +19,8 @@ import java.util.stream.Stream; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index 1df41b945f..52d3608a43 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -25,8 +25,8 @@ import java.util.Set; import java.util.stream.Stream; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.hibernate.LazyInitializationException; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java index 7d0df51936..e438d4e2b0 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java @@ -25,8 +25,8 @@ import java.time.LocalDate; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java index 242e09f7c9..e5675fff31 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java @@ -17,8 +17,8 @@ import static org.assertj.core.api.Assertions.*; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java index f8d194de03..78ce578977 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java @@ -19,8 +19,8 @@ import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index 1c2d3827e9..9fabb41df0 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -22,11 +22,11 @@ import java.util.Arrays; import java.util.Optional; -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java b/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java index 0345ab3891..d06a73c34d 100644 --- a/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java @@ -24,7 +24,7 @@ import java.util.Collections; import java.util.Set; -import javax.persistence.Entity; +import jakarta.persistence.Entity; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java b/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java index 369028daa6..67dd3e4eab 100644 --- a/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java +++ b/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.support; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.util.ReflectionUtils; diff --git a/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java b/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java index f0f076d18f..db0d5e5799 100644 --- a/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java @@ -22,7 +22,7 @@ import java.net.URL; import java.util.Arrays; -import javax.persistence.spi.PersistenceUnitInfo; +import jakarta.persistence.spi.PersistenceUnitInfo; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java b/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java index 277a92064e..035f5d3717 100644 --- a/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java @@ -18,7 +18,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java b/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java index 2ccc0ef49f..37fc84555a 100644 --- a/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java @@ -20,8 +20,8 @@ import java.util.Collections; -import javax.persistence.metamodel.EntityType; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.Metamodel; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/resources/META-INF/persistence.xml b/src/test/resources/META-INF/persistence.xml index d5fbd08bc8..2a35e280d3 100644 --- a/src/test/resources/META-INF/persistence.xml +++ b/src/test/resources/META-INF/persistence.xml @@ -123,10 +123,10 @@ org.springframework.data.jpa.domain.sample.Dummy true - - - - + + + + diff --git a/src/test/resources/config/jpa-context-with-jndi.xml b/src/test/resources/config/jpa-context-with-jndi.xml index 3688b88a62..3d41c9f94c 100644 --- a/src/test/resources/config/jpa-context-with-jndi.xml +++ b/src/test/resources/config/jpa-context-with-jndi.xml @@ -13,7 +13,7 @@ + expected-type="jakarta.persistence.EntityManagerFactory" /> diff --git a/src/test/resources/eclipselink.xml b/src/test/resources/eclipselink.xml index 5871d54cb3..069583385c 100644 --- a/src/test/resources/eclipselink.xml +++ b/src/test/resources/eclipselink.xml @@ -9,11 +9,11 @@ - org.hsqldb.jdbcDriver - jdbc:hsqldb:mem:hades - sa - - create-tables + org.hsqldb.jdbcDriver + jdbc:hsqldb:mem:hades + sa + + create-tables false SEVERE diff --git a/src/test/resources/multiple-entity-manager-context.xml b/src/test/resources/multiple-entity-manager-context.xml index 53f76712d1..e9931932f2 100644 --- a/src/test/resources/multiple-entity-manager-context.xml +++ b/src/test/resources/multiple-entity-manager-context.xml @@ -10,11 +10,11 @@ http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> - + - + diff --git a/template.mf b/template.mf index 9705afec65..c3e75cc826 100644 --- a/template.mf +++ b/template.mf @@ -8,9 +8,9 @@ Export-Template: org.springframework.data.jpa.*;version="${project.version}" Import-Template: com.querydsl.*;version="${querydsl:[=.=.=,+1.0.0)}";resolution:=optional, - javax.persistence.*;version="${jpa:[=.=.=,+1.0.0)}", - javax.annotation.*;version="0.0.0", - javax.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional, + jakarta.persistence.*;version="${jpa:[=.=.=,+1.0.0)}", + jakarta.annotation.*;version="0.0.0", + jakarta.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional, org.aopalliance.*;version="[1.0.0,2.0.0)", org.apache.openjpa.*;version="${openjpa:[=.=.=,+1.0.0)}";resolution:=optional, org.aspectj.*;version="${aspectj:[=.=.=,+1.0.0)}";resolution:=optional, From c402cd9fe60e7b21dacffbb1f83ae06a43b0177b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 30 Sep 2021 10:25:08 +0200 Subject: [PATCH 119/821] Refine CI triggers. See #2307 --- Jenkinsfile | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 24e4496530..4a11f0b168 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -75,34 +75,6 @@ pipeline { } } } - stage('Publish documentation') { - when { - branch 'main' - } - agent { - label 'data' - } - options { timeout(time: 20, unit: 'MINUTES') } - - environment { - ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') - } - - steps { - script { - docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('openjdk:17-bullseye').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute ' + - '-Dartifactory.server=https://repo.spring.io ' + - "-Dartifactory.username=${ARTIFACTORY_USR} " + - "-Dartifactory.password=${ARTIFACTORY_PSW} " + - "-Dartifactory.distribution-repository=temp-private-local " + - '-Dmaven.test.skip=true clean deploy -U -B' - } - } - } - } - } } post { From 0ae3d435023ffbf4cea2a9de13fd77114fb97c53 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 27 Sep 2021 10:51:09 +0200 Subject: [PATCH 120/821] Merge the Envers repo. Spring Data JPA is now a multimodule project. The artifacts build are still separate, except for the documentation which is now a single one. Closes # 2316 --- pom.xml | 401 +---------------- .../etc/eclipse-formatter.xml | 0 spring-data-envers/pom.xml | 87 ++++ .../config/EnableEnversRepositories.java | 199 +++++++++ .../repository/config/package-info.java | 5 + .../DefaultRevisionEntityInformation.java | 51 +++ .../support/DefaultRevisionMetadata.java | 123 ++++++ .../support/EnversRevisionRepository.java | 37 ++ .../EnversRevisionRepositoryFactoryBean.java | 113 +++++ .../support/EnversRevisionRepositoryImpl.java | 230 ++++++++++ .../ReflectionRevisionEntityInformation.java | 68 +++ .../repository/support/package-info.java | 5 + .../src/main/resources/changelog.txt | 260 +++++++++++ .../src/main/resources/license.txt | 279 ++++++++++++ .../src/main/resources/notice.txt | 33 ++ .../springframework/data/envers/Config.java | 76 ++++ .../DefaultRevisionMetadataUnitTests.java | 50 +++ ...EnversRevisionRepositoryImplUnitTests.java | 60 +++ .../QueryDslRepositoryIntegrationTests.java | 97 +++++ .../support/RepositoryIntegrationTests.java | 251 +++++++++++ .../data/envers/sample/AbstractEntity.java | 29 ++ .../data/envers/sample/Country.java | 36 ++ .../sample/CountryQueryDslRepository.java | 31 ++ .../data/envers/sample/CountryRepository.java | 28 ++ .../data/envers/sample/License.java | 41 ++ .../data/envers/sample/LicenseRepository.java | 28 ++ .../data/envers/sample/QCountry.java | 63 +++ .../src/test/resources/logback.xml | 16 + spring-data-jpa-distribution/pom.xml | 39 ++ .../Spring Data JPA.sonargraph | 0 aop.xml => spring-data-jpa/aop.xml | 0 spring-data-jpa/formatting.xml | 291 +++++++++++++ spring-data-jpa/pom.xml | 402 ++++++++++++++++++ .../QueryByExamplePredicateBuilder.java | 0 .../data/jpa/convert/package-info.java | 0 .../convert/threeten/Jsr310JpaConverters.java | 0 .../jpa/convert/threeten/package-info.java | 0 .../data/jpa/domain/AbstractAuditable.java | 0 .../data/jpa/domain/AbstractPersistable.java | 0 .../data/jpa/domain/JpaSort.java | 0 .../data/jpa/domain/Specification.java | 0 .../jpa/domain/SpecificationComposition.java | 0 .../data/jpa/domain/package-info.java | 0 .../AuditingBeanFactoryPostProcessor.java | 0 .../support/AuditingEntityListener.java | 0 .../data/jpa/domain/support/package-info.java | 0 .../mapping/JpaMetamodelMappingContext.java | 0 .../data/jpa/mapping/JpaPersistentEntity.java | 0 .../jpa/mapping/JpaPersistentEntityImpl.java | 0 .../jpa/mapping/JpaPersistentProperty.java | 0 .../mapping/JpaPersistentPropertyImpl.java | 0 .../data/jpa/mapping/package-info.java | 0 .../CollectionAwareProjectionFactory.java | 0 .../data/jpa/provider/HibernateUtils.java | 0 .../data/jpa/provider/JpaClassUtils.java | 0 .../jpa/provider/PersistenceProvider.java | 0 .../data/jpa/provider/ProxyIdAccessor.java | 0 .../data/jpa/provider/QueryExtractor.java | 0 .../data/jpa/provider/package-info.java | 0 .../data/jpa/repository/EntityGraph.java | 0 .../data/jpa/repository/JpaContext.java | 0 .../data/jpa/repository/JpaRepository.java | 0 .../repository/JpaSpecificationExecutor.java | 0 .../data/jpa/repository/Lock.java | 0 .../data/jpa/repository/Modifying.java | 0 .../data/jpa/repository/Query.java | 0 .../data/jpa/repository/QueryHints.java | 0 .../data/jpa/repository/Temporal.java | 0 .../jpa/repository/cdi/JpaRepositoryBean.java | 0 .../cdi/JpaRepositoryExtension.java | 0 .../data/jpa/repository/cdi/package-info.java | 0 .../config/AuditingBeanDefinitionParser.java | 0 .../config/BeanDefinitionNames.java | 0 .../repository/config/EnableJpaAuditing.java | 0 .../config/EnableJpaRepositories.java | 0 .../config/InspectionClassLoader.java | 0 .../config/JpaAuditingRegistrar.java | 0 ...JpaMetamodelMappingContextFactoryBean.java | 0 .../config/JpaRepositoriesRegistrar.java | 0 .../config/JpaRepositoryConfigExtension.java | 0 .../config/JpaRepositoryNameSpaceHandler.java | 0 .../jpa/repository/config/package-info.java | 0 .../data/jpa/repository/package-info.java | 0 .../repository/query/AbstractJpaQuery.java | 0 .../query/AbstractStringBasedJpaQuery.java | 0 .../jpa/repository/query/DeclaredQuery.java | 0 .../query/DefaultJpaEntityMetadata.java | 0 .../query/DefaultJpaQueryMethodFactory.java | 0 .../repository/query/EmptyDeclaredQuery.java | 0 .../jpa/repository/query/EscapeCharacter.java | 0 .../query/ExpressionBasedStringQuery.java | 0 .../query/InvalidJpaQueryMethodException.java | 0 .../data/jpa/repository/query/Jpa21Utils.java | 0 .../query/JpaCountQueryCreator.java | 0 .../jpa/repository/query/JpaEntityGraph.java | 0 .../repository/query/JpaEntityMetadata.java | 0 .../jpa/repository/query/JpaParameters.java | 0 .../query/JpaParametersParameterAccessor.java | 0 .../jpa/repository/query/JpaQueryCreator.java | 0 .../repository/query/JpaQueryExecution.java | 0 .../jpa/repository/query/JpaQueryFactory.java | 0 .../query/JpaQueryLookupStrategy.java | 0 .../jpa/repository/query/JpaQueryMethod.java | 0 .../query/JpaQueryMethodFactory.java | 0 .../repository/query/JpaResultConverters.java | 0 .../data/jpa/repository/query/NamedQuery.java | 0 .../jpa/repository/query/NativeJpaQuery.java | 0 .../jpa/repository/query/ParameterBinder.java | 0 .../query/ParameterBinderFactory.java | 0 .../query/ParameterMetadataProvider.java | 0 .../repository/query/PartTreeJpaQuery.java | 0 .../data/jpa/repository/query/Procedure.java | 0 .../repository/query/ProcedureParameter.java | 0 .../query/QueryParameterSetter.java | 0 .../query/QueryParameterSetterFactory.java | 0 .../data/jpa/repository/query/QueryUtils.java | 0 .../jpa/repository/query/SimpleJpaQuery.java | 0 .../query/StoredProcedureAttributeSource.java | 0 .../query/StoredProcedureAttributes.java | 0 .../query/StoredProcedureJpaQuery.java | 0 .../jpa/repository/query/StringQuery.java | 0 .../jpa/repository/query/package-info.java | 0 .../support/CrudMethodMetadata.java | 0 .../CrudMethodMetadataPostProcessor.java | 0 .../repository/support/DefaultJpaContext.java | 0 .../repository/support/DefaultQueryHints.java | 0 ...rBeanDefinitionRegistrarPostProcessor.java | 0 .../FetchableFluentQueryByExample.java | 0 .../FetchableFluentQueryByPredicate.java | 0 .../support/FluentQuerySupport.java | 0 .../support/JpaEntityInformation.java | 0 .../support/JpaEntityInformationSupport.java | 0 .../JpaEvaluationContextExtension.java | 0 .../JpaMetamodelEntityInformation.java | 0 .../JpaPersistableEntityInformation.java | 0 .../support/JpaRepositoryFactory.java | 0 .../support/JpaRepositoryFactoryBean.java | 0 .../support/JpaRepositoryImplementation.java | 0 .../repository/support/MutableQueryHints.java | 0 .../repository/support/QueryHintValue.java | 0 .../jpa/repository/support/QueryHints.java | 0 .../data/jpa/repository/support/Querydsl.java | 0 .../support/QuerydslJpaPredicateExecutor.java | 0 .../support/QuerydslJpaRepository.java | 0 .../support/QuerydslRepositorySupport.java | 0 .../support/SimpleJpaRepository.java | 0 .../jpa/repository/support/package-info.java | 0 ...hScanningPersistenceUnitPostProcessor.java | 0 .../MergingPersistenceUnitManager.java | 0 .../data/jpa/support/package-info.java | 0 .../data/jpa/util/BeanDefinitionUtils.java | 0 .../data/jpa/util/HibernateProxyDetector.java | 0 .../data/jpa/util/JpaMetamodel.java | 0 .../jpa/util/JpaMetamodelCacheCleanup.java | 0 .../data/jpa/util/package-info.java | 0 .../jakarta.enterprise.inject.spi.Extension | 0 .../main/resources/META-INF/spring.factories | 0 .../main/resources/META-INF/spring.handlers | 0 .../main/resources/META-INF/spring.schemas | 0 .../main/resources/META-INF/spring.tooling | 0 .../src}/main/resources/license.txt | 0 .../src}/main/resources/notice.txt | 0 .../jpa/repository/config/spring-jpa-1.0.xsd | 0 .../jpa/repository/config/spring-jpa-1.1.xsd | 0 .../jpa/repository/config/spring-jpa-1.11.xsd | 0 .../jpa/repository/config/spring-jpa-1.2.xsd | 0 .../jpa/repository/config/spring-jpa-1.3.xsd | 0 .../jpa/repository/config/spring-jpa-1.8.xsd | 0 .../src}/main/resources/readme.txt | 0 ...eryByExamplePredicateBuilderUnitTests.java | 0 .../jpa/convert/threeten/DateTimeSample.java | 0 .../Jsr310JpaConvertersIntegrationTests.java | 0 .../Jsr310JpaConvertersUnitTests.java | 0 .../data/jpa/domain/JpaSortTests.java | 0 .../jpa/domain/SpecificationUnitTests.java | 0 .../sample/AbstractAnnotatedAuditable.java | 0 .../jpa/domain/sample/AbstractMappedType.java | 0 .../data/jpa/domain/sample/Account.java | 0 .../data/jpa/domain/sample/Address.java | 0 .../domain/sample/AnnotatedAuditableUser.java | 0 .../data/jpa/domain/sample/AuditableRole.java | 0 .../data/jpa/domain/sample/AuditableUser.java | 0 .../jpa/domain/sample/AuditorAwareStub.java | 0 .../data/jpa/domain/sample/Category.java | 0 .../data/jpa/domain/sample/Child.java | 0 .../data/jpa/domain/sample/ConcreteType1.java | 0 .../data/jpa/domain/sample/ConcreteType2.java | 0 .../sample/CustomAbstractPersistable.java | 0 .../data/jpa/domain/sample/Customer.java | 0 .../data/jpa/domain/sample/Dummy.java | 0 .../sample/EmbeddedIdExampleDepartment.java | 0 .../sample/EmbeddedIdExampleEmployee.java | 0 .../sample/EmbeddedIdExampleEmployeePK.java | 0 .../domain/sample/EntityWithAssignedId.java | 0 .../sample/IdClassExampleDepartment.java | 0 .../domain/sample/IdClassExampleEmployee.java | 0 .../sample/IdClassExampleEmployeePK.java | 0 .../data/jpa/domain/sample/Invoice.java | 0 .../data/jpa/domain/sample/InvoiceItem.java | 0 .../data/jpa/domain/sample/Item.java | 0 .../data/jpa/domain/sample/ItemId.java | 0 .../data/jpa/domain/sample/ItemSite.java | 0 .../data/jpa/domain/sample/ItemSiteId.java | 0 .../data/jpa/domain/sample/MailMessage.java | 0 .../data/jpa/domain/sample/MailSender.java | 0 .../data/jpa/domain/sample/MailUser.java | 0 .../data/jpa/domain/sample/Order.java | 0 .../data/jpa/domain/sample/OrmXmlEntity.java | 0 .../data/jpa/domain/sample/Parent.java | 0 .../domain/sample/PersistableWithIdClass.java | 0 .../sample/PersistableWithIdClassPK.java | 0 .../sample/PersistableWithSingleIdClass.java | 0 .../PersistableWithSingleIdClassPK.java | 0 .../sample/PrimitiveVersionProperty.java | 0 .../data/jpa/domain/sample/Product.java | 0 .../data/jpa/domain/sample/Role.java | 0 .../data/jpa/domain/sample/SampleEntity.java | 0 .../jpa/domain/sample/SampleEntityPK.java | 0 .../jpa/domain/sample/SampleWithIdClass.java | 0 .../SampleWithIdClassIncludingEntity.java | 0 .../domain/sample/SampleWithPrimitiveId.java | 0 .../sample/SampleWithTimestampVersion.java | 0 .../data/jpa/domain/sample/Site.java | 0 .../data/jpa/domain/sample/SpecialUser.java | 0 .../data/jpa/domain/sample/User.java | 0 .../jpa/domain/sample/UserSpecifications.java | 0 .../data/jpa/domain/sample/VersionedUser.java | 0 ...actAttributeConverterIntegrationTests.java | 0 ...tingBeanFactoryPostProcessorUnitTests.java | 0 ...tingBeanFactoryPostProcessorUnitTests.java | 0 .../support/AuditingEntityListenerTests.java | 0 .../support/AuditingNamespaceUnitTests.java | 0 .../EclipseLinkMetamodelIntegrationTests.java | 0 .../HibernateMetamodelIntegrationTests.java | 0 .../infrastructure/HibernateTestUtils.java | 0 .../MetamodelIntegrationTests.java | 0 .../OpenJpaMetamodelIntegrationTests.java | 0 ...tamodelMappingContextIntegrationTests.java | 0 .../JpaMetamodelMappingContextUnitTests.java | 0 .../JpaPersistentPropertyImplUnitTests.java | 0 .../PersistenceProviderIntegrationTests.java | 0 .../PersistenceProviderUnitTests.java | 0 .../AbstractPersistableIntegrationTests.java | 0 .../CrudMethodMetadataUnitTests.java | 0 ...omAbstractPersistableIntegrationTests.java | 0 .../CustomEclipseLinkJpaVendorAdapter.java | 0 .../CustomHsqlHibernateJpaVendorAdaptor.java | 0 ...raphRepositoryMethodsIntegrationTests.java | 0 ...lipseLinkNamespaceUserRepositoryTests.java | 0 ...eLinkParentRepositoryIntegrationTests.java | 0 ...itoryWithCompositeKeyIntegrationTests.java | 0 ...seLinkStoredProcedureIntegrationTests.java | 0 .../EclipseLinkUserRepositoryFinderTests.java | 0 ...raphRepositoryMethodsIntegrationTests.java | 0 .../EntityWithAssignedIdIntegrationTests.java | 0 .../JavaConfigUserRepositoryTests.java | 0 .../MappedTypeRepositoryIntegrationTests.java | 0 .../NamespaceUserRepositoryTests.java | 0 .../repository/ORMInfrastructureTests.java | 0 ...raphRepositoryMethodsIntegrationTests.java | 0 .../OpenJpaNamespaceUserRepositoryTests.java | 0 ...enJpaParentRepositoryIntegrationTests.java | 0 ...itoryWithCompositeKeyIntegrationTests.java | 0 ...penJpaStoredProcedureIntegrationTests.java | 0 .../OpenJpaUserRepositoryFinderTests.java | 0 .../ParentRepositoryIntegrationTests.java | 0 .../RedeclaringRepositoryMethodsTests.java | 0 .../RepositoryWithCompositeKeyTests.java | 0 .../RepositoryWithIdClassKeyTests.java | 0 .../RoleRepositoryIntegrationTests.java | 0 .../data/jpa/repository/SPR8954Tests.java | 0 .../SimpleJpaParameterBindingTests.java | 0 .../StoredProcedureIntegrationTests.java | 0 .../repository/UserRepositoryFinderTests.java | 0 ...sitoryStoredProcedureIntegrationTests.java | 0 .../jpa/repository/UserRepositoryTests.java | 0 .../cdi/CdiExtensionIntegrationTests.java | 0 .../cdi/EntityManagerFactoryProducer.java | 0 .../cdi/JpaRepositoryExtensionUnitTests.java | 0 .../data/jpa/repository/cdi/Person.java | 0 .../data/jpa/repository/cdi/PersonDB.java | 0 .../jpa/repository/cdi/PersonRepository.java | 0 .../QualifiedCustomizedCdiConfiguration.java | 0 .../QualifiedCustomizedUserRepository.java | 0 ...QualifiedCustomizedUserRepositoryBean.java | 0 ...alifiedCustomizedUserRepositoryCustom.java | 0 .../cdi/QualifiedEntityManagerProducer.java | 0 .../jpa/repository/cdi/QualifiedFragment.java | 0 .../repository/cdi/QualifiedFragmentBean.java | 0 .../cdi/QualifiedPersonRepository.java | 0 .../repository/cdi/RepositoryConsumer.java | 0 .../cdi/SamplePersonRepository.java | 0 .../cdi/SamplePersonRepositoryCustom.java | 0 .../cdi/SamplePersonRepositoryImpl.java | 0 .../jpa/repository/cdi/Transactional.java | 0 .../cdi/TransactionalInterceptor.java | 0 .../cdi/UnqualifiedEntityManagerProducer.java | 0 .../cdi/UnqualifiedPersonRepository.java | 0 .../data/jpa/repository/cdi/UserDB.java | 0 ...uditingViaJavaConfigRepositoriesTests.java | 0 .../config/AbstractRepositoryConfigTests.java | 0 ...stedRepositoriesRepositoryConfigTests.java | 0 .../AuditingBeanDefinitionParserTests.java | 0 .../CustomRepositoryFactoryConfigTests.java | 0 ...uditingViaJavaConfigRepositoriesTests.java | 0 ...uditingViaJavaConfigRepositoriesTests.java | 0 .../config/InfrastructureConfig.java | 0 .../InspectionClassLoaderUnitTests.java | 0 .../config/JpaAuditingRegistrarUnitTests.java | 0 ...RepositoriesRegistrarIntegrationTests.java | 0 .../JpaRepositoriesRegistrarUnitTests.java | 0 ...RepositoryConfigDefinitionParserTests.java | 0 ...JpaRepositoryConfigExtensionUnitTests.java | 0 .../NestedRepositoriesJavaConfigTests.java | 0 .../config/QueryLookupStrategyTests.java | 0 .../config/RepositoriesJavaConfigTests.java | 0 .../config/RepositoryAutoConfigTests.java | 0 .../config/RepositoryConfigTests.java | 0 .../config/TypeFilterConfigTests.java | 0 .../data/jpa/repository/config/package.html | 0 .../custom/CustomGenericJpaRepository.java | 0 .../CustomGenericJpaRepositoryFactory.java | 0 ...CustomGenericJpaRepositoryFactoryBean.java | 0 .../custom/CustomGenericRepository.java | 0 .../custom/UserCustomExtendedRepository.java | 0 .../jpa/repository/custom/package-info.java | 0 .../MySqlStoredProcedureIntegrationTests.java | 0 ...stgresStoredProcedureIntegrationTests.java | 0 .../ProjectionJoinIntegrationTests.java | 0 .../ProjectionsIntegrationTests.java | 0 .../query/AbstractJpaQueryTests.java | 0 ...ctStringBasedJpaQueryIntegrationTests.java | 0 ...BindableJpaParametersIntegrationTests.java | 0 .../query/EclipseLinkJpa21UtilsTests.java | 0 ...meterMetadataProviderIntegrationTests.java | 0 ...EclipseLinkQueryUtilsIntegrationTests.java | 0 .../query/EscapeCharacterUnitTests.java | 0 .../ExpressionBasedStringQueryUnitTests.java | 0 .../jpa/repository/query/Jpa21UtilsTests.java | 0 .../repository/query/Jpa21UtilsUnitTests.java | 0 .../JpaCountQueryCreatorIntegrationTests.java | 0 .../query/JpaParametersUnitTests.java | 0 .../query/JpaQueryExecutionUnitTests.java | 0 .../JpaQueryLookupStrategyUnitTests.java | 0 .../query/JpaQueryMethodUnitTests.java | 0 .../query/LikeBindingUnitTests.java | 0 ...rIndexedQueryParameterSetterUnitTests.java | 0 .../repository/query/NamedQueryUnitTests.java | 0 .../query/OpenJpaJpa21UtilsTests.java | 0 ...meterMetadataProviderIntegrationTests.java | 0 .../OpenJpaQueryUtilsIntegrationTests.java | 0 .../query/ParameterBinderUnitTests.java | 0 .../ParameterBindingParserUnitTests.java | 0 .../ParameterExpressionProviderTests.java | 0 ...meterMetadataProviderIntegrationTests.java | 0 .../ParameterMetadataProviderUnitTests.java | 0 .../PartTreeJpaQueryIntegrationTests.java | 0 .../QueryParameterSetterFactoryUnitTests.java | 0 .../query/QueryUtilsIntegrationTests.java | 0 .../repository/query/QueryUtilsUnitTests.java | 0 .../query/SimpleJpaQueryUnitTests.java | 0 ...oredProcedureAttributeSourceUnitTests.java | 0 .../StoredProcedureAttributesUnitTests.java | 0 .../query/StringQueryUnitTests.java | 0 .../query/TupleConverterUnitTests.java | 0 .../AnnotatedAuditableUserRepository.java | 0 .../sample/AuditableUserRepository.java | 0 .../repository/sample/CategoryRepository.java | 0 .../sample/ClassWithNestedRepository.java | 0 .../sample/ConcreteRepository1.java | 0 .../sample/ConcreteRepository2.java | 0 .../CustomAbstractPersistableRepository.java | 0 .../repository/sample/DummyRepository.java | 0 .../EmployeeRepositoryWithEmbeddedId.java | 0 .../sample/EmployeeRepositoryWithIdClass.java | 0 .../EntityWithAssignedIdRepository.java | 0 .../jpa/repository/sample/ItemRepository.java | 0 .../repository/sample/ItemSiteRepository.java | 0 .../sample/MailMessageRepository.java | 0 .../sample/MappedTypeRepository.java | 0 .../jpa/repository/sample/NameOnlyDto.java | 0 .../repository/sample/ParentRepository.java | 0 .../repository/sample/ProductRepository.java | 0 ...edeclaringRepositoryMethodsRepository.java | 0 ...ethodsWithEntityGraphConfigRepository.java | 0 .../jpa/repository/sample/RoleRepository.java | 0 .../jpa/repository/sample/SampleConfig.java | 0 .../SampleEvaluationContextExtension.java | 0 .../jpa/repository/sample/SiteRepository.java | 0 .../jpa/repository/sample/UserRepository.java | 0 .../sample/UserRepositoryCustom.java | 0 .../repository/sample/UserRepositoryImpl.java | 0 ...aPopulatingMethodInterceptorUnitTests.java | 0 .../DefaultJpaContextIntegrationTests.java | 0 .../support/DefaultJpaContextUnitTests.java | 0 .../DefaultJpaEntityMetadataUnitTest.java | 0 .../support/DefaultQueryHintsTest.java | 0 ...tTransactionDisablingIntegrationTests.java | 0 ...odelEntityInformationIntegrationTests.java | 0 .../EclipseLinkJpaRepositoryTests.java | 0 .../EclipseLinkProxyIdAccessorTests.java | 0 ...egistrarPostProcessorIntegrationTests.java | 0 ...nitionRegistrarPostProcessorUnitTests.java | 0 .../support/EntityManagerFactoryRefTests.java | 0 .../EntityManagerFactoryRefUnitTests.java | 0 ...tTransactionDisablingIntegrationTests.java | 0 .../JpaEntityInformationSupportUnitTests.java | 0 ...odelEntityInformationIntegrationTests.java | 0 ...paMetamodelEntityInformationUnitTests.java | 0 ...PersistableEntityInformationUnitTests.java | 0 ...eanEntityPathResolverIntegrationTests.java | 0 .../JpaRepositoryFactoryBeanUnitTests.java | 0 .../JpaRepositoryFactoryUnitTests.java | 0 .../support/JpaRepositoryTests.java | 0 ...MailMessageRepositoryIntegrationTests.java | 0 .../support/MutableQueryHintsUnitTests.java | 0 .../support/OpenJpaJpaRepositoryTests.java | 0 ...odelEntityInformationIntegrationTests.java | 0 .../support/OpenJpaProxyIdAccessorTests.java | 0 ...pleEntityPathResolverUnitTests_Sample.java | 0 .../support/QuerydslIntegrationTests.java | 0 ...QuerydslJpaPredicateExecutorUnitTests.java | 0 .../support/QuerydslJpaRepositoryTests.java | 0 ...ydslRepositorySupportIntegrationTests.java | 0 .../QuerydslRepositorySupportTests.java | 0 .../support/SimpleJpaRepositoryUnitTests.java | 0 .../support/TransactionalRepositoryTests.java | 0 ...tTransactionDisablingIntegrationTests.java | 0 .../data/jpa/repository/support/package.html | 0 ...PersistenceUnitPostProcessorUnitTests.java | 0 .../jpa/support/EntityManagerTestUtils.java | 0 ...ergingPersistenceUnitManagerUnitTests.java | 0 .../data/jpa/util/FixedDate.java | 0 ...MetamodelCacheCleanupIntegrationTests.java | 0 .../data/jpa/util/JpaMetamodelUnitTests.java | 0 .../META-INF/jpa-named-queries.properties | 0 .../src}/test/resources/META-INF/orm.xml | 0 .../test/resources/META-INF/persistence.xml | 0 .../test/resources/META-INF/persistence2.xml | 0 .../test/resources/application-context.xml | 0 .../auditing/auditing-bfpp-context.xml | 0 .../auditing/auditing-entity-listener.xml | 0 .../auditing/auditing-namespace-context.xml | 0 .../auditing/auditing-namespace-context2.xml | 0 .../auditing/auditing-namespace-context3.xml | 0 .../config/jpa-context-with-jndi.xml | 0 .../config/lookup-strategies-context.xml | 0 .../config/namespace-application-context.xml | 0 .../config/namespace-autoconfig-context.xml | 0 ...amespace-autoconfig-typefilter-context.xml | 0 .../namespace-customfactory-context.xml | 0 ...ested-repositories-application-context.xml | 0 .../src}/test/resources/eclipselink.xml | 0 .../src}/test/resources/hibernate.xml | 0 .../src}/test/resources/infrastructure.xml | 0 .../src}/test/resources/logback.xml | 0 .../multiple-entity-manager-context.xml | 0 ...ple-entity-manager-integration-context.xml | 0 .../src}/test/resources/openjpa.xml | 0 .../support/disable-default-transactions.xml | 0 .../data/jpa/support/mapping.xml | 0 .../data/jpa/support/module1/module1-orm.xml | 0 .../data/jpa/support/module2/module2-orm.xml | 0 .../data/jpa/support/persistence.xml | 0 .../data/jpa/support/persistence2.xml | 0 .../test/resources/scripts/hsqldb-init.sql | 0 .../scripts/mysql-stored-procedures.sql | 0 .../scripts/postgres-stored-procedures.sql | 0 .../scripts/schema-stored-procedures.sql | 0 .../simple-persistence/simple-persistence.xml | 0 .../src}/test/resources/tx-manager.xml | 0 src/main/asciidoc/envers.adoc | 204 +++++++++ src/main/asciidoc/index.adoc | 2 + template.mf | 28 -- 474 files changed, 3249 insertions(+), 414 deletions(-) rename formatting.xml => spring-data-envers/etc/eclipse-formatter.xml (100%) create mode 100755 spring-data-envers/pom.xml create mode 100644 spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java create mode 100644 spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/package-info.java create mode 100644 spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java create mode 100755 spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java create mode 100644 spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java create mode 100755 spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java create mode 100755 spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java create mode 100644 spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java create mode 100644 spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/package-info.java create mode 100644 spring-data-envers/src/main/resources/changelog.txt create mode 100644 spring-data-envers/src/main/resources/license.txt create mode 100644 spring-data-envers/src/main/resources/notice.txt create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/Config.java create mode 100644 spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java create mode 100644 spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java create mode 100644 spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java create mode 100755 spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java create mode 100644 spring-data-envers/src/test/resources/logback.xml create mode 100644 spring-data-jpa-distribution/pom.xml rename Spring Data JPA.sonargraph => spring-data-jpa/Spring Data JPA.sonargraph (100%) rename aop.xml => spring-data-jpa/aop.xml (100%) create mode 100644 spring-data-jpa/formatting.xml create mode 100644 spring-data-jpa/pom.xml rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/convert/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/convert/threeten/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/JpaSort.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/Specification.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/domain/support/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/mapping/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/provider/HibernateUtils.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/provider/QueryExtractor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/provider/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/EntityGraph.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/JpaContext.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/JpaRepository.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/Lock.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/Modifying.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/Query.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/QueryHints.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/Temporal.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/cdi/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/config/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/Procedure.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/StringQuery.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/query/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/QueryHints.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/Querydsl.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/support/package-info.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/util/JpaMetamodel.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java (100%) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/util/package-info.java (100%) rename {src => spring-data-jpa/src}/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension (100%) rename {src => spring-data-jpa/src}/main/resources/META-INF/spring.factories (100%) rename {src => spring-data-jpa/src}/main/resources/META-INF/spring.handlers (100%) rename {src => spring-data-jpa/src}/main/resources/META-INF/spring.schemas (100%) rename {src => spring-data-jpa/src}/main/resources/META-INF/spring.tooling (100%) rename {src => spring-data-jpa/src}/main/resources/license.txt (100%) rename {src => spring-data-jpa/src}/main/resources/notice.txt (100%) rename {src => spring-data-jpa/src}/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.0.xsd (100%) rename {src => spring-data-jpa/src}/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.1.xsd (100%) rename {src => spring-data-jpa/src}/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.11.xsd (100%) rename {src => spring-data-jpa/src}/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.2.xsd (100%) rename {src => spring-data-jpa/src}/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.3.xsd (100%) rename {src => spring-data-jpa/src}/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.8.xsd (100%) rename {src => spring-data-jpa/src}/main/resources/readme.txt (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/JpaSortTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Account.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Address.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Category.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Child.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Customer.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Dummy.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Invoice.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Item.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/ItemId.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/MailSender.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/MailUser.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Order.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Parent.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Product.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Role.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/Site.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/User.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/Person.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/config/package.html (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/custom/package-info.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/package.html (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/util/FixedDate.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java (100%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java (100%) rename {src => spring-data-jpa/src}/test/resources/META-INF/jpa-named-queries.properties (100%) rename {src => spring-data-jpa/src}/test/resources/META-INF/orm.xml (100%) rename {src => spring-data-jpa/src}/test/resources/META-INF/persistence.xml (100%) rename {src => spring-data-jpa/src}/test/resources/META-INF/persistence2.xml (100%) rename {src => spring-data-jpa/src}/test/resources/application-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/auditing/auditing-bfpp-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/auditing/auditing-entity-listener.xml (100%) rename {src => spring-data-jpa/src}/test/resources/auditing/auditing-namespace-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/auditing/auditing-namespace-context2.xml (100%) rename {src => spring-data-jpa/src}/test/resources/auditing/auditing-namespace-context3.xml (100%) rename {src => spring-data-jpa/src}/test/resources/config/jpa-context-with-jndi.xml (100%) rename {src => spring-data-jpa/src}/test/resources/config/lookup-strategies-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/config/namespace-application-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/config/namespace-autoconfig-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/config/namespace-autoconfig-typefilter-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/config/namespace-customfactory-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/config/namespace-nested-repositories-application-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/eclipselink.xml (100%) rename {src => spring-data-jpa/src}/test/resources/hibernate.xml (100%) rename {src => spring-data-jpa/src}/test/resources/infrastructure.xml (100%) rename {src => spring-data-jpa/src}/test/resources/logback.xml (100%) rename {src => spring-data-jpa/src}/test/resources/multiple-entity-manager-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/multiple-entity-manager-integration-context.xml (100%) rename {src => spring-data-jpa/src}/test/resources/openjpa.xml (100%) rename {src => spring-data-jpa/src}/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions.xml (100%) rename {src => spring-data-jpa/src}/test/resources/org/springframework/data/jpa/support/mapping.xml (100%) rename {src => spring-data-jpa/src}/test/resources/org/springframework/data/jpa/support/module1/module1-orm.xml (100%) rename {src => spring-data-jpa/src}/test/resources/org/springframework/data/jpa/support/module2/module2-orm.xml (100%) rename {src => spring-data-jpa/src}/test/resources/org/springframework/data/jpa/support/persistence.xml (100%) rename {src => spring-data-jpa/src}/test/resources/org/springframework/data/jpa/support/persistence2.xml (100%) rename {src => spring-data-jpa/src}/test/resources/scripts/hsqldb-init.sql (100%) rename {src => spring-data-jpa/src}/test/resources/scripts/mysql-stored-procedures.sql (100%) rename {src => spring-data-jpa/src}/test/resources/scripts/postgres-stored-procedures.sql (100%) rename {src => spring-data-jpa/src}/test/resources/scripts/schema-stored-procedures.sql (100%) rename {src => spring-data-jpa/src}/test/resources/simple-persistence/simple-persistence.xml (100%) rename {src => spring-data-jpa/src}/test/resources/tx-manager.xml (100%) create mode 100644 src/main/asciidoc/envers.adoc delete mode 100644 template.mf diff --git a/pom.xml b/pom.xml index 4ff3ce005f..3edbc5d5fa 100644 --- a/pom.xml +++ b/pom.xml @@ -5,11 +5,12 @@ 4.0.0 org.springframework.data - spring-data-jpa + spring-data-jpa-parent 3.0.0-SNAPSHOT + pom - Spring Data JPA - Spring Data module for JPA repositories. + Spring Data JPA Parent + Parent module for Spring Data JPA repositories. https://projects.spring.io/spring-data-jpa @@ -21,8 +22,6 @@ 16 - DATAJPA - 3.0.2 5.6.0.Final 8.0.23 @@ -32,12 +31,17 @@ org.hibernate - spring.data.jpa - reuseReports + + spring-data-envers + spring-data-jpa + spring-data-jpa-distribution + + + all-dbs @@ -89,73 +93,6 @@ - - docs - - - - - maven-antrun-plugin - 1.8 - - - copy-schemas - prepare-package - - run - - - - - - - - - - - package-and-attach-docs-zip - package - - run - - - - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.10 - - - attach-zip - - attach-artifact - - package - - - - - ${project.build.directory}/${project.artifactId}-${project.version}.zip - - zip - - - - - - - - - @@ -170,263 +107,10 @@ - - - - ${project.groupId} - spring-data-commons - ${springdata.commons} - - - - org.springframework - spring-orm - - - - org.springframework - spring-context - - - - org.springframework - spring-aop - - - - org.springframework - spring-tx - - - - org.springframework - spring-beans - - - - org.springframework - spring-instrument - provided - - - - org.springframework - spring-core - - - commons-logging - commons-logging - - - - - - org.aspectj - aspectjweaver - ${aspectj} - test - - - - org.springframework - spring-aspects - compile - true - - - - org.hsqldb - hsqldb - 2.5.1 - test - - - - - mysql - mysql-connector-java - ${mysql-connector-java} - test - - - - org.testcontainers - mysql - test - - - org.slf4j - jcl-over-slf4j - - - - - - - org.postgresql - postgresql - ${postgresql} - test - - - - org.testcontainers - postgresql - test - - - - io.vavr - vavr - ${vavr} - test - - - - - - org.eclipse.persistence - org.eclipse.persistence.jpa - ${eclipselink} - true - - - - ${hibernate.groupId} - hibernate-core-jakarta - ${hibernate} - true - - - - ${hibernate.groupId} - hibernate-jpamodelgen-jakarta - ${hibernate} - provided - - - - jakarta.annotation - jakarta.annotation-api - ${jakarta-annotation-api} - - - - - com.querydsl - querydsl-apt - ${querydsl} - jakarta - provided - - - - com.querydsl - querydsl-jpa - jakarta - ${querydsl} - true - - - - - org.jmolecules.integrations - jmolecules-spring - ${jmolecules-integration} - test - - - - - - org.apache.geronimo.specs - geronimo-jcdi_2.0_spec - 1.0.1 - optional - - - - jakarta.interceptor - jakarta.interceptor-api - 2.0.0 - test - - - - jakarta.enterprise - jakarta.enterprise.cdi-api - ${cdi} - provided - true - - - - - org.apache.openwebbeans - openwebbeans-se - ${webbeans} - jakarta - test - - - org.apache.openwebbeans - openwebbeans-impl - - - org.apache.openwebbeans - openwebbeans-spi - - - - - org.apache.openwebbeans - openwebbeans-impl - ${webbeans} - jakarta - test - - - org.apache.openwebbeans - openwebbeans-spi - ${webbeans} - jakarta - test - - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco} - - ${jacoco.destfile} - - - - jacoco-initialize - - prepare-agent - - - - - org.apache.maven.plugins maven-surefire-plugin @@ -520,65 +204,6 @@ - - io.starter - aspectj-maven-plugin - 1.12.9 - - - org.aspectj - aspectjrt - ${aspectj} - - - org.aspectj - aspectjtools - ${aspectj} - - - - - - compile - - process-classes - - - - true - - true - - - org.springframework - spring-aspects - - - - **/domain/support/AuditingEntityListener.java - - ${source.level} - ${source.level} - ${source.level} - aop.xml - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - org.asciidoctor - asciidoctor-maven-plugin - @@ -598,6 +223,10 @@ spring-libs-milestone https://repo.spring.io/libs-milestone + + spring-libs-snapshot + https://repo.spring.io/libs-snapshot + diff --git a/formatting.xml b/spring-data-envers/etc/eclipse-formatter.xml similarity index 100% rename from formatting.xml rename to spring-data-envers/etc/eclipse-formatter.xml diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml new file mode 100755 index 0000000000..5e82ca77cc --- /dev/null +++ b/spring-data-envers/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + org.springframework.data + spring-data-envers + 3.0.0-SNAPSHOT + + + org.springframework.data + spring-data-jpa-parent + 3.0.0-SNAPSHOT + ../pom.xml + + + Spring Data Envers + Spring Data extension to work with Hibernate Envers + + + + Oliver Gierke + ogierke@pivotal.io + Pivotal Software, Inc. + www.spring.io + + + Philip Huegelmeyer + philip.huegelmeyer@ble.de + BLE + www.ble.de + + + Michael Igler + michael.igler@forward-tech.de + Freelancer + + + Jens Schauder + jschauder@vmware.com + VMware, Inc. + www.spring.io + + + + + https://github.com/SpringSource/spring-data-envers + + + + 5.5.7.Final + spring.data.envers + + + + + + org.springframework.data + spring-data-jpa + ${project.version} + + + + + org.hibernate + hibernate-envers-jakarta + ${hibernate.envers} + + + + com.querydsl + querydsl-jpa + ${querydsl} + jakarta + true + + + + + com.h2database + h2 + 1.4.200 + test + + + + diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java new file mode 100644 index 0000000000..ba7582d347 --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java @@ -0,0 +1,199 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.config; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.persistence.EntityManagerFactory; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.annotation.AliasFor; +import org.springframework.data.envers.repository.support.EnversRevisionRepositoryFactoryBean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; +import org.springframework.data.repository.config.BootstrapMode; +import org.springframework.data.repository.config.DefaultRepositoryBaseClass; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryLookupStrategy.Key; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * Annotation to enable Envers repositories. Will scan the package of the annotated configuration class for Spring Data + * repositories by default. + *

+ * This annotation is a meta-annotation for {@link EnableJpaRepositories @EnableJpaRepositories} overriding the default + * {@link #repositoryFactoryBeanClass} to {@link EnversRevisionRepositoryFactoryBean}. + * + * @author Mark Paluch + * @since 2.5 + * @see EnableJpaRepositories + * @see AliasFor + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@EnableJpaRepositories +public @interface EnableEnversRepositories { + + /** + * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.: + * {@code @EnableJpaRepositories("org.my.pkg")} instead of + * {@code @EnableEnversRepositories(basePackages="org.my.pkg")}. + */ + @AliasFor(annotation = EnableJpaRepositories.class) + String[] value() default {}; + + /** + * Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this + * attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names. + */ + @AliasFor(annotation = EnableJpaRepositories.class) + String[] basePackages() default {}; + + /** + * Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The + * package of each class specified will be scanned. Consider creating a special no-op marker class or interface in + * each package that serves no purpose other than being referenced by this attribute. + */ + @AliasFor(annotation = EnableJpaRepositories.class) + Class[] basePackageClasses() default {}; + + /** + * Specifies which types are eligible for component scanning. Further narrows the set of candidate components from + * everything in {@link #basePackages()} to everything in the base packages that matches the given filter or filters. + */ + @AliasFor(annotation = EnableJpaRepositories.class) + Filter[] includeFilters() default {}; + + /** + * Specifies which types are not eligible for component scanning. + */ + @AliasFor(annotation = EnableJpaRepositories.class) + Filter[] excludeFilters() default {}; + + /** + * Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So + * for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning + * for {@code PersonRepositoryImpl}. + * + * @return + */ + @AliasFor(annotation = EnableJpaRepositories.class) + String repositoryImplementationPostfix() default "Impl"; + + /** + * Configures the location of where to find the Spring Data named queries properties file. Will default to + * {@code META-INF/jpa-named-queries.properties}. + * + * @return + */ + @AliasFor(annotation = EnableJpaRepositories.class) + String namedQueriesLocation() default ""; + + /** + * Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to + * {@link Key#CREATE_IF_NOT_FOUND}. + * + * @return + */ + @AliasFor(annotation = EnableJpaRepositories.class) + Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND; + + /** + * Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to + * {@link JpaRepositoryFactoryBean}. + * + * @return + */ + @AliasFor(annotation = EnableJpaRepositories.class) + Class repositoryFactoryBeanClass() default EnversRevisionRepositoryFactoryBean.class; + + /** + * Configure the repository base class to be used to create repository proxies for this particular configuration. + * + * @return + */ + @AliasFor(annotation = EnableJpaRepositories.class) + Class repositoryBaseClass() default DefaultRepositoryBaseClass.class; + + // JPA specific configuration + + /** + * Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories + * discovered through this annotation. Defaults to {@code entityManagerFactory}. + * + * @return + */ + @AliasFor(annotation = EnableJpaRepositories.class) + String entityManagerFactoryRef() default "entityManagerFactory"; + + /** + * Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories + * discovered through this annotation. Defaults to {@code transactionManager}. + * + * @return + */ + @AliasFor(annotation = EnableJpaRepositories.class) + String transactionManagerRef() default "transactionManager"; + + /** + * Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the + * repositories infrastructure. + */ + @AliasFor(annotation = EnableJpaRepositories.class) + boolean considerNestedRepositories() default false; + + /** + * Configures whether to enable default transactions for Spring Data JPA repositories. Defaults to {@literal true}. If + * disabled, repositories must be used behind a facade that's configuring transactions (e.g. using Spring's annotation + * driven transaction facilities) or repository methods have to be used to demarcate transactions. + * + * @return whether to enable default transactions, defaults to {@literal true}. + */ + @AliasFor(annotation = EnableJpaRepositories.class) + boolean enableDefaultTransactions() default true; + + /** + * Configures when the repositories are initialized in the bootstrap lifecycle. {@link BootstrapMode#DEFAULT} + * (default) means eager initialization except all repository interfaces annotated with {@link Lazy}, + * {@link BootstrapMode#LAZY} means lazy by default including injection of lazy-initialization proxies into client + * beans so that those can be instantiated but will only trigger the initialization upon first repository usage (i.e a + * method invocation on it). This means repositories can still be uninitialized when the application context has + * completed its bootstrap. {@link BootstrapMode#DEFERRED} is fundamentally the same as {@link BootstrapMode#LAZY}, + * but triggers repository initialization when the application context finishes its bootstrap. + * + * @return + */ + @AliasFor(annotation = EnableJpaRepositories.class) + BootstrapMode bootstrapMode() default BootstrapMode.DEFAULT; + + /** + * Configures what character is used to escape the wildcards {@literal _} and {@literal %} in derived queries with + * {@literal contains}, {@literal startsWith} or {@literal endsWith} clauses. + * + * @return a single character used for escaping. + */ + @AliasFor(annotation = EnableJpaRepositories.class) + char escapeCharacter() default '\\'; +} diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/package-info.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/package-info.java new file mode 100644 index 0000000000..2e79b25c03 --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/package-info.java @@ -0,0 +1,5 @@ +/** + * Classes for Envers Repositories configuration support. + */ +@org.springframework.lang.NonNullApi +package org.springframework.data.envers.repository.config; diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java new file mode 100644 index 0000000000..b01edde01d --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import org.hibernate.envers.DefaultRevisionEntity; +import org.springframework.data.repository.history.support.RevisionEntityInformation; + +/** + * {@link RevisionEntityInformation} for {@link DefaultRevisionEntity}. + * + * @author Oliver Gierke + */ +class DefaultRevisionEntityInformation implements RevisionEntityInformation { + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.history.support.RevisionEntityInformation#getRevisionNumberType() + */ + public Class getRevisionNumberType() { + return Integer.class; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.history.support.RevisionEntityInformation#isDefaultRevisionEntity() + */ + public boolean isDefaultRevisionEntity() { + return true; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.history.support.RevisionEntityInformation#getRevisionEntityClass() + */ + public Class getRevisionEntityClass() { + return DefaultRevisionEntity.class; + } +} diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java new file mode 100755 index 0000000000..912d10cf59 --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java @@ -0,0 +1,123 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Optional; + +import org.hibernate.envers.DefaultRevisionEntity; + +import org.springframework.data.history.RevisionMetadata; +import org.springframework.util.Assert; + +/** + * {@link RevisionMetadata} working with a {@link DefaultRevisionEntity}. The entity/delegate itself gets ignored for + * {@link #equals(Object)} and {@link #hashCode()} since they depend on the way they were obtained. + * + * @author Oliver Gierke + * @author Philip Huegelmeyer + * @author Jens Schauder + */ +public final class DefaultRevisionMetadata implements RevisionMetadata { + + private final DefaultRevisionEntity entity; + private final RevisionType revisionType; + + public DefaultRevisionMetadata(DefaultRevisionEntity entity) { + this(entity, RevisionType.UNKNOWN); + } + + public DefaultRevisionMetadata(DefaultRevisionEntity entity, RevisionType revisionType) { + + Assert.notNull(entity, "DefaultRevisionEntity must not be null"); + + this.entity = entity; + this.revisionType = revisionType; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.history.RevisionMetadata#getRevisionNumber() + */ + public Optional getRevisionNumber() { + return Optional.of(entity.getId()); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.history.RevisionMetadata#getRevisionDate() + */ + @Deprecated + public Optional getRevisionDate() { + return getRevisionInstant().map(instant -> LocalDateTime.ofInstant(instant, ZoneOffset.systemDefault())); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.history.RevisionMetadata#getRevisionInstant() + */ + @Override + public Optional getRevisionInstant() { + return Optional.of(Instant.ofEpochMilli(entity.getTimestamp())); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.history.RevisionMetadata#getDelegate() + */ + @SuppressWarnings("unchecked") + public T getDelegate() { + return (T) entity; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.history.RevisionMetadata#getRevisionType() + */ + @Override + public RevisionType getRevisionType() { + return revisionType; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultRevisionMetadata that = (DefaultRevisionMetadata) o; + return getRevisionNumber().equals(that.getRevisionNumber()) + && getRevisionInstant().equals(that.getRevisionInstant()) && revisionType.equals(that.getRevisionType()); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "DefaultRevisionMetadata{" + "entity=" + entity + ", revisionType=" + revisionType + '}'; + } +} diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java new file mode 100644 index 0000000000..79beff3d62 --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import java.io.Serializable; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; +import org.springframework.data.repository.history.RevisionRepository; + +/** + * Convenience interface to allow pulling in {@link JpaRepository} and {@link RevisionRepository} functionality in one + * go. + * + * @author Oliver Gierke + * @author Michael Igler + * @deprecated since 1.1, in favor of simply extending {@link RevisionRepository}. + */ +@Deprecated +@NoRepositoryBean +public interface EnversRevisionRepository> + extends RevisionRepository, JpaRepository { + +} diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java new file mode 100755 index 0000000000..12819f261c --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java @@ -0,0 +1,113 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import java.util.Optional; + +import jakarta.persistence.EntityManager; + +import org.hibernate.envers.DefaultRevisionEntity; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; +import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments; +import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.data.repository.history.RevisionRepository; +import org.springframework.data.repository.history.support.RevisionEntityInformation; + +/** + * {@link FactoryBean} creating {@link RevisionRepository} instances. + * + * @author Oliver Gierke + * @author Michael Igler + */ +public class EnversRevisionRepositoryFactoryBean, S, ID, N extends Number & Comparable> + extends JpaRepositoryFactoryBean { + + private Class revisionEntityClass; + + /** + * Creates a new {@link EnversRevisionRepositoryFactoryBean} for the given repository interface. + * + * @param repositoryInterface must not be {@literal null}. + */ + public EnversRevisionRepositoryFactoryBean(Class repositoryInterface) { + super(repositoryInterface); + } + + /** + * Configures the revision entity class. Will default to {@link DefaultRevisionEntity}. + * + * @param revisionEntityClass + */ + public void setRevisionEntityClass(Class revisionEntityClass) { + this.revisionEntityClass = revisionEntityClass; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean#createRepositoryFactory(jakarta.persistence.EntityManager) + */ + @Override + protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { + return new RevisionRepositoryFactory(entityManager, revisionEntityClass); + } + + /** + * Repository factory creating {@link RevisionRepository} instances. + * + * @author Oliver Gierke + * @author Jens Schauder + */ + private static class RevisionRepositoryFactory> extends JpaRepositoryFactory { + + private final RevisionEntityInformation revisionEntityInformation; + private final EntityManager entityManager; + + /** + * Creates a new {@link RevisionRepositoryFactory} using the given {@link EntityManager} and revision entity class. + * + * @param entityManager must not be {@literal null}. + * @param revisionEntityClass can be {@literal null}, will default to {@link DefaultRevisionEntity}. + */ + public RevisionRepositoryFactory(EntityManager entityManager, Class revisionEntityClass) { + + super(entityManager); + + this.entityManager = entityManager; + this.revisionEntityInformation = Optional.ofNullable(revisionEntityClass) // + .filter(it -> !it.equals(DefaultRevisionEntity.class))// + . map(ReflectionRevisionEntityInformation::new) // + .orElseGet(DefaultRevisionEntityInformation::new); + } + + @Override + protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) { + + Object fragmentImplementation = getTargetRepositoryViaReflection( // + EnversRevisionRepositoryImpl.class, // + getEntityInformation(metadata.getDomainType()), // + revisionEntityInformation, // + entityManager // + ); + + return RepositoryFragments // + .just(fragmentImplementation) // + .append(super.getRepositoryFragments(metadata)); + } + } +} diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java new file mode 100755 index 0000000000..dae06837f9 --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -0,0 +1,230 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import static org.springframework.data.history.RevisionMetadata.RevisionType.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import jakarta.persistence.EntityManager; + +import org.hibernate.envers.AuditReader; +import org.hibernate.envers.AuditReaderFactory; +import org.hibernate.envers.DefaultRevisionEntity; +import org.hibernate.envers.RevisionNumber; +import org.hibernate.envers.RevisionTimestamp; +import org.hibernate.envers.RevisionType; +import org.hibernate.envers.query.AuditEntity; +import org.hibernate.envers.query.AuditQuery; +import org.hibernate.envers.query.order.AuditOrder; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.history.AnnotationRevisionMetadata; +import org.springframework.data.history.Revision; +import org.springframework.data.history.RevisionMetadata; +import org.springframework.data.history.RevisionSort; +import org.springframework.data.history.Revisions; +import org.springframework.data.jpa.repository.support.JpaEntityInformation; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.history.RevisionRepository; +import org.springframework.data.repository.history.support.RevisionEntityInformation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +/** + * Repository implementation using Hibernate Envers to implement revision specific query methods. + * + * @author Oliver Gierke + * @author Philipp Huegelmeyer + * @author Michael Igler + * @author Jens Schauder + * @author Julien Millau + * @author Mark Paluch + * @author Sander Bylemans + */ +@Transactional(readOnly = true) +public class EnversRevisionRepositoryImpl> + implements RevisionRepository { + + private final EntityInformation entityInformation; + private final EntityManager entityManager; + + /** + * Creates a new {@link EnversRevisionRepositoryImpl} using the given {@link JpaEntityInformation}, + * {@link RevisionEntityInformation} and {@link EntityManager}. + * + * @param entityInformation must not be {@literal null}. + * @param revisionEntityInformation must not be {@literal null}. + * @param entityManager must not be {@literal null}. + */ + public EnversRevisionRepositoryImpl(JpaEntityInformation entityInformation, + RevisionEntityInformation revisionEntityInformation, EntityManager entityManager) { + + Assert.notNull(revisionEntityInformation, "RevisionEntityInformation must not be null!"); + + this.entityInformation = entityInformation; + this.entityManager = entityManager; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.history.RevisionRepository#findLastChangeRevision(java.io.Serializable) + */ + @SuppressWarnings("unchecked") + public Optional> findLastChangeRevision(ID id) { + + List singleResult = createBaseQuery(id) // + .addOrder(AuditEntity.revisionProperty("timestamp").desc()) // + .setMaxResults(1) // + .getResultList(); + + Assert.state(singleResult.size() <= 1, "We expect at most one result."); + + if (singleResult.isEmpty()) { + return Optional.empty(); + } + + return Optional.of(createRevision(new QueryResult<>(singleResult.get(0)))); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.envers.repository.support.EnversRevisionRepository#findRevision(java.io.Serializable, java.lang.Number) + */ + @Override + @SuppressWarnings("unchecked") + public Optional> findRevision(ID id, N revisionNumber) { + + Assert.notNull(id, "Identifier must not be null!"); + Assert.notNull(revisionNumber, "Revision number must not be null!"); + + List singleResult = (List) createBaseQuery(id) // + .add(AuditEntity.revisionNumber().eq(revisionNumber)) // + .getResultList(); + + Assert.state(singleResult.size() <= 1, "We expect at most one result."); + + if (singleResult.isEmpty()) { + return Optional.empty(); + } + + return Optional.of(createRevision(new QueryResult<>(singleResult.get(0)))); + } + + @SuppressWarnings("unchecked") + public Revisions findRevisions(ID id) { + + List resultList = createBaseQuery(id).getResultList(); + List> revisionList = new ArrayList<>(resultList.size()); + + for (Object[] objects : resultList) { + revisionList.add(createRevision(new QueryResult<>(objects))); + } + + return Revisions.of(revisionList); + } + + @SuppressWarnings("unchecked") + public Page> findRevisions(ID id, Pageable pageable) { + + AuditOrder sorting = RevisionSort.getRevisionDirection(pageable.getSort()).isDescending() // + ? AuditEntity.revisionNumber().desc() // + : AuditEntity.revisionNumber().asc(); + + List resultList = createBaseQuery(id) // + .addOrder(sorting) // + .setFirstResult((int) pageable.getOffset()) // + .setMaxResults(pageable.getPageSize()) // + .getResultList(); + + Long count = (Long) createBaseQuery(id) // + .addProjection(AuditEntity.revisionNumber().count()).getSingleResult(); + + List> revisions = new ArrayList<>(); + + for (Object[] singleResult : resultList) { + revisions.add(createRevision(new QueryResult<>(singleResult))); + } + + return new PageImpl<>(revisions, pageable, count); + } + + private AuditQuery createBaseQuery(ID id) { + + Class type = entityInformation.getJavaType(); + AuditReader reader = AuditReaderFactory.get(entityManager); + + return reader.createQuery() // + .forRevisionsOfEntity(type, false, true) // + .add(AuditEntity.id().eq(id)); + } + + @SuppressWarnings("unchecked") + private Revision createRevision(QueryResult queryResult) { + return Revision.of((RevisionMetadata) queryResult.createRevisionMetadata(), queryResult.entity); + } + + @SuppressWarnings("unchecked") + static class QueryResult { + + private final T entity; + private final Object metadata; + private final RevisionMetadata.RevisionType revisionType; + + QueryResult(Object[] data) { + + Assert.notNull(data, "Data must not be null"); + Assert.isTrue( // + data.length == 3, // + () -> String.format("Data must have length three, but has length %d.", data.length)); + Assert.isTrue( // + data[2] instanceof RevisionType, // + () -> String.format("The third array element must be of type Revision type, but is of type %s", + data[2].getClass())); + + entity = (T) data[0]; + metadata = data[1]; + revisionType = convertRevisionType((RevisionType) data[2]); + } + + RevisionMetadata createRevisionMetadata() { + + return metadata instanceof DefaultRevisionEntity // + ? new DefaultRevisionMetadata((DefaultRevisionEntity) metadata, revisionType) // + : new AnnotationRevisionMetadata<>(metadata, RevisionNumber.class, RevisionTimestamp.class, revisionType); + } + + private static RevisionMetadata.RevisionType convertRevisionType(RevisionType datum) { + + switch (datum) { + + case ADD: + return INSERT; + case MOD: + return UPDATE; + case DEL: + return DELETE; + default: + return UNKNOWN; + } + } + } + +} diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java new file mode 100644 index 0000000000..6edd4e9ad9 --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import org.hibernate.envers.RevisionNumber; + +import org.springframework.data.repository.history.support.RevisionEntityInformation; +import org.springframework.data.util.AnnotationDetectionFieldCallback; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +/** + * {@link RevisionEntityInformation} that uses reflection to inspect a property annotated with {@link RevisionNumber} to + * find out about the revision number type. + * + * @author Oliver Gierke + */ +public class ReflectionRevisionEntityInformation implements RevisionEntityInformation { + + private final Class revisionEntityClass; + private final Class revisionNumberType; + + /** + * Creates a new {@link ReflectionRevisionEntityInformation} inspecting the given revision entity class. + * + * @param revisionEntityClass must not be {@literal null}. + */ + public ReflectionRevisionEntityInformation(Class revisionEntityClass) { + + Assert.notNull(revisionEntityClass, "Revision entity type must not be null!"); + + AnnotationDetectionFieldCallback fieldCallback = new AnnotationDetectionFieldCallback(RevisionNumber.class); + ReflectionUtils.doWithFields(revisionEntityClass, fieldCallback); + + this.revisionNumberType = fieldCallback.getRequiredType(); + this.revisionEntityClass = revisionEntityClass; + + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.history.support.RevisionEntityInformation#isDefaultRevisionEntity() + */ + public boolean isDefaultRevisionEntity() { + return false; + } + + public Class getRevisionEntityClass() { + return this.revisionEntityClass; + } + + public Class getRevisionNumberType() { + return this.revisionNumberType; + } +} diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/package-info.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/package-info.java new file mode 100644 index 0000000000..dd135fdacf --- /dev/null +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/package-info.java @@ -0,0 +1,5 @@ +/** + * Spring Data JPA specific converter infrastructure. + */ +@org.springframework.lang.NonNullApi +package org.springframework.data.envers.repository.support; diff --git a/spring-data-envers/src/main/resources/changelog.txt b/spring-data-envers/src/main/resources/changelog.txt new file mode 100644 index 0000000000..01822a191c --- /dev/null +++ b/spring-data-envers/src/main/resources/changelog.txt @@ -0,0 +1,260 @@ +Spring Data Envers Changelog +========================== + +Changes in version 2.6.0-M1 (2021-07-16) +---------------------------------------- +* #305 - Upgrade to Envers 5.5.3.Final. +* #288 - Update CI to Java 16. + + +Changes in version 2.4.11 (2021-07-16) +-------------------------------------- + + +Changes in version 2.5.2 (2021-06-22) +------------------------------------- + + +Changes in version 2.4.10 (2021-06-22) +-------------------------------------- + + +Changes in version 2.5.1 (2021-05-14) +------------------------------------- + + +Changes in version 2.4.9 (2021-05-14) +------------------------------------- + + +Changes in version 2.5.0 (2021-04-14) +------------------------------------- + + +Changes in version 2.4.8 (2021-04-14) +------------------------------------- + + +Changes in version 2.3.9.RELEASE (2021-04-14) +--------------------------------------------- + + +Changes in version 2.4.7 (2021-03-31) +------------------------------------- + + +Changes in version 2.5.0-RC1 (2021-03-31) +----------------------------------------- +* #289 - Investigate `@EnableEnversRepositories` meta-annotation. + + +Changes in version 2.5.0-M5 (2021-03-17) +---------------------------------------- +* #283 - Editing pass. +* #282 - Add Java config example, fix StackOverflow tag URL. +* #61 - Spring Data Envers Documentation Only Contains General Spring Data Information. + + +Changes in version 2.4.6 (2021-03-17) +------------------------------------- +* #283 - Editing pass. +* #282 - Add Java config example, fix StackOverflow tag URL. +* #61 - Spring Data Envers Documentation Only Contains General Spring Data Information. + + +Changes in version 2.3.8.RELEASE (2021-03-17) +--------------------------------------------- +* #283 - Editing pass. +* #282 - Add Java config example, fix StackOverflow tag URL. +* #61 - Spring Data Envers Documentation Only Contains General Spring Data Information. + + +Changes in version 2.5.0-M4 (2021-02-18) +---------------------------------------- + + +Changes in version 2.4.5 (2021-02-18) +------------------------------------- + + +Changes in version 2.5.0-M3 (2021-02-17) +---------------------------------------- +* #265 - Enable Project automation through GitHub Actions. + + +Changes in version 2.4.4 (2021-02-17) +------------------------------------- + + +Changes in version 2.3.7.RELEASE (2021-02-17) +--------------------------------------------- +* #271 - Update copyright year to 2021. +* #266 - Update CI jobs with Docker Login. + + +Changes in version 2.5.0-M2 (2021-01-13) +---------------------------------------- +* #271 - Update copyright year to 2021. +* #266 - Update CI jobs with Docker Login. + + +Changes in version 2.4.3 (2021-01-13) +------------------------------------- +* #271 - Update copyright year to 2021. +* #266 - Update CI jobs with Docker Login. + + +Changes in version 2.4.2 (2020-12-09) +------------------------------------- +* #263 - Release 2.4.2 (2020.0.2). + + +Changes in version 2.5.0-M1 (2020-12-09) +---------------------------------------- +* #265 - Enable Project automation through GitHub Actions. +* #262 - Release 2.5 M1 (2021.0.0). + + +Changes in version 2.3.6.RELEASE (2020-12-09) +--------------------------------------------- +* #260 - Release 2.3.6 (Neumann SR6). + + +Changes in version 2.4.1 (2020-11-11) +------------------------------------- +* #261 - Release 2.4.1 (2020.0.1). + + +Changes in version 2.4.0 (2020-10-28) +------------------------------------- +* #257 - Release 2.4 GA (2020.0.0). + + +Changes in version 2.3.5.RELEASE (2020-10-28) +--------------------------------------------- +* #254 - Release 2.3.5 (Neumann SR5). + + +Changes in version 2.4.0-RC2 (2020-10-14) +----------------------------------------- +* #256 - Update CI jobs for Java 15. +* #255 - Release 2.4 RC2 (2020.0.0). + + +Changes in version 2.4.0-RC1 (2020-09-16) +----------------------------------------- +* #251 - Release 2.4 RC1 (2020.0.0). + + +Changes in version 2.3.4.RELEASE (2020-09-16) +--------------------------------------------- +* #252 - Release 2.3.4 (Neumann SR4). + + +Changes in version 2.3.3.RELEASE (2020-08-12) +--------------------------------------------- +* #247 - Release 2.3.3 (Neumann SR3). + + +Changes in version 2.4.0-M2 (2020-08-12) +---------------------------------------- +* #244 - Release 2.4 M2 (2020.0.0). + + +Changes in version 2.3.2.RELEASE (2020-07-22) +--------------------------------------------- +* #242 - Release 2.3.2 (Neumann SR2). + + +Changes in version 2.4.0-M1 (2020-06-25) +---------------------------------------- +* #239 - Use standard Spring code of conduct. +* #238 - Delombok source files. +* #237 - Release 2.4 M1 (2020.0.0). + + +Changes in version 2.3.1.RELEASE (2020-06-10) +--------------------------------------------- +* #236 - Release 2.3.1 (Neumann SR1). + + +Changes in version 2.3.0.RELEASE (2020-05-12) +--------------------------------------------- +* #234 - Release 2.3 GA (Neumann). + + +Changes in version 2.3.0.RC2 (2020-04-28) +----------------------------------------- +* #230 - Use JDK 14 for Java.NEXT CI testing. +* #229 - Release 2.3 RC2 (Neumann). +* #215 - RevisionType always Unknown in RevisionMetadata. + + +Changes in version 2.3.0.RC1 (2020-03-31) +----------------------------------------- +* #224 - Release 2.3 RC1 (Neumann). + + +Changes in version 2.3.0.M4 (2020-03-11) +---------------------------------------- +* #221 - Release 2.3 M4 (Neumann). + + +Changes in version 2.3.0.M3 (2020-02-12) +---------------------------------------- +* #220 - Release 2.3 M3 (Neumann). + + +Changes in version 2.3.0.M2 (2020-01-17) +---------------------------------------- +* #219 - Release 2.3 M2 (Neumann). + + +Changes in version 2.3.0.M1 (2020-01-16) +---------------------------------------- +* #210 - Upgrade to Envers 5.4.8. +* #205 - Release 2.3 M1 (Neumann). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-data-envers/src/main/resources/license.txt b/spring-data-envers/src/main/resources/license.txt new file mode 100644 index 0000000000..4211577074 --- /dev/null +++ b/spring-data-envers/src/main/resources/license.txt @@ -0,0 +1,279 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +======================================================================= + +SPRING FRAMEWORK ${version} SUBCOMPONENTS: + +Spring Framework ${version} includes a number of subcomponents +with separate copyright notices and license terms. The product that +includes this file does not necessarily use all the open source +subcomponents referred to below. Your use of the source +code for these subcomponents is subject to the terms and +conditions of the following licenses. + + +>>> ASM 4.0 (org.ow2.asm:asm:4.0, org.ow2.asm:asm-commons:4.0): + +Copyright (c) 2000-2011 INRIA, France Telecom +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (c) 1999-2009, OW2 Consortium + + +>>> CGLIB 3.0 (cglib:cglib:3.0): + +Per the LICENSE file in the CGLIB JAR distribution downloaded from +https://sourceforge.net/projects/cglib/files/cglib3/3.0/cglib-3.0.jar/download, +CGLIB 3.0 is licensed under the Apache License, version 2.0, the text of which +is included above. + + +======================================================================= + +To the extent any open source subcomponents are licensed under the EPL and/or +other similar licenses that require the source code and/or modifications to +source code to be made available (as would be noted above), you may obtain a +copy of the source code corresponding to the binaries for such open source +components and modifications thereto, if any, (the "Source Files"), by +downloading the Source Files from https://www.springsource.org/download, or by +sending a request, with your name and address to: + + VMware, Inc., 3401 Hillview Avenue + Palo Alto, CA 94304 + United States of America + +or email info@vmware.com. All such requests should clearly specify: + + OPEN SOURCE FILES REQUEST + Attention General Counsel + +VMware shall mail a copy of the Source Files to you on a CD or equivalent +physical medium. This offer to obtain a copy of the Source Files is valid for +three years from the date you acquired this Software product. \ No newline at end of file diff --git a/spring-data-envers/src/main/resources/notice.txt b/spring-data-envers/src/main/resources/notice.txt new file mode 100644 index 0000000000..2eadbe8f33 --- /dev/null +++ b/spring-data-envers/src/main/resources/notice.txt @@ -0,0 +1,33 @@ +Spring Data Envers 2.6 M3 (2021.1.0) +Copyright (c) 2015-2019 Pivotal Software, Inc. + +This product is licensed to you under the Apache License, Version 2.0 +(the "License"). You may not use this product except in compliance with +the License. + +This product may include a number of subcomponents with separate +copyright notices and license terms. Your use of the source code for +these subcomponents is subject to the terms and conditions of the +subcomponent's license, as noted in the license.txt file. + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java b/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java new file mode 100755 index 0000000000..93ad2f51bd --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers; + +import java.sql.SQLException; +import java.util.Collections; +import java.util.Map; + +import javax.sql.DataSource; + +import org.hibernate.envers.strategy.ValidityAuditStrategy; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.envers.repository.config.EnableEnversRepositories; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * Spring JavaConfig configuration for general infrastructure. + * + * @author Oliver Gierke + */ +@Configuration +@EnableEnversRepositories +public class Config { + + @Bean + public DataSource dataSource() throws SQLException { + return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build(); + } + + @Bean + public PlatformTransactionManager transactionManager() throws SQLException { + return new JpaTransactionManager(); + } + + @Bean + public AbstractEntityManagerFactoryBean entityManagerFactory() throws SQLException { + + HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); + jpaVendorAdapter.setDatabase(Database.H2); + jpaVendorAdapter.setGenerateDdl(true); + + LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean(); + bean.setJpaVendorAdapter(jpaVendorAdapter); + bean.setPackagesToScan(Config.class.getPackage().getName()); + bean.setDataSource(dataSource()); + bean.setJpaPropertyMap(jpaProperties()); + + return bean; + } + + private Map jpaProperties() { + return Collections.singletonMap("org.hibernate.envers.audit_strategy", ValidityAuditStrategy.class.getName()); + } +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java new file mode 100644 index 0000000000..9a41760826 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import static org.assertj.core.api.Assertions.*; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; + +import org.hibernate.envers.DefaultRevisionEntity; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link DefaultRevisionMetadata}. + * + * @author Benedikt Ritter + * @author Jens Schauder + * @author Mark Paluch + */ +class DefaultRevisionMetadataUnitTests { + + private static final Instant NOW = Instant.now();; + + @Test // #112 + void createsLocalDateTimeFromTimestamp() { + + DefaultRevisionEntity entity = new DefaultRevisionEntity(); + entity.setTimestamp(NOW.toEpochMilli()); + + DefaultRevisionMetadata metadata = new DefaultRevisionMetadata(entity); + + assertThat(metadata.getRevisionDate()) + .hasValue(LocalDateTime.ofInstant(NOW, ZoneOffset.systemDefault()).truncatedTo(ChronoUnit.MILLIS)); + } +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java new file mode 100644 index 0000000000..fbbe1d0d04 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.hibernate.envers.DefaultRevisionEntity; +import org.hibernate.envers.RevisionType; +import org.junit.jupiter.api.Test; +import org.springframework.data.history.AnnotationRevisionMetadata; +import org.springframework.data.history.RevisionMetadata; + +/** + * Unit tests for {@link EnversRevisionRepositoryImpl}. + * + * @author Jens Schauder + */ +class EnversRevisionRepositoryImplUnitTests { + + @Test // gh-215 + void revisionTypeOfAnnotationRevisionMetadataIsProperlySet() { + + Object[] data = new Object[] { "a", "some metadata", RevisionType.DEL }; + + EnversRevisionRepositoryImpl.QueryResult result = new EnversRevisionRepositoryImpl.QueryResult<>(data); + + RevisionMetadata revisionMetadata = result.createRevisionMetadata(); + + assertThat(revisionMetadata).isInstanceOf(AnnotationRevisionMetadata.class); + assertThat(revisionMetadata.getRevisionType()).isEqualTo(RevisionMetadata.RevisionType.DELETE); + } + + @Test // gh-215 + void revisionTypeOfDefaultRevisionMetadataIsProperlySet() { + + Object[] data = new Object[] { "a", mock(DefaultRevisionEntity.class), RevisionType.DEL }; + + EnversRevisionRepositoryImpl.QueryResult result = new EnversRevisionRepositoryImpl.QueryResult<>(data); + + RevisionMetadata revisionMetadata = result.createRevisionMetadata(); + + assertThat(revisionMetadata).isInstanceOf(DefaultRevisionMetadata.class); + assertThat(revisionMetadata.getRevisionType()).isEqualTo(RevisionMetadata.RevisionType.DELETE); + } + +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java new file mode 100755 index 0000000000..751673d305 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java @@ -0,0 +1,97 @@ +/* + * Copyright 2018-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Iterator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.envers.Config; +import org.springframework.data.envers.sample.Country; +import org.springframework.data.envers.sample.CountryQueryDslRepository; +import org.springframework.data.envers.sample.QCountry; +import org.springframework.data.history.Revision; +import org.springframework.data.history.Revisions; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Integration tests for repositories with Querydsl support. They make sure that methods provided by both + * {@link org.springframework.data.repository.history.RevisionRepository} and {@link org.springframework.data.querydsl.QuerydslPredicateExecutor} are working. + * + * @author Dmytro Iaroslavskyi + * @author Jens Schauder + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = Config.class) +class QueryDslRepositoryIntegrationTests { + + @Autowired + CountryQueryDslRepository countryRepository; + + @BeforeEach + void setUp() { + countryRepository.deleteAll(); + } + + @Test + void testWithQueryDsl() { + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + + countryRepository.save(de); + + Country found = countryRepository.findOne(QCountry.country.name.eq("Deutschland")).get(); + + assertThat(found).isNotNull(); + assertThat(found.id).isEqualTo(de.id); + } + + @Test + void testWithRevisions() { + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + + countryRepository.save(de); + + de.name = "Germany"; + + countryRepository.save(de); + + Revisions revisions = countryRepository.findRevisions(de.id); + + assertThat(revisions).hasSize(2); + + Iterator> iterator = revisions.iterator(); + + Integer firstRevisionNumber = iterator.next().getRevisionNumber().get(); + Integer secondRevisionNumber = iterator.next().getRevisionNumber().get(); + + assertThat(countryRepository.findRevision(de.id, firstRevisionNumber).get().getEntity().name) + .isEqualTo("Deutschland"); + assertThat(countryRepository.findRevision(de.id, secondRevisionNumber).get().getEntity().name).isEqualTo("Germany"); + } + +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java new file mode 100755 index 0000000000..126e903990 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java @@ -0,0 +1,251 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.repository.support; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.history.RevisionMetadata.RevisionType.*; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Optional; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.envers.Config; +import org.springframework.data.envers.sample.Country; +import org.springframework.data.envers.sample.CountryRepository; +import org.springframework.data.envers.sample.License; +import org.springframework.data.envers.sample.LicenseRepository; +import org.springframework.data.history.Revision; +import org.springframework.data.history.RevisionSort; +import org.springframework.data.history.Revisions; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Integration tests for repositories. + * + * @author Oliver Gierke + * @author Jens Schauder + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = Config.class) +class RepositoryIntegrationTests { + + @Autowired + LicenseRepository licenseRepository; + @Autowired + CountryRepository countryRepository; + + @BeforeEach + void setUp() { + + licenseRepository.deleteAll(); + countryRepository.deleteAll(); + } + + @AfterEach + void tearDown() { + + licenseRepository.deleteAll(); + countryRepository.deleteAll(); + } + + @Test + void testLifeCycle() { + + License license = new License(); + license.name = "Schnitzel"; + + licenseRepository.save(license); + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + + countryRepository.save(de); + + Country se = new Country(); + se.code = "se"; + se.name = "Schweden"; + + countryRepository.save(se); + + license.laender = new HashSet<>(); + license.laender.addAll(Arrays.asList(de, se)); + + licenseRepository.save(license); + + de.name = "Daenemark"; + + countryRepository.save(de); + + Optional> revision = licenseRepository.findLastChangeRevision(license.id); + + assertThat(revision).hasValueSatisfying(it -> { + + Page> page = licenseRepository.findRevisions(license.id, PageRequest.of(0, 10)); + Revisions revisions = Revisions.of(page.getContent()); + assertThat(revisions.getLatestRevision()).isEqualTo(it); + }); + } + + @Test // #1 + void returnsEmptyLastRevisionForUnrevisionedEntity() { + assertThat(countryRepository.findLastChangeRevision(100L)).isEmpty(); + } + + @Test // #47 + void returnsEmptyRevisionsForUnrevisionedEntity() { + assertThat(countryRepository.findRevisions(100L)).isEmpty(); + } + + @Test // #47 + void returnsEmptyRevisionForUnrevisionedEntity() { + assertThat(countryRepository.findRevision(100L, 23)).isEmpty(); + } + + @Test // #31 + void returnsParticularRevisionForAnEntity() { + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + + countryRepository.save(de); + + de.name = "Germany"; + + countryRepository.save(de); + + Revisions revisions = countryRepository.findRevisions(de.id); + + assertThat(revisions).hasSize(2); + + Iterator> iterator = revisions.iterator(); + Revision first = iterator.next(); + Revision second = iterator.next(); + + assertThat(countryRepository.findRevision(de.id, first.getRequiredRevisionNumber())) + .hasValueSatisfying(it -> assertThat(it.getEntity().name).isEqualTo("Deutschland")); + + assertThat(countryRepository.findRevision(de.id, second.getRequiredRevisionNumber())) + .hasValueSatisfying(it -> assertThat(it.getEntity().name).isEqualTo("Germany")); + } + + @Test // #55 + void considersRevisionNumberSortOrder() { + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + + countryRepository.save(de); + + de.name = "Germany"; + + countryRepository.save(de); + + Page> page = countryRepository.findRevisions(de.id, + PageRequest.of(0, 10, RevisionSort.desc())); + + assertThat(page).hasSize(2); + assertThat(page.getContent().get(0).getRequiredRevisionNumber()) + .isGreaterThan(page.getContent().get(1).getRequiredRevisionNumber()); + } + + @Test // #21 + void findsDeletedRevisions() { + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + + countryRepository.save(de); + + countryRepository.delete(de); + + Revisions revisions = countryRepository.findRevisions(de.id); + + assertThat(revisions).hasSize(2); + assertThat(revisions.getLatestRevision().getEntity()) // + .isNotNull() // + .extracting(c -> c.name, c -> c.code) // + .containsExactly(null, null); + } + + @Test // #47 + void includesCorrectRevisionType() { + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + + countryRepository.save(de); + + de.name = "Bundes Republik Deutschland"; + + countryRepository.save(de); + + countryRepository.delete(de); + + Revisions revisions = countryRepository.findRevisions(de.id); + + assertThat(revisions) // + .extracting(r -> r.getMetadata().getRevisionType()) // + .containsExactly( // + INSERT, // + UPDATE, // + DELETE // + ); + } + + @Test // #146 + void shortCircuitingWhenOffsetIsToLarge() { + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + + countryRepository.save(de); + + countryRepository.delete(de); + + check(de.id, 0, 1, 2); + check(de.id, 1, 1, 2); + check(de.id, 2, 0, 2); + } + + @Test // #47 + void paginationWithEmptyResult() { + + check(23L, 0, 0, 0); + } + + void check(Long id, int page, int expectedSize, int expectedTotalSize) { + + Page> revisions = countryRepository.findRevisions(id, PageRequest.of(page, 1)); + assertThat(revisions).hasSize(expectedSize); + assertThat(revisions.getTotalElements()).isEqualTo(expectedTotalSize); + } +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java new file mode 100644 index 0000000000..5d94361b20 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import lombok.EqualsAndHashCode; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +@EqualsAndHashCode +abstract class AbstractEntity { + + public @Id @GeneratedValue Long id; +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java new file mode 100755 index 0000000000..e0d79c654a --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import jakarta.persistence.Entity; + +import lombok.ToString; +import org.hibernate.envers.Audited; + +/** + * Sample domain class. + * + * @author Oliver Gierke + * @author Jens Schauder + */ +@Audited +@Entity +@ToString +public class Country extends AbstractEntity { + + public String code; + public String name; +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java new file mode 100755 index 0000000000..c78dc44ea5 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java @@ -0,0 +1,31 @@ +/* + * Copyright 2015-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import org.springframework.data.envers.repository.support.EnversRevisionRepository; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; + +/** + * Repository with QueryDsl support for {@link Country} objects. + * + * @author Dmytro Iaroslavskyi + */ +public interface CountryQueryDslRepository + extends EnversRevisionRepository, JpaRepository, + QuerydslPredicateExecutor { + +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java new file mode 100755 index 0000000000..e9e80bf105 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.history.RevisionRepository; + +/** + * Repository for {@link Country} objects. + * + * @author Oliver Gierke + */ +public interface CountryRepository extends RevisionRepository, JpaRepository { + +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java new file mode 100755 index 0000000000..fd47bb1a6f --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import java.util.Set; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Version; + +import org.hibernate.envers.Audited; + +/** + * Sample domain class. + * + * @author Philip Huegelmeyer + */ +@Audited +@Entity +public class License extends AbstractEntity { + + @Version + public Integer version; + + public String name; + @ManyToMany + public Set laender; +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java new file mode 100755 index 0000000000..37a887db9a --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.history.RevisionRepository; + +/** + * Repository for {@link License} objects. + * + * @author Oliver Gierke + */ +public interface LicenseRepository extends RevisionRepository, JpaRepository { + +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java new file mode 100755 index 0000000000..456805ce8f --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import com.querydsl.core.types.Path; +import com.querydsl.core.types.PathMetadata; +import com.querydsl.core.types.dsl.EntityPathBase; +import com.querydsl.core.types.dsl.PathInits; +import com.querydsl.core.types.dsl.StringPath; + +import static com.querydsl.core.types.PathMetadataFactory.forVariable; + +/** + * Query class for Country domain. + * + * @author Dmytro Iaroslavskyi + */ +public class QCountry extends EntityPathBase { + + private static final long serialVersionUID = -936338527; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QCountry country = new QCountry("country"); + + public final StringPath code = createString("code"); + public final StringPath name = createString("name"); + + public QCountry(String variable) { + this(Country.class, forVariable(variable), INITS); + } + + @SuppressWarnings("all") + public QCountry(Path path) { + this((Class) path.getType(), path.getMetadata(), path.getMetadata().isRoot() ? INITS : PathInits.DEFAULT); + } + + public QCountry(PathMetadata metadata) { + this(metadata, metadata.isRoot() ? INITS : PathInits.DEFAULT); + } + + public QCountry(PathMetadata metadata, PathInits inits) { + this(Country.class, metadata, inits); + } + + public QCountry(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + } + +} diff --git a/spring-data-envers/src/test/resources/logback.xml b/spring-data-envers/src/test/resources/logback.xml new file mode 100644 index 0000000000..b2160dd819 --- /dev/null +++ b/spring-data-envers/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + \ No newline at end of file diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml new file mode 100644 index 0000000000..bba8571672 --- /dev/null +++ b/spring-data-jpa-distribution/pom.xml @@ -0,0 +1,39 @@ + + + + 4.0.0 + + spring-data-jpa-distribution + + pom + + Spring Data JPA - Distribution + Distribution build for Spring Data JPA + + + org.springframework.data + spring-data-jpa-parent + 3.0.0-SNAPSHOT + ../pom.xml + + + + ${basedir}/.. + DATAJPA + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + org.asciidoctor + asciidoctor-maven-plugin + + + + + diff --git a/Spring Data JPA.sonargraph b/spring-data-jpa/Spring Data JPA.sonargraph similarity index 100% rename from Spring Data JPA.sonargraph rename to spring-data-jpa/Spring Data JPA.sonargraph diff --git a/aop.xml b/spring-data-jpa/aop.xml similarity index 100% rename from aop.xml rename to spring-data-jpa/aop.xml diff --git a/spring-data-jpa/formatting.xml b/spring-data-jpa/formatting.xml new file mode 100644 index 0000000000..c74468778e --- /dev/null +++ b/spring-data-jpa/formatting.xml @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml new file mode 100644 index 0000000000..5960d72cca --- /dev/null +++ b/spring-data-jpa/pom.xml @@ -0,0 +1,402 @@ + + + + 4.0.0 + + org.springframework.data + spring-data-jpa + 3.0.0-SNAPSHOT + + Spring Data JPA + Spring Data module for JPA repositories. + https://projects.spring.io/spring-data-jpa + + + org.springframework.data + spring-data-jpa-parent + 3.0.0-SNAPSHOT + ../pom.xml + + + + spring.data.jpa + + + + + + ${project.groupId} + spring-data-commons + ${springdata.commons} + + + + org.springframework + spring-orm + + + + org.springframework + spring-context + + + + org.springframework + spring-aop + + + + org.springframework + spring-tx + + + + org.springframework + spring-beans + + + + org.springframework + spring-instrument + provided + + + + org.springframework + spring-core + + + commons-logging + commons-logging + + + + + + org.aspectj + aspectjweaver + ${aspectj} + test + + + + org.springframework + spring-aspects + compile + true + + + + org.hsqldb + hsqldb + 2.5.1 + test + + + + + mysql + mysql-connector-java + ${mysql-connector-java} + test + + + + org.testcontainers + mysql + test + + + org.slf4j + jcl-over-slf4j + + + + + + + org.postgresql + postgresql + ${postgresql} + test + + + + org.testcontainers + postgresql + test + + + + io.vavr + vavr + ${vavr} + test + + + + + + org.eclipse.persistence + org.eclipse.persistence.jpa + ${eclipselink} + true + + + + ${hibernate.groupId} + hibernate-core-jakarta + ${hibernate} + true + + + + ${hibernate.groupId} + hibernate-jpamodelgen-jakarta + ${hibernate} + provided + + + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation-api} + + + + + com.querydsl + querydsl-apt + ${querydsl} + jakarta + provided + + + + com.querydsl + querydsl-jpa + jakarta + ${querydsl} + true + + + + + org.jmolecules.integrations + jmolecules-spring + ${jmolecules-integration} + test + + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + ${cdi} + provided + true + + + + + org.apache.openwebbeans + openwebbeans-se + jakarta + test + + + org.apache.openwebbeans + openwebbeans-impl + jakarta + test + + + org.apache.openwebbeans + openwebbeans-spi + jakarta + test + + + + + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco} + + ${jacoco.destfile} + + + + jacoco-initialize + + prepare-agent + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.springframework + spring-instrument + ${spring} + runtime + + + + + + default-test + + + **/* + + + + + unit-test + + test + + test + + + **/*UnitTests.java + + + + + integration-test + + test + + test + + + **/*IntegrationTests.java + **/*Tests.java + + + **/*UnitTests.java + **/OpenJpa* + **/EclipseLink* + **/MySql* + **/Postgres* + + + -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar + -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} + + + + + eclipselink-test + + test + + test + + + **/EclipseLink*Tests.java + + + -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} + -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar + -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar + + + + + + + + maven-compiler-plugin + + + java-test-compile + test-compile + + testCompile + + + false + + + + + + + io.starter + aspectj-maven-plugin + 1.12.9 + + + org.aspectj + aspectjrt + ${aspectj} + + + org.aspectj + aspectjtools + ${aspectj} + + + + + + compile + + process-classes + + + + true + + true + + + org.springframework + spring-aspects + + + + **/domain/support/AuditingEntityListener.java + + ${source.level} + ${source.level} + ${source.level} + aop.xml + + + + + + + diff --git a/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java diff --git a/src/main/java/org/springframework/data/jpa/convert/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/convert/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java diff --git a/src/main/java/org/springframework/data/jpa/convert/threeten/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/convert/threeten/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java diff --git a/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java diff --git a/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/JpaSort.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java diff --git a/src/main/java/org/springframework/data/jpa/domain/Specification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/Specification.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java diff --git a/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java diff --git a/src/main/java/org/springframework/data/jpa/domain/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java diff --git a/src/main/java/org/springframework/data/jpa/domain/support/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/domain/support/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java diff --git a/src/main/java/org/springframework/data/jpa/mapping/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/mapping/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java diff --git a/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java diff --git a/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java diff --git a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java diff --git a/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java diff --git a/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java diff --git a/src/main/java/org/springframework/data/jpa/provider/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/provider/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/EntityGraph.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/JpaContext.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/JpaRepository.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java diff --git a/src/main/java/org/springframework/data/jpa/repository/Lock.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/Lock.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java diff --git a/src/main/java/org/springframework/data/jpa/repository/Modifying.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/Modifying.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java diff --git a/src/main/java/org/springframework/data/jpa/repository/Query.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/Query.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java diff --git a/src/main/java/org/springframework/data/jpa/repository/QueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/QueryHints.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java diff --git a/src/main/java/org/springframework/data/jpa/repository/Temporal.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/Temporal.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/cdi/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java diff --git a/src/main/java/org/springframework/data/jpa/repository/config/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/config/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/repository/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/Procedure.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java diff --git a/src/main/java/org/springframework/data/jpa/repository/query/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/query/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java diff --git a/src/main/java/org/springframework/data/jpa/repository/support/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/repository/support/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java diff --git a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java diff --git a/src/main/java/org/springframework/data/jpa/support/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/support/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/support/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java diff --git a/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java diff --git a/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java diff --git a/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java diff --git a/src/main/java/org/springframework/data/jpa/util/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/package-info.java similarity index 100% rename from src/main/java/org/springframework/data/jpa/util/package-info.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/util/package-info.java diff --git a/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/spring-data-jpa/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension similarity index 100% rename from src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension rename to spring-data-jpa/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension diff --git a/src/main/resources/META-INF/spring.factories b/spring-data-jpa/src/main/resources/META-INF/spring.factories similarity index 100% rename from src/main/resources/META-INF/spring.factories rename to spring-data-jpa/src/main/resources/META-INF/spring.factories diff --git a/src/main/resources/META-INF/spring.handlers b/spring-data-jpa/src/main/resources/META-INF/spring.handlers similarity index 100% rename from src/main/resources/META-INF/spring.handlers rename to spring-data-jpa/src/main/resources/META-INF/spring.handlers diff --git a/src/main/resources/META-INF/spring.schemas b/spring-data-jpa/src/main/resources/META-INF/spring.schemas similarity index 100% rename from src/main/resources/META-INF/spring.schemas rename to spring-data-jpa/src/main/resources/META-INF/spring.schemas diff --git a/src/main/resources/META-INF/spring.tooling b/spring-data-jpa/src/main/resources/META-INF/spring.tooling similarity index 100% rename from src/main/resources/META-INF/spring.tooling rename to spring-data-jpa/src/main/resources/META-INF/spring.tooling diff --git a/src/main/resources/license.txt b/spring-data-jpa/src/main/resources/license.txt similarity index 100% rename from src/main/resources/license.txt rename to spring-data-jpa/src/main/resources/license.txt diff --git a/src/main/resources/notice.txt b/spring-data-jpa/src/main/resources/notice.txt similarity index 100% rename from src/main/resources/notice.txt rename to spring-data-jpa/src/main/resources/notice.txt diff --git a/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.0.xsd b/spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.0.xsd similarity index 100% rename from src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.0.xsd rename to spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.0.xsd diff --git a/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.1.xsd b/spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.1.xsd similarity index 100% rename from src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.1.xsd rename to spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.1.xsd diff --git a/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.11.xsd b/spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.11.xsd similarity index 100% rename from src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.11.xsd rename to spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.11.xsd diff --git a/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.2.xsd b/spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.2.xsd similarity index 100% rename from src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.2.xsd rename to spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.2.xsd diff --git a/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.3.xsd b/spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.3.xsd similarity index 100% rename from src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.3.xsd rename to spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.3.xsd diff --git a/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.8.xsd b/spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.8.xsd similarity index 100% rename from src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.8.xsd rename to spring-data-jpa/src/main/resources/org/springframework/data/jpa/repository/config/spring-jpa-1.8.xsd diff --git a/src/main/resources/readme.txt b/spring-data-jpa/src/main/resources/readme.txt similarity index 100% rename from src/main/resources/readme.txt rename to spring-data-jpa/src/main/resources/readme.txt diff --git a/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java diff --git a/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java diff --git a/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Account.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Account.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Address.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Address.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Category.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Category.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Category.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Category.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Child.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Child.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Customer.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Item.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Item.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Order.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Order.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Parent.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Product.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Product.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Product.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Product.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Role.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Role.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Site.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/Site.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SpecialUser.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/User.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java diff --git a/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java diff --git a/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/Person.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/config/package.html b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/package.html similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/config/package.html rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/package.html diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/package-info.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/package-info.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/custom/package-info.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/package-info.java diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java diff --git a/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 similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/repository/support/package.html b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/package.html similarity index 100% rename from src/test/java/org/springframework/data/jpa/repository/support/package.html rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/package.html diff --git a/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java diff --git a/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java diff --git a/src/test/java/org/springframework/data/jpa/util/FixedDate.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/util/FixedDate.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java diff --git a/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java diff --git a/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java similarity index 100% rename from src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java diff --git a/src/test/resources/META-INF/jpa-named-queries.properties b/spring-data-jpa/src/test/resources/META-INF/jpa-named-queries.properties similarity index 100% rename from src/test/resources/META-INF/jpa-named-queries.properties rename to spring-data-jpa/src/test/resources/META-INF/jpa-named-queries.properties diff --git a/src/test/resources/META-INF/orm.xml b/spring-data-jpa/src/test/resources/META-INF/orm.xml similarity index 100% rename from src/test/resources/META-INF/orm.xml rename to spring-data-jpa/src/test/resources/META-INF/orm.xml diff --git a/src/test/resources/META-INF/persistence.xml b/spring-data-jpa/src/test/resources/META-INF/persistence.xml similarity index 100% rename from src/test/resources/META-INF/persistence.xml rename to spring-data-jpa/src/test/resources/META-INF/persistence.xml diff --git a/src/test/resources/META-INF/persistence2.xml b/spring-data-jpa/src/test/resources/META-INF/persistence2.xml similarity index 100% rename from src/test/resources/META-INF/persistence2.xml rename to spring-data-jpa/src/test/resources/META-INF/persistence2.xml diff --git a/src/test/resources/application-context.xml b/spring-data-jpa/src/test/resources/application-context.xml similarity index 100% rename from src/test/resources/application-context.xml rename to spring-data-jpa/src/test/resources/application-context.xml diff --git a/src/test/resources/auditing/auditing-bfpp-context.xml b/spring-data-jpa/src/test/resources/auditing/auditing-bfpp-context.xml similarity index 100% rename from src/test/resources/auditing/auditing-bfpp-context.xml rename to spring-data-jpa/src/test/resources/auditing/auditing-bfpp-context.xml diff --git a/src/test/resources/auditing/auditing-entity-listener.xml b/spring-data-jpa/src/test/resources/auditing/auditing-entity-listener.xml similarity index 100% rename from src/test/resources/auditing/auditing-entity-listener.xml rename to spring-data-jpa/src/test/resources/auditing/auditing-entity-listener.xml diff --git a/src/test/resources/auditing/auditing-namespace-context.xml b/spring-data-jpa/src/test/resources/auditing/auditing-namespace-context.xml similarity index 100% rename from src/test/resources/auditing/auditing-namespace-context.xml rename to spring-data-jpa/src/test/resources/auditing/auditing-namespace-context.xml diff --git a/src/test/resources/auditing/auditing-namespace-context2.xml b/spring-data-jpa/src/test/resources/auditing/auditing-namespace-context2.xml similarity index 100% rename from src/test/resources/auditing/auditing-namespace-context2.xml rename to spring-data-jpa/src/test/resources/auditing/auditing-namespace-context2.xml diff --git a/src/test/resources/auditing/auditing-namespace-context3.xml b/spring-data-jpa/src/test/resources/auditing/auditing-namespace-context3.xml similarity index 100% rename from src/test/resources/auditing/auditing-namespace-context3.xml rename to spring-data-jpa/src/test/resources/auditing/auditing-namespace-context3.xml diff --git a/src/test/resources/config/jpa-context-with-jndi.xml b/spring-data-jpa/src/test/resources/config/jpa-context-with-jndi.xml similarity index 100% rename from src/test/resources/config/jpa-context-with-jndi.xml rename to spring-data-jpa/src/test/resources/config/jpa-context-with-jndi.xml diff --git a/src/test/resources/config/lookup-strategies-context.xml b/spring-data-jpa/src/test/resources/config/lookup-strategies-context.xml similarity index 100% rename from src/test/resources/config/lookup-strategies-context.xml rename to spring-data-jpa/src/test/resources/config/lookup-strategies-context.xml diff --git a/src/test/resources/config/namespace-application-context.xml b/spring-data-jpa/src/test/resources/config/namespace-application-context.xml similarity index 100% rename from src/test/resources/config/namespace-application-context.xml rename to spring-data-jpa/src/test/resources/config/namespace-application-context.xml diff --git a/src/test/resources/config/namespace-autoconfig-context.xml b/spring-data-jpa/src/test/resources/config/namespace-autoconfig-context.xml similarity index 100% rename from src/test/resources/config/namespace-autoconfig-context.xml rename to spring-data-jpa/src/test/resources/config/namespace-autoconfig-context.xml diff --git a/src/test/resources/config/namespace-autoconfig-typefilter-context.xml b/spring-data-jpa/src/test/resources/config/namespace-autoconfig-typefilter-context.xml similarity index 100% rename from src/test/resources/config/namespace-autoconfig-typefilter-context.xml rename to spring-data-jpa/src/test/resources/config/namespace-autoconfig-typefilter-context.xml diff --git a/src/test/resources/config/namespace-customfactory-context.xml b/spring-data-jpa/src/test/resources/config/namespace-customfactory-context.xml similarity index 100% rename from src/test/resources/config/namespace-customfactory-context.xml rename to spring-data-jpa/src/test/resources/config/namespace-customfactory-context.xml diff --git a/src/test/resources/config/namespace-nested-repositories-application-context.xml b/spring-data-jpa/src/test/resources/config/namespace-nested-repositories-application-context.xml similarity index 100% rename from src/test/resources/config/namespace-nested-repositories-application-context.xml rename to spring-data-jpa/src/test/resources/config/namespace-nested-repositories-application-context.xml diff --git a/src/test/resources/eclipselink.xml b/spring-data-jpa/src/test/resources/eclipselink.xml similarity index 100% rename from src/test/resources/eclipselink.xml rename to spring-data-jpa/src/test/resources/eclipselink.xml diff --git a/src/test/resources/hibernate.xml b/spring-data-jpa/src/test/resources/hibernate.xml similarity index 100% rename from src/test/resources/hibernate.xml rename to spring-data-jpa/src/test/resources/hibernate.xml diff --git a/src/test/resources/infrastructure.xml b/spring-data-jpa/src/test/resources/infrastructure.xml similarity index 100% rename from src/test/resources/infrastructure.xml rename to spring-data-jpa/src/test/resources/infrastructure.xml diff --git a/src/test/resources/logback.xml b/spring-data-jpa/src/test/resources/logback.xml similarity index 100% rename from src/test/resources/logback.xml rename to spring-data-jpa/src/test/resources/logback.xml diff --git a/src/test/resources/multiple-entity-manager-context.xml b/spring-data-jpa/src/test/resources/multiple-entity-manager-context.xml similarity index 100% rename from src/test/resources/multiple-entity-manager-context.xml rename to spring-data-jpa/src/test/resources/multiple-entity-manager-context.xml diff --git a/src/test/resources/multiple-entity-manager-integration-context.xml b/spring-data-jpa/src/test/resources/multiple-entity-manager-integration-context.xml similarity index 100% rename from src/test/resources/multiple-entity-manager-integration-context.xml rename to spring-data-jpa/src/test/resources/multiple-entity-manager-integration-context.xml diff --git a/src/test/resources/openjpa.xml b/spring-data-jpa/src/test/resources/openjpa.xml similarity index 100% rename from src/test/resources/openjpa.xml rename to spring-data-jpa/src/test/resources/openjpa.xml diff --git a/src/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions.xml b/spring-data-jpa/src/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions.xml similarity index 100% rename from src/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions.xml rename to spring-data-jpa/src/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions.xml diff --git a/src/test/resources/org/springframework/data/jpa/support/mapping.xml b/spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/mapping.xml similarity index 100% rename from src/test/resources/org/springframework/data/jpa/support/mapping.xml rename to spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/mapping.xml diff --git a/src/test/resources/org/springframework/data/jpa/support/module1/module1-orm.xml b/spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/module1/module1-orm.xml similarity index 100% rename from src/test/resources/org/springframework/data/jpa/support/module1/module1-orm.xml rename to spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/module1/module1-orm.xml diff --git a/src/test/resources/org/springframework/data/jpa/support/module2/module2-orm.xml b/spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/module2/module2-orm.xml similarity index 100% rename from src/test/resources/org/springframework/data/jpa/support/module2/module2-orm.xml rename to spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/module2/module2-orm.xml diff --git a/src/test/resources/org/springframework/data/jpa/support/persistence.xml b/spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/persistence.xml similarity index 100% rename from src/test/resources/org/springframework/data/jpa/support/persistence.xml rename to spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/persistence.xml diff --git a/src/test/resources/org/springframework/data/jpa/support/persistence2.xml b/spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/persistence2.xml similarity index 100% rename from src/test/resources/org/springframework/data/jpa/support/persistence2.xml rename to spring-data-jpa/src/test/resources/org/springframework/data/jpa/support/persistence2.xml diff --git a/src/test/resources/scripts/hsqldb-init.sql b/spring-data-jpa/src/test/resources/scripts/hsqldb-init.sql similarity index 100% rename from src/test/resources/scripts/hsqldb-init.sql rename to spring-data-jpa/src/test/resources/scripts/hsqldb-init.sql diff --git a/src/test/resources/scripts/mysql-stored-procedures.sql b/spring-data-jpa/src/test/resources/scripts/mysql-stored-procedures.sql similarity index 100% rename from src/test/resources/scripts/mysql-stored-procedures.sql rename to spring-data-jpa/src/test/resources/scripts/mysql-stored-procedures.sql diff --git a/src/test/resources/scripts/postgres-stored-procedures.sql b/spring-data-jpa/src/test/resources/scripts/postgres-stored-procedures.sql similarity index 100% rename from src/test/resources/scripts/postgres-stored-procedures.sql rename to spring-data-jpa/src/test/resources/scripts/postgres-stored-procedures.sql diff --git a/src/test/resources/scripts/schema-stored-procedures.sql b/spring-data-jpa/src/test/resources/scripts/schema-stored-procedures.sql similarity index 100% rename from src/test/resources/scripts/schema-stored-procedures.sql rename to spring-data-jpa/src/test/resources/scripts/schema-stored-procedures.sql diff --git a/src/test/resources/simple-persistence/simple-persistence.xml b/spring-data-jpa/src/test/resources/simple-persistence/simple-persistence.xml similarity index 100% rename from src/test/resources/simple-persistence/simple-persistence.xml rename to spring-data-jpa/src/test/resources/simple-persistence/simple-persistence.xml diff --git a/src/test/resources/tx-manager.xml b/spring-data-jpa/src/test/resources/tx-manager.xml similarity index 100% rename from src/test/resources/tx-manager.xml rename to spring-data-jpa/src/test/resources/tx-manager.xml diff --git a/src/main/asciidoc/envers.adoc b/src/main/asciidoc/envers.adoc new file mode 100644 index 0000000000..d1c46be1dd --- /dev/null +++ b/src/main/asciidoc/envers.adoc @@ -0,0 +1,204 @@ +[[envers]] += Spring Data Envers + +[[envers.what.is.spring.data]] +== What is Spring Data Envers? + +Spring Data Envers makes typical Envers queries available in repositories for Spring Data JPA. +It differs from other Spring Data modules in that it is always used in combination with another Spring Data Module: Spring Data JPA. + +[[envers.what]] +== What is Envers? + +Envers is a https://hibernate.org/orm/envers/[Hibernate module] that adds auditing capabilities to JPA entities. +This documentation assumes you are familiar with Envers, just as Spring Data Envers relies on Envers being properly configured. + +[[envers.configuration]] +== Configuration + +As a starting point for using Spring Data Envers, you need a project with Spring Data JPA on the classpath and an additional `spring-data-envers` dependency: + +==== +[source,xml,subs="+attributes"] +---- + + + + + + org.springframework.data + spring-data-envers + {version} + + + +---- +==== + +This also brings `hibernate-envers` into the project as a transient dependency. + +To enable Spring Data Envers and Spring Data JPA, we need to configure two beans and a special `repositoryFactoryBeanClass`: + +==== +[source,java] +---- +@Configuration +@EnableEnversRepositories +@EnableTransactionManagement +public class EnversDemoConfiguration { + + @Bean + public DataSource dataSource() { + + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); + return builder.setType(EmbeddedDatabaseType.HSQL).build(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + vendorAdapter.setGenerateDdl(true); + + LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); + factory.setJpaVendorAdapter(vendorAdapter); + factory.setPackagesToScan("example.springdata.jpa.envers"); + factory.setDataSource(dataSource()); + return factory; + } + + @Bean + public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + + JpaTransactionManager txManager = new JpaTransactionManager(); + txManager.setEntityManagerFactory(entityManagerFactory); + return txManager; + } +} +---- +==== + +To actually use Spring Data Envers, make one or more repositories into a {spring-data-commons-javadoc-base}/org/springframework/data/repository/history/RevisionRepository.html[`RevisionRepository`] by adding it as an extended interface: + +==== +[source,java] +---- +interface PersonRepository + extends CrudRepository, + RevisionRepository // <1> +{} +---- +<1> The first type parameter (`Person`) denotes the entity type, the second (`Long`) denotes the type of the id property, and the last one (`Long`) is the type of the revision number. +For Envers in default configuration, the revision number parameter should be `Integer` or `Long`. +==== + +The entity for that repository must be an entity with Envers auditing enabled (that is, it must have an `@Audited` annotation): + +==== +[source,java] +---- +@Entity +@Audited +class Person { + + @Id @GeneratedValue + Long id; + String name; + @Version Long version; +} +---- +==== + +[[envers.usage]] +== Usage + +You can now use the methods from `RevisionRepository` to query the revisions of the entity, as the following test case shows: + +==== +[source,java] +---- +@ExtendWith(SpringExtension.class) +@Import(EnversDemoConfiguration.class) // <1> +class EnversIntegrationTests { + + final PersonRepository repository; + final TransactionTemplate tx; + + EnversIntegrationTests(@Autowired PersonRepository repository, @Autowired PlatformTransactionManager tm) { + this.repository = repository; + this.tx = new TransactionTemplate(tm); + } + + @Test + void testRepository() { + + Person updated = preparePersonHistory(); + + Revisions revisions = repository.findRevisions(updated.id); + + Iterator> revisionIterator = revisions.iterator(); + + checkNextRevision(revisionIterator, "John", RevisionType.INSERT); + checkNextRevision(revisionIterator, "Jonny", RevisionType.UPDATE); + checkNextRevision(revisionIterator, null, RevisionType.DELETE); + assertThat(revisionIterator.hasNext()).isFalse(); + + } + + /** + * Checks that the next element in the iterator is a Revision entry referencing a Person + * with the given name after whatever change brought that Revision into existence. + *

+ * As a side effect the Iterator gets advanced by one element. + * + * @param revisionIterator the iterator to be tested. + * @param name the expected name of the Person referenced by the Revision. + * @param revisionType the type of the revision denoting if it represents an insert, update or delete. + */ + private void checkNextRevision(Iterator> revisionIterator, String name, + RevisionType revisionType) { + + assertThat(revisionIterator.hasNext()).isTrue(); + Revision revision = revisionIterator.next(); + assertThat(revision.getEntity().name).isEqualTo(name); + assertThat(revision.getMetadata().getRevisionType()).isEqualTo(revisionType); + } + + /** + * Creates a Person with a couple of changes so it has a non-trivial revision history. + * @return the created Person. + */ + private Person preparePersonHistory() { + + Person john = new Person(); + john.setName("John"); + + // create + Person saved = tx.execute(__ -> repository.save(john)); + assertThat(saved).isNotNull(); + + saved.setName("Jonny"); + + // update + Person updated = tx.execute(__ -> repository.save(saved)); + assertThat(updated).isNotNull(); + + // delete + tx.executeWithoutResult(__ -> repository.delete(updated)); + return updated; + } +} +---- +<1> This references the application context configuration presented earlier (in the <> section). +==== + +[[envers.resources]] +== Further Resources + +You can download the https://github.com/spring-projects/spring-data-examples[Spring Data Envers example in the Spring Data Examples repository] and play around with to get a feel for how the library works. + +You should also check out the {spring-data-commons-javadoc-base}/org/springframework/data/repository/history/RevisionRepository.html[Javadoc for `RevisionRepository`] and related classes. + +You can ask questions at https://stackoverflow.com/questions/tagged/spring-data-envers[Stackoverflow by using the `spring-data-envers` tag]. + +The https://github.com/spring-projects/spring-data-envers[source code and issue tracker for Spring Data Envers is hosted at GitHub]. diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index 1a45e2757f..14cc7771fa 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -23,6 +23,8 @@ include::{spring-data-commons-docs}/repositories.adoc[leveloffset=+1] include::jpa.adoc[leveloffset=+2] +include::envers.adoc[leveloffset=+2] + [[appendix]] == Appendix diff --git a/template.mf b/template.mf deleted file mode 100644 index c3e75cc826..0000000000 --- a/template.mf +++ /dev/null @@ -1,28 +0,0 @@ -Bundle-ManifestVersion: 2 -Bundle-SymbolicName: org.springframework.data.jpa -Bundle-Name: ${project.name} -Bundle-Vendor: Pivotal Software, Inc -Bundle-Version: ${project.version} -Bundle-RequiredExecutionEnvironment: J2SE-1.5 -Export-Template: - org.springframework.data.jpa.*;version="${project.version}" -Import-Template: - com.querydsl.*;version="${querydsl:[=.=.=,+1.0.0)}";resolution:=optional, - jakarta.persistence.*;version="${jpa:[=.=.=,+1.0.0)}", - jakarta.annotation.*;version="0.0.0", - jakarta.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional, - org.aopalliance.*;version="[1.0.0,2.0.0)", - org.apache.openjpa.*;version="${openjpa:[=.=.=,+1.0.0)}";resolution:=optional, - org.aspectj.*;version="${aspectj:[=.=.=,+1.0.0)}";resolution:=optional, - org.eclipse.persistence.*;version="${eclipselink:[=.=.=,+1.0.0)}";resolution:=optional, - org.hibernate.*;version="[3.6.10,4.4.0)";resolution:=optional, - org.slf4j.*;version="${slf4j:[=.=.=,+1.0.0)}", - org.springframework.*;version="${spring:[=.=.=.=,+1.1.0)}", - org.springframework.beans.factory.aspectj;version="${spring:[=.=.=.=,+1.1.0)}";resolution:=optional, - org.springframework.data.*;version="${springdata.commons:[=.=.=.=,+1.0.0)}", - org.threeten.bp.*;version="${threetenbp:[=.=.=,+1.0.0)}";resolution:=optional, - org.w3c.*;version="0.0.0" -Import-Package: org.aspectj.lang;version="${aspectj:[=.=.=,+1.0.0)}";resolution:=optional, - org.aspectj.runtime.reflect;version="${aspectj:[=.=.=,+1.0.0)}";resolution:=optional, - org.springframework.beans.factory.aspectj;version="${spring:[=.=.=.=,+1.1.0)}";resolution:=optional - From ea51ee8c746f37b70f5b7d87f3152ebcd4ab3b90 Mon Sep 17 00:00:00 2001 From: Ayoub Rossi Date: Tue, 5 Oct 2021 01:54:16 +0200 Subject: [PATCH 121/821] Change Java Persistence API to Jakarta Persistence API in documentation. Original pull request #2325 --- src/main/asciidoc/glossary.adoc | 2 +- src/main/asciidoc/preface.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/asciidoc/glossary.adoc b/src/main/asciidoc/glossary.adoc index 18a0027176..a0d9ea6c7e 100644 --- a/src/main/asciidoc/glossary.adoc +++ b/src/main/asciidoc/glossary.adoc @@ -16,6 +16,6 @@ EclipseLink :: Object relational mapper implementing JPA - link:$$https://www.ec Hibernate :: Object relational mapper implementing JPA - link:$$https://hibernate.org/$$[https://hibernate.org/] -JPA :: Java Persistence API +JPA :: Jakarta Persistence API Spring :: Java application framework - link:$$https://projects.spring.io/spring-framework$$[https://projects.spring.io/spring-framework] diff --git a/src/main/asciidoc/preface.adoc b/src/main/asciidoc/preface.adoc index 37a01d6e62..22a0e3e852 100644 --- a/src/main/asciidoc/preface.adoc +++ b/src/main/asciidoc/preface.adoc @@ -1,7 +1,7 @@ [[preface]] == Preface -Spring Data JPA provides repository support for the Java Persistence API (JPA). It eases development of applications that need to access JPA data sources. +Spring Data JPA provides repository support for the Jakarta Persistence API (JPA). It eases development of applications that need to access JPA data sources. [[project]] === Project Metadata From 033b4c7db43c5da7bfd5e73c982216ba7a6eb088 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 16 Nov 2021 14:18:33 +0100 Subject: [PATCH 122/821] Cleanup after rebase on 2.6 release. See #2316, #2305, #2294 and #2269 --- .../data/jpa/repository/support/EntityGraphFactory.java | 6 +++--- .../repository/support/FetchableFluentQueryByPredicate.java | 2 +- .../jpa/repository/support/EntityGraphFactoryUnitTests.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename {src => spring-data-jpa/src}/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java (93%) rename {src => spring-data-jpa/src}/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java (95%) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java similarity index 93% rename from src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java index a619916bf9..68edc9d058 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java @@ -17,9 +17,9 @@ import java.util.Set; -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.Subgraph; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Subgraph; import org.springframework.data.mapping.PropertyPath; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index f71eeacc0b..7f8522db0c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -23,7 +23,7 @@ import java.util.function.Function; import java.util.stream.Stream; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; diff --git a/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java similarity index 95% rename from src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java index e0ac1ce52c..448bd0adbd 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java @@ -20,9 +20,9 @@ import java.util.HashSet; -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.Subgraph; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Subgraph; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From 5bc995c744d7863e4bb99a8b75c1828401a1f204 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 10 Dec 2021 18:42:55 +0100 Subject: [PATCH 123/821] Adapt to changes in Spring Framework. Test based on JNDI injection removed, along with the context configuration xml used by the test. The classes used for mocking the JNDI context are no longer part of Spring Framework. RequiredAnnotationBeanPostProcessor removed from configuration. It's no longer provided by Spring Framework. Closes #2385 --- .../DefaultJpaContextIntegrationTests.java | 42 ++----------------- .../test/resources/application-context.xml | 3 -- .../config/jpa-context-with-jndi.xml | 23 ---------- 3 files changed, 3 insertions(+), 65 deletions(-) delete mode 100644 spring-data-jpa/src/test/resources/config/jpa-context-with-jndi.xml diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java index fe8215e224..31daa73660 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java @@ -16,20 +16,16 @@ package org.springframework.data.jpa.repository.support; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; -import java.util.Arrays; -import java.util.HashSet; - -import javax.naming.NamingException; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import javax.sql.DataSource; + +import java.util.Arrays; +import java.util.HashSet; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -37,7 +33,6 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.FilterType; -import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.jpa.domain.sample.Category; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.infrastructure.HibernateTestUtils; @@ -45,9 +40,6 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; -import org.springframework.jndi.JndiObjectFactoryBean; -import org.springframework.mock.jndi.ExpectedLookupTemplate; -import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.stereotype.Component; @@ -132,21 +124,6 @@ void bootstrapsDefaultJpaContextInSpringContainer() { context.close(); } - @Test // DATAJPA-813 - void bootstrapsDefaultJpaContextInSpringContainerWithEntityManagerFromJndi() throws Exception { - - SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder(); - builder.bind("some/EMF", createEntityManagerFactory("spring-data-jpa")); - builder.bind("some/other/Component", new Object()); - - ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("config/jpa-context-with-jndi.xml"); - ApplicationComponent component = context.getBean(ApplicationComponent.class); - - assertThat(component.context).isNotNull(); - - context.close(); - } - @EnableJpaRepositories @ComponentScan(includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = ApplicationComponent.class), useDefaultFilters = false) @@ -157,19 +134,6 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { return createEntityManagerFactoryBean("spring-data-jpa"); } - // A non-EntityManagerFactory JNDI object to make sure the detection doesn't include it - // see DATAJPA-956 - @Bean - public JndiObjectFactoryBean jndiObject() throws NamingException { - - JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); - - bean.setJndiName("some/DataSource"); - bean.setJndiTemplate(new ExpectedLookupTemplate("some/DataSource", mock(DataSource.class))); - bean.setExpectedType(DataSource.class); - - return bean; - } } @Component diff --git a/spring-data-jpa/src/test/resources/application-context.xml b/spring-data-jpa/src/test/resources/application-context.xml index 377b60a53c..3fcd0a6f67 100644 --- a/spring-data-jpa/src/test/resources/application-context.xml +++ b/spring-data-jpa/src/test/resources/application-context.xml @@ -39,9 +39,6 @@ - - - diff --git a/spring-data-jpa/src/test/resources/config/jpa-context-with-jndi.xml b/spring-data-jpa/src/test/resources/config/jpa-context-with-jndi.xml deleted file mode 100644 index 3d41c9f94c..0000000000 --- a/spring-data-jpa/src/test/resources/config/jpa-context-with-jndi.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - From d803ff7ede35ac877db7fab5363906db76e7cb5f Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 5 Jan 2022 14:06:26 +0100 Subject: [PATCH 124/821] Remove Eclipse Non-Javadoc comments. Closes #2397 --- .../DefaultRevisionEntityInformation.java | 12 -- .../support/DefaultRevisionMetadata.java | 28 ---- .../EnversRevisionRepositoryFactoryBean.java | 4 - .../support/EnversRevisionRepositoryImpl.java | 8 - .../ReflectionRevisionEntityInformation.java | 4 - .../data/jpa/domain/AbstractAuditable.java | 32 ---- .../data/jpa/domain/AbstractPersistable.java | 16 -- .../data/jpa/domain/JpaSort.java | 20 --- .../AuditingBeanFactoryPostProcessor.java | 4 - .../mapping/JpaMetamodelMappingContext.java | 16 -- .../jpa/mapping/JpaPersistentEntityImpl.java | 16 -- .../mapping/JpaPersistentPropertyImpl.java | 48 ------ .../CollectionAwareProjectionFactory.java | 8 - .../jpa/provider/PersistenceProvider.java | 72 --------- .../data/jpa/repository/JpaRepository.java | 20 --- .../jpa/repository/cdi/JpaRepositoryBean.java | 4 - .../config/AuditingBeanDefinitionParser.java | 4 - .../config/JpaAuditingRegistrar.java | 20 --- ...JpaMetamodelMappingContextFactoryBean.java | 12 -- .../config/JpaRepositoriesRegistrar.java | 8 - .../config/JpaRepositoryConfigExtension.java | 40 ----- .../config/JpaRepositoryNameSpaceHandler.java | 5 - .../repository/query/AbstractJpaQuery.java | 12 -- .../query/AbstractStringBasedJpaQuery.java | 12 -- .../query/DefaultJpaEntityMetadata.java | 8 - .../repository/query/EmptyDeclaredQuery.java | 32 ---- .../jpa/repository/query/EscapeCharacter.java | 12 -- .../query/JpaCountQueryCreator.java | 8 - .../jpa/repository/query/JpaEntityGraph.java | 4 - .../jpa/repository/query/JpaParameters.java | 12 -- .../jpa/repository/query/JpaQueryCreator.java | 12 -- .../repository/query/JpaQueryExecution.java | 16 -- .../query/JpaQueryLookupStrategy.java | 12 -- .../jpa/repository/query/JpaQueryMethod.java | 20 --- .../data/jpa/repository/query/NamedQuery.java | 12 -- .../jpa/repository/query/NativeJpaQuery.java | 4 - .../repository/query/PartTreeJpaQuery.java | 12 -- .../query/QueryParameterSetter.java | 4 - .../query/QueryParameterSetterFactory.java | 24 --- .../query/StoredProcedureJpaQuery.java | 12 -- .../jpa/repository/query/StringQuery.java | 64 -------- .../CrudMethodMetadataPostProcessor.java | 48 ------ .../repository/support/DefaultJpaContext.java | 4 - .../repository/support/DefaultQueryHints.java | 12 -- ...rBeanDefinitionRegistrarPostProcessor.java | 8 - .../FetchableFluentQueryByExample.java | 40 ----- .../FetchableFluentQueryByPredicate.java | 40 ----- .../support/JpaEntityInformationSupport.java | 4 - .../JpaEvaluationContextExtension.java | 8 - .../JpaMetamodelEntityInformation.java | 36 ----- .../JpaPersistableEntityInformation.java | 8 - .../support/JpaRepositoryFactory.java | 32 ---- .../support/JpaRepositoryFactoryBean.java | 12 -- .../repository/support/MutableQueryHints.java | 12 -- .../jpa/repository/support/QueryHints.java | 12 -- .../support/QuerydslJpaPredicateExecutor.java | 36 ----- .../support/QuerydslJpaRepository.java | 32 ---- .../support/SimpleJpaRepository.java | 144 ------------------ ...hScanningPersistenceUnitPostProcessor.java | 12 -- .../MergingPersistenceUnitManager.java | 8 - .../data/jpa/util/BeanDefinitionUtils.java | 8 - .../data/jpa/util/HibernateProxyDetector.java | 4 - .../jpa/util/JpaMetamodelCacheCleanup.java | 4 - .../jpa/domain/sample/AuditorAwareStub.java | 4 - .../sample/EmbeddedIdExampleDepartment.java | 6 - .../sample/EmbeddedIdExampleEmployeePK.java | 6 - .../domain/sample/EntityWithAssignedId.java | 8 - .../sample/IdClassExampleDepartment.java | 6 - .../data/jpa/domain/sample/MailSender.java | 8 - .../domain/sample/PersistableWithIdClass.java | 6 - .../sample/PersistableWithIdClassPK.java | 8 - .../PersistableWithSingleIdClassPK.java | 8 - .../data/jpa/domain/sample/Role.java | 5 - .../jpa/domain/sample/SampleEntityPK.java | 10 -- .../jpa/domain/sample/SampleWithIdClass.java | 8 - .../data/jpa/domain/sample/User.java | 10 -- ...tingBeanFactoryPostProcessorUnitTests.java | 4 - .../support/AuditingNamespaceUnitTests.java | 4 - .../CustomEclipseLinkJpaVendorAdapter.java | 8 - .../CustomHsqlHibernateJpaVendorAdaptor.java | 4 - .../config/TypeFilterConfigTests.java | 7 - .../custom/CustomGenericJpaRepository.java | 7 - .../CustomGenericJpaRepositoryFactory.java | 8 - ...CustomGenericJpaRepositoryFactoryBean.java | 6 - .../jpa/repository/sample/RoleRepository.java | 12 -- .../repository/sample/UserRepositoryImpl.java | 8 - .../JpaRepositoryFactoryBeanUnitTests.java | 4 - .../support/OpenJpaJpaRepositoryTests.java | 4 - 88 files changed, 1386 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java index b01edde01d..db9cab6aa1 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java @@ -25,26 +25,14 @@ */ class DefaultRevisionEntityInformation implements RevisionEntityInformation { - /* - * (non-Javadoc) - * @see org.springframework.data.repository.history.support.RevisionEntityInformation#getRevisionNumberType() - */ public Class getRevisionNumberType() { return Integer.class; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.history.support.RevisionEntityInformation#isDefaultRevisionEntity() - */ public boolean isDefaultRevisionEntity() { return true; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.history.support.RevisionEntityInformation#getRevisionEntityClass() - */ public Class getRevisionEntityClass() { return DefaultRevisionEntity.class; } diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java index 912d10cf59..ee2bbe30b8 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java @@ -50,54 +50,30 @@ public DefaultRevisionMetadata(DefaultRevisionEntity entity, RevisionType revisi this.revisionType = revisionType; } - /* - * (non-Javadoc) - * @see org.springframework.data.history.RevisionMetadata#getRevisionNumber() - */ public Optional getRevisionNumber() { return Optional.of(entity.getId()); } - /* - * (non-Javadoc) - * @see org.springframework.data.history.RevisionMetadata#getRevisionDate() - */ @Deprecated public Optional getRevisionDate() { return getRevisionInstant().map(instant -> LocalDateTime.ofInstant(instant, ZoneOffset.systemDefault())); } - /* - * (non-Javadoc) - * @see org.springframework.data.history.RevisionMetadata#getRevisionInstant() - */ @Override public Optional getRevisionInstant() { return Optional.of(Instant.ofEpochMilli(entity.getTimestamp())); } - /* - * (non-Javadoc) - * @see org.springframework.data.history.RevisionMetadata#getDelegate() - */ @SuppressWarnings("unchecked") public T getDelegate() { return (T) entity; } - /* - * (non-Javadoc) - * @see org.springframework.data.history.RevisionMetadata#getRevisionType() - */ @Override public RevisionType getRevisionType() { return revisionType; } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object o) { @@ -112,10 +88,6 @@ public boolean equals(Object o) { && getRevisionInstant().equals(that.getRevisionInstant()) && revisionType.equals(that.getRevisionType()); } - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { return "DefaultRevisionMetadata{" + "entity=" + entity + ", revisionType=" + revisionType + '}'; diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java index 12819f261c..05bd785d86 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java @@ -58,10 +58,6 @@ public void setRevisionEntityClass(Class revisionEntityClass) { this.revisionEntityClass = revisionEntityClass; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean#createRepositoryFactory(jakarta.persistence.EntityManager) - */ @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new RevisionRepositoryFactory(entityManager, revisionEntityClass); diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index dae06837f9..a9b61b37eb 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -83,10 +83,6 @@ public EnversRevisionRepositoryImpl(JpaEntityInformation entityInformation this.entityManager = entityManager; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.history.RevisionRepository#findLastChangeRevision(java.io.Serializable) - */ @SuppressWarnings("unchecked") public Optional> findLastChangeRevision(ID id) { @@ -104,10 +100,6 @@ public Optional> findLastChangeRevision(ID id) { return Optional.of(createRevision(new QueryResult<>(singleResult.get(0)))); } - /* - * (non-Javadoc) - * @see org.springframework.data.envers.repository.support.EnversRevisionRepository#findRevision(java.io.Serializable, java.lang.Number) - */ @Override @SuppressWarnings("unchecked") public Optional> findRevision(ID id, N revisionNumber) { diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java index 6edd4e9ad9..95ca40e5ff 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java @@ -50,10 +50,6 @@ public ReflectionRevisionEntityInformation(Class revisionEntityClass) { } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.history.support.RevisionEntityInformation#isDefaultRevisionEntity() - */ public boolean isDefaultRevisionEntity() { return false; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java index a4fa689179..45c6d7edad 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java @@ -56,75 +56,43 @@ public abstract class AbstractAuditable extends Abst @Temporal(TemporalType.TIMESTAMP) // private @Nullable Date lastModifiedDate; - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Auditable#getCreatedBy() - */ @Override public Optional getCreatedBy() { return Optional.ofNullable(createdBy); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Auditable#setCreatedBy(java.lang.Object) - */ @Override public void setCreatedBy(U createdBy) { this.createdBy = createdBy; } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Auditable#getCreatedDate() - */ @Override public Optional getCreatedDate() { return null == createdDate ? Optional.empty() : Optional.of(LocalDateTime.ofInstant(createdDate.toInstant(), ZoneId.systemDefault())); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Auditable#setCreatedDate(java.time.temporal.TemporalAccessor) - */ @Override public void setCreatedDate(LocalDateTime createdDate) { this.createdDate = Date.from(createdDate.atZone(ZoneId.systemDefault()).toInstant()); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Auditable#getLastModifiedBy() - */ @Override public Optional getLastModifiedBy() { return Optional.ofNullable(lastModifiedBy); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Auditable#setLastModifiedBy(java.lang.Object) - */ @Override public void setLastModifiedBy(U lastModifiedBy) { this.lastModifiedBy = lastModifiedBy; } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Auditable#getLastModifiedDate() - */ @Override public Optional getLastModifiedDate() { return null == lastModifiedDate ? Optional.empty() : Optional.of(LocalDateTime.ofInstant(lastModifiedDate.toInstant(), ZoneId.systemDefault())); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Auditable#setLastModifiedDate(java.time.temporal.TemporalAccessor) - */ @Override public void setLastModifiedDate(LocalDateTime lastModifiedDate) { this.lastModifiedDate = Date.from(lastModifiedDate.atZone(ZoneId.systemDefault()).toInstant()); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java index ff4f86f443..2531f9f615 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java @@ -41,10 +41,6 @@ public abstract class AbstractPersistable implements Pe @Id @GeneratedValue private @Nullable PK id; - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Persistable#getId() - */ @Nullable @Override public PK getId() { @@ -71,19 +67,11 @@ public boolean isNew() { return null == getId(); } - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { return String.format("Entity of type %s with id: %s", this.getClass().getName(), getId()); } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -104,10 +92,6 @@ public boolean equals(Object obj) { return null == this.getId() ? false : this.getId().equals(that.getId()); } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index 4e131e46f9..c29704cc32 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -338,10 +338,6 @@ public , U> Path dot(A attribute) { return newAttributes; } - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { @@ -401,19 +397,11 @@ private JpaOrder(@Nullable Direction direction, String property, NullHandling nu this.unsafe = unsafe; } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Sort.Order#with(org.springframework.data.domain.Sort.Direction) - */ @Override public JpaOrder with(Direction order) { return new JpaOrder(order, getProperty(), getNullHandling(), isIgnoreCase(), this.unsafe); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Sort.Order#with(org.springframework.data.domain.Sort.NullHandling) - */ @Override public JpaOrder with(NullHandling nullHandling) { return new JpaOrder(getDirection(), getProperty(), nullHandling, isIgnoreCase(), this.unsafe); @@ -439,19 +427,11 @@ public Sort withUnsafe(String... properties) { return Sort.by(orders); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Sort.Order#ignoreCase() - */ @Override public JpaOrder ignoreCase() { return new JpaOrder(getDirection(), getProperty(), getNullHandling(), true, this.unsafe); } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Sort.Order#isIgnoreCase() - */ @Override public boolean isIgnoreCase() { return super.isIgnoreCase() || ignoreCase; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java index d35cfc6d5d..2fc1170873 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java @@ -37,10 +37,6 @@ public class AuditingBeanFactoryPostProcessor implements BeanFactoryPostProcesso public static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME = "org.springframework.context.config.internalBeanConfigurerAspect"; - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) - */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index feb9176995..33b0dab855 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -61,30 +61,18 @@ public JpaMetamodelMappingContext(Set models) { this.persistenceProvider = PersistenceProvider.fromMetamodel(models.iterator().next()); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation) - */ @Override protected JpaPersistentEntityImpl createPersistentEntity(TypeInformation typeInformation) { return new JpaPersistentEntityImpl<>(typeInformation, persistenceProvider, models.getRequiredMetamodel(typeInformation)); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder) - */ @Override protected JpaPersistentProperty createPersistentProperty(Property property, JpaPersistentEntityImpl owner, SimpleTypeHolder simpleTypeHolder) { return new JpaPersistentPropertyImpl(owner.getMetamodel(), property, owner, simpleTypeHolder); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.context.AbstractMappingContext#shouldCreatePersistentEntityFor(org.springframework.data.util.TypeInformation) - */ @Override protected boolean shouldCreatePersistentEntityFor(TypeInformation type) { return models.isMetamodelManagedType(type.getUserTypeInformation()); @@ -102,10 +90,6 @@ public PersistentPropertyPaths findPersistentPrope return doFindPersistentPropertyPaths(type, predicate, JpaPersistentProperty::isEmbeddable); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.context.AbstractMappingContext#hasPersistentEntityFor(java.lang.Class) - */ @Override public boolean hasPersistentEntityFor(Class type) { return super.hasPersistentEntityFor(type) || models.isMetamodelManagedType(type); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index 0d389d851e..67cd5edd10 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -63,28 +63,16 @@ public JpaPersistentEntityImpl(TypeInformation information, ProxyIdAccessor p this.metamodel = metamodel; } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.BasicPersistentEntity#returnPropertyIfBetterIdPropertyCandidateOrNull(org.springframework.data.mapping.PersistentProperty) - */ @Override protected JpaPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(JpaPersistentProperty property) { return property.isIdProperty() ? property : null; } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.BasicPersistentEntity#getIdentifierAccessor(java.lang.Object) - */ @Override public IdentifierAccessor getIdentifierAccessor(Object bean) { return new JpaProxyAwareIdentifierAccessor(this, bean, proxyIdAccessor); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.BasicPersistentEntity#verify() - */ @Override public void verify() { @@ -131,10 +119,6 @@ private static class JpaProxyAwareIdentifierAccessor extends IdPropertyIdentifie this.bean = bean; } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.IdentifierAccessor#getIdentifier() - */ @Override public Object getIdentifier() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index 234c19e3bd..08fb038954 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -112,10 +112,6 @@ public JpaPersistentPropertyImpl(JpaMetamodel metamodel, Property property, this.isEntity = Lazy.of(() -> metamodel.isJpaManaged(getActualType())); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AbstractPersistentProperty#getActualType() - */ @Override public Class getActualType() { @@ -124,10 +120,6 @@ public Class getActualType() { : super.getActualType(); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AbstractPersistentProperty#getPersistentEntityTypeInformation() - */ @Override public Iterable> getPersistentEntityTypeInformation() { @@ -136,91 +128,51 @@ public Iterable> getPersistentEntityTypeInformation : super.getPersistentEntityTypeInformation(); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isIdProperty() - */ @Override public boolean isIdProperty() { return isIdProperty.get(); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AbstractPersistentProperty#isEntity() - */ @Override public boolean isEntity() { return isEntity.get(); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isAssociation() - */ @Override public boolean isAssociation() { return isAssociation.get(); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isTransient() - */ @Override public boolean isTransient() { return isAnnotationPresent(Transient.class) || super.isTransient(); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation() - */ @Override protected Association createAssociation() { return new Association(this, null); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#usePropertyAccess() - */ @Override public boolean usePropertyAccess() { return usePropertyAccess != null ? usePropertyAccess : super.usePropertyAccess(); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isVersionProperty() - */ @Override public boolean isVersionProperty() { return isAnnotationPresent(Version.class); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isWritable() - */ @Override public boolean isWritable() { return updateable && super.isWritable(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.mapping.JpaPersistentProperty#isEmbeddable() - */ @Override public boolean isEmbeddable() { return isAnnotationPresent(Embedded.class) || hasActualTypeAnnotation(Embeddable.class); } - /* - * (non-Javadoc) - * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#getAssociationTargetTypeInformation() - */ @Override public TypeInformation getAssociationTargetTypeInformation() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java index 5abdae5a32..64e0ca80b8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java @@ -31,10 +31,6 @@ */ public class CollectionAwareProjectionFactory extends SpelAwareProxyProjectionFactory { - /* - * (non-Javadoc) - * @see org.springframework.data.projection.SpelAwareProxyProjectionFactory#createProjectionInformation(java.lang.Class) - */ @Override protected ProjectionInformation createProjectionInformation(Class projectionType) { return new CollectionAwareProjectionInformation(projectionType); @@ -46,10 +42,6 @@ private static class CollectionAwareProjectionInformation extends SpelAwareProje super(projectionType); } - /* - * (non-Javadoc) - * @see org.springframework.data.projection.SpelAwareProxyProjectionFactory.SpelAwareProjectionInformation#isInputProperty(java.beans.PropertyDescriptor) - */ @Override protected boolean isInputProperty(PropertyDescriptor descriptor) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 02b071fed6..a877d49c15 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -79,37 +79,21 @@ public String getCountQueryPlaceholder() { return "*"; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object) - */ @Override public boolean shouldUseAccessorFor(Object entity) { return entity instanceof HibernateProxy; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) - */ @Override public Object getIdentifierFrom(Object entity) { return ((HibernateProxy) entity).getHibernateLazyInitializer().getIdentifier(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.provider.PersistenceProvider#getIdClassAttributes(javax.persistence.metamodel.IdentifiableType) - */ @Override public Set> getIdClassAttributes(IdentifiableType type) { return type.hasSingleIdAttribute() ? Collections.emptySet() : super.getIdClassAttributes(type); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.provider.PersistenceProvider#executeQueryWithResultStream(jakarta.persistence.Query) - */ @Override public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { return new HibernateScrollableResultsIterator(jpaQuery); @@ -127,29 +111,17 @@ public String extractQueryString(Query query) { return ((JpaQuery) query).getDatabaseQuery().getJPQLString(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object) - */ @Override public boolean shouldUseAccessorFor(Object entity) { return false; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) - */ @Nullable @Override public Object getIdentifierFrom(Object entity) { return null; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.provider.PersistenceProvider#executeQueryWithResultStream(jakarta.persistence.Query) - */ @Override public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { return new EclipseLinkScrollableResultsIterator<>(jpaQuery); @@ -161,38 +133,22 @@ public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { */ GENERIC_JPA(Collections.singleton(GENERIC_JPA_ENTITY_MANAGER_INTERFACE), Collections.emptySet()) { - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.QueryExtractor#extractQueryString(jakarta.persistence.Query) - */ @Nullable @Override public String extractQueryString(Query query) { return null; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.PersistenceProvider#canExtractQuery() - */ @Override public boolean canExtractQuery() { return false; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#isProxy(java.lang.Object) - */ @Override public boolean shouldUseAccessorFor(Object entity) { return false; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) - */ @Nullable @Override public Object getIdentifierFrom(Object entity) { @@ -295,10 +251,6 @@ public String getCountQueryPlaceholder() { return "x"; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.QueryExtractor#canExtractQuery() - */ @Override public boolean canExtractQuery() { return true; @@ -363,10 +315,6 @@ private static class HibernateScrollableResultsIterator implements CloseableIter .scroll(ScrollMode.FORWARD_ONLY); } - /* - * (non-Javadoc) - * @see java.util.Iterator#next() - */ @Override public Object next() { @@ -380,19 +328,11 @@ public Object next() { return row.length == 1 ? row[0] : row; } - /* - * (non-Javadoc) - * @see java.util.Iterator#hasNext() - */ @Override public boolean hasNext() { return scrollableResults != null && scrollableResults.next(); } - /* - * (non-Javadoc) - * @see org.springframework.data.util.CloseableIterator#close() - */ @Override public void close() { @@ -427,19 +367,11 @@ private static class EclipseLinkScrollableResultsIterator implements Closeabl this.scrollableCursor = (ScrollableCursor) jpaQuery.getSingleResult(); } - /* - * (non-Javadoc) - * @see java.util.Iterator#hasNext() - */ @Override public boolean hasNext() { return scrollableCursor != null && scrollableCursor.hasNext(); } - /* - * (non-Javadoc) - * @see java.util.Iterator#next() - */ @Override public T next() { @@ -450,10 +382,6 @@ public T next() { return (T) scrollableCursor.next(); } - /* - * (non-Javadoc) - * @see org.springframework.data.util.CloseableIterator#close() - */ @Override public void close() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index f8f8c0d5fe..ba10d435a3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -38,31 +38,15 @@ @NoRepositoryBean public interface JpaRepository extends PagingAndSortingRepository, QueryByExampleExecutor { - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findAll() - */ @Override List findAll(); - /* - * (non-Javadoc) - * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort) - */ @Override List findAll(Sort sort); - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) - */ @Override List findAllById(Iterable ids); - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) - */ @Override List saveAll(Iterable entities); @@ -174,10 +158,6 @@ default void deleteInBatch(Iterable entities) { @Override List findAll(Example example); - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort) - */ @Override List findAll(Example example, Sort sort); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index 9645fe2f16..203802b19e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -60,10 +60,6 @@ class JpaRepositoryBean extends CdiRepositoryBean { this.entityManagerBean = entityManagerBean; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(jakarta.enterprise.context.spi.CreationalContext, java.lang.Class) - */ @Override protected T create(CreationalContext creationalContext, Class repositoryType) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java index 335e0ff08a..f77140c4b9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java @@ -44,10 +44,6 @@ public class AuditingBeanDefinitionParser implements BeanDefinitionParser { BeanDefinitionNames.JPA_MAPPING_CONTEXT_BEAN_NAME); private final SpringConfiguredBeanDefinitionParser springConfiguredParser = new SpringConfiguredBeanDefinitionParser(); - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext) - */ @Override public BeanDefinition parse(Element element, ParserContext parser) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java index 9d90bde77e..9552190628 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java @@ -47,28 +47,16 @@ class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { private static final String BEAN_CONFIGURER_ASPECT_CLASS_NAME = "org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"; - /* - * (non-Javadoc) - * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation() - */ @Override protected Class getAnnotation() { return EnableJpaAuditing.class; } - /* - * (non-Javadoc) - * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName() - */ @Override protected String getAuditingHandlerBeanName() { return "jpaAuditingHandler"; } - /* - * (non-Javadoc) - * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration) - */ @Override protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) { @@ -79,10 +67,6 @@ protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingCon return builder.addConstructorArgValue(definition.getBeanDefinition()); } - /* - * (non-Javadoc) - * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry) - */ @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { @@ -96,10 +80,6 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD AuditingBeanFactoryPostProcessor.class.getName(), registry); } - /* - * (non-Javadoc) - * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry) - */ @Override protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition, BeanDefinitionRegistry registry) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index 8afdf1fc90..8a8c841446 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -48,28 +48,16 @@ public class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean getObjectType() { return JpaMetamodelMappingContext.class; } - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance() - */ @Override protected JpaMetamodelMappingContext createInstance() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java index 350fe2dd93..391a6b0ea8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java @@ -28,19 +28,11 @@ */ class JpaRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport { - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation() - */ @Override protected Class getAnnotation() { return EnableJpaRepositories.class; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension() - */ @Override protected RepositoryConfigurationExtension getExtension() { return new JpaRepositoryConfigExtension(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index b92348c052..5dab0e01ea 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -77,55 +77,31 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi private static final String JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME = "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup"; private static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter"; - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModuleName() - */ @Override public String getModuleName() { return "JPA"; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtension#getRepositoryFactoryBeanClassName() - */ @Override public String getRepositoryFactoryBeanClassName() { return JpaRepositoryFactoryBean.class.getName(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config14.RepositoryConfigurationExtensionSupport#getModulePrefix() - */ @Override protected String getModulePrefix() { return getModuleName().toLowerCase(Locale.US); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingAnnotations() - */ @Override protected Collection> getIdentifyingAnnotations() { return Arrays.asList(Entity.class, MappedSuperclass.class); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingTypes() - */ @Override protected Collection> getIdentifyingTypes() { return Collections.> singleton(JpaRepository.class); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.RepositoryConfigurationSource) - */ @Override public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) { @@ -149,10 +125,6 @@ private static Optional getEscapeCharacter(RepositoryConfigurationSou } } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource) - */ @Override public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) { @@ -162,10 +134,6 @@ public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfi attributes.getBoolean(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.XmlRepositoryConfigurationSource) - */ @Override public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) { @@ -176,10 +144,6 @@ public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfiguratio } } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#registerBeansForRoot(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.data.repository.config.RepositoryConfigurationSource) - */ @Override public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource config) { @@ -227,10 +191,6 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf }, registry, JpaEvaluationContextExtension.class.getName(), source); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getConfigurationInspectionClassLoader(org.springframework.core.io.ResourceLoader) - */ @Override protected ClassLoader getConfigurationInspectionClassLoader(ResourceLoader loader) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java index a95a6ae603..91d16a010b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java @@ -26,11 +26,6 @@ */ public class JpaRepositoryNameSpaceHandler extends NamespaceHandlerSupport { - /* - * (non-Javadoc) - * - * @see org.springframework.beans.factory.xml.NamespaceHandler#init() - */ @Override public void init() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 52af0614a5..dfd11cc60d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -106,10 +106,6 @@ public AbstractJpaQuery(JpaQueryMethod method, EntityManager em) { }); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod() - */ @Override public JpaQueryMethod getQueryMethod() { return method; @@ -133,10 +129,6 @@ protected JpaMetamodel getMetamodel() { return metamodel; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[]) - */ @Nullable @Override public Object execute(Object[] parameters) { @@ -304,10 +296,6 @@ public TupleConverter(ReturnedType type) { this.type = type; } - /* - * (non-Javadoc) - * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) - */ @Override public Object convert(Object source) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 5aaa71c0f6..4211f50c07 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -76,10 +76,6 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri "JDBC style parameters (?) are not supported for JPA queries."); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(JpaParametersParameterAccessor) - */ @Override public Query doCreateQuery(JpaParametersParameterAccessor accessor) { @@ -95,10 +91,6 @@ public Query doCreateQuery(JpaParametersParameterAccessor accessor) { return parameterBinder.get().bindAndPrepare(query, metadata, accessor); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#createBinder(JpaParametersParameterAccessor) - */ @Override protected ParameterBinder createBinder() { @@ -106,10 +98,6 @@ protected ParameterBinder createBinder() { evaluationContextProvider); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(JpaParametersParameterAccessor) - */ @Override protected Query doCreateCountQuery(JpaParametersParameterAccessor accessor) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java index f469b17ee3..edfc8cca3a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java @@ -42,19 +42,11 @@ public DefaultJpaEntityMetadata(Class domainType) { this.domainType = domainType; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.EntityMetadata#getJavaType() - */ @Override public Class getJavaType() { return domainType; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaEntityMetadata#getEntityName() - */ @Override public String getEntityName() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java index 7bd3e68d32..35896df5f7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java @@ -34,64 +34,36 @@ class EmptyDeclaredQuery implements DeclaredQuery { */ static final DeclaredQuery EMPTY_QUERY = new EmptyDeclaredQuery(); - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#hasNamedParameter() - */ @Override public boolean hasNamedParameter() { return false; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#getQueryString() - */ @Override public String getQueryString() { return ""; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#getAlias() - */ @Override public String getAlias() { return null; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#hasConstructorExpression() - */ @Override public boolean hasConstructorExpression() { return false; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#isDefaultProjection() - */ @Override public boolean isDefaultProjection() { return false; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#getParameterBindings() - */ @Override public List getParameterBindings() { return Collections.emptyList(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#deriveCountQuery(java.lang.String, java.lang.String) - */ @Override public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable String countQueryProjection) { @@ -100,10 +72,6 @@ public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable Str return DeclaredQuery.of(countQuery); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#usesJdbcStyleParameters() - */ @Override public boolean usesJdbcStyleParameters() { return false; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java index 95a54dc94d..ee7c6945ac 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java @@ -62,10 +62,6 @@ public char getEscapeCharacter() { return this.escapeCharacter; } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object o) { @@ -81,19 +77,11 @@ public boolean equals(Object o) { return escapeCharacter == that.escapeCharacter; } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { return escapeCharacter; } - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { return "EscapeCharacter(escapeCharacter=" + this.getEscapeCharacter() + ")"; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index 7105d349bb..e36f9d27f2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -48,19 +48,11 @@ public JpaCountQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder bu super(tree, type, builder, provider); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryCreator#createCriteriaQuery(jakarta.persistence.criteria.CriteriaBuilder, org.springframework.data.repository.query.ReturnedType) - */ @Override protected CriteriaQuery createCriteriaQuery(CriteriaBuilder builder, ReturnedType type) { return builder.createQuery(type.getDomainType()); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryCreator#complete(jakarta.persistence.criteria.Predicate, org.springframework.data.domain.Sort, jakarta.persistence.criteria.CriteriaQuery, jakarta.persistence.criteria.CriteriaBuilder, jakarta.persistence.criteria.Root) - */ @Override @SuppressWarnings("unchecked") protected CriteriaQuery complete(@Nullable Predicate predicate, Sort sort, diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java index 8ca7b8c55c..a94a190592 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java @@ -106,10 +106,6 @@ public boolean isAdHocEntityGraph() { return !attributePaths.isEmpty(); } - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { return "JpaEntityGraph [name=" + name + ", type=" + type + ", attributePaths=" + attributePaths.toString() + "]"; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index cdb59d8833..3f69741e1e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -50,19 +50,11 @@ private JpaParameters(List parameters) { super(parameters); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.Parameters#createParameter(org.springframework.core.MethodParameter) - */ @Override protected JpaParameter createParameter(MethodParameter parameter) { return new JpaParameter(parameter); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List) - */ @Override protected JpaParameters createFrom(List parameters) { return new JpaParameters(parameters); @@ -97,10 +89,6 @@ protected JpaParameter(MethodParameter parameter) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.Parameter#isBindable() - */ @Override public boolean isBindable() { return super.isBindable() || isTemporalParameter(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index dcf06f9d91..7584cd6994 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -114,29 +114,17 @@ public List> getParameterExpressions() { return provider.getExpressions(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#create(org.springframework.data.repository.query.parser.Part, java.util.Iterator) - */ @Override protected Predicate create(Part part, Iterator iterator) { return toPredicate(part, root); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator) - */ @Override protected Predicate and(Part part, Predicate base, Iterator iterator) { return builder.and(base, toPredicate(part, root)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#or(java.lang.Object, java.lang.Object) - */ @Override protected Predicate or(Predicate base, Predicate predicate) { return builder.or(base, predicate); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index dfb98ff139..bb8f0c931c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -136,10 +136,6 @@ protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccesso */ static class SlicedExecution extends JpaQueryExecution { - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) - */ @Override @SuppressWarnings("unchecked") protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { @@ -262,10 +258,6 @@ public DeleteExecution(EntityManager em) { this.em = em; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) - */ @Override protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAccessor accessor) { @@ -304,10 +296,6 @@ static class ProcedureExecution extends JpaQueryExecution { private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a @Procedure method without a surrounding transaction that keeps the connection open so that the ResultSet can actually be consumed. Make sure the consumer code uses @Transactional or any other way of declaring a (read-only) transaction."; - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) - */ @Override protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAccessor accessor) { @@ -347,10 +335,6 @@ static class StreamExecution extends JpaQueryExecution { private static Method streamMethod = ReflectionUtils.findMethod(Query.class, "getResultStream"); - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, JpaParametersParameterAccessor) - */ @Override protected Object doExecute(final AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 12d61cb9fc..771d3f62cc 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -77,10 +77,6 @@ public AbstractQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory query this.queryMethodFactory = queryMethodFactory; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries) - */ @Override public final RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { @@ -141,10 +137,6 @@ public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory query this.evaluationContextProvider = evaluationContextProvider; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy.AbstractQueryLookupStrategy#resolveQuery(org.springframework.data.jpa.repository.query.JpaQueryMethod, jakarta.persistence.EntityManager, org.springframework.data.repository.core.NamedQueries) - */ @Override protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { @@ -240,10 +232,6 @@ public CreateIfNotFoundQueryLookupStrategy(EntityManager em, JpaQueryMethodFacto this.lookupStrategy = lookupStrategy; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy.AbstractQueryLookupStrategy#resolveQuery(org.springframework.data.jpa.repository.query.JpaQueryMethod, jakarta.persistence.EntityManager, org.springframework.data.repository.core.NamedQueries) - */ @Override protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index 63f6a546e3..a830d269ce 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -175,10 +175,6 @@ private void assertParameterNamesInAnnotatedQuery() { } } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryMethod#getEntityInformation() - */ @Override @SuppressWarnings({ "rawtypes", "unchecked" }) public JpaEntityMetadata getEntityInformation() { @@ -336,10 +332,6 @@ boolean isNativeQuery() { return this.isNativeQuery.get(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryMethod#getNamedQueryName() - */ @Override public String getNamedQueryName() { @@ -399,28 +391,16 @@ private T getMergedOrDefaultAnnotationValue(String attribute, Class annotati return targetType.cast(AnnotationUtils.getValue(annotation, attribute)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryMethod#createParameters(java.lang.reflect.Method) - */ @Override protected JpaParameters createParameters(Method method) { return new JpaParameters(method); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryMethod#getParameters() - */ @Override public JpaParameters getParameters() { return (JpaParameters) super.getParameters(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryMethod#isCollectionQuery() - */ @Override public boolean isCollectionQuery() { return this.isCollectionQuery.get(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 7754d62cbb..5b7edddd62 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -157,10 +157,6 @@ public static RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(JpaParametersParameterAccessor) - */ @Override protected Query doCreateQuery(JpaParametersParameterAccessor accessor) { @@ -180,10 +176,6 @@ protected Query doCreateQuery(JpaParametersParameterAccessor accessor) { return parameterBinder.get().bindAndPrepare(query, metadata, accessor); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(JpaParametersParameterAccessor) - */ @Override protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { @@ -207,10 +199,6 @@ protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor acc return parameterBinder.get().bind(countQuery, metadata, accessor); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#getTypeToRead() - */ @Override protected Class getTypeToRead(ReturnedType returnedType) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index 6cbc0ae874..8697e34243 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -59,10 +59,6 @@ public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryStrin } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery#createJpaQuery(java.lang.String) - */ @Override protected Query createJpaQuery(String queryString, ReturnedType returnedType) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 6c000a7ae8..69775ad6e0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -97,29 +97,17 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(JpaParametersParameterAccessor) - */ @Override public Query doCreateQuery(JpaParametersParameterAccessor accessor) { return query.createQuery(accessor); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(JpaParametersParameterAccessor) - */ @Override @SuppressWarnings("unchecked") public TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { return (TypedQuery) countQuery.createQuery(accessor); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#getExecution() - */ @Override protected JpaQueryExecution getExecution() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index 322214abb4..e2f75cc650 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -75,10 +75,6 @@ class NamedOrIndexedQueryParameterSetter implements QueryParameterSetter { this.temporalType = temporalType; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.QueryParameterSetter#setParameter(jakarta.persistence.Query, java.lang.Object[]) - */ @SuppressWarnings("unchecked") @Override public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index 06464f156b..9cce41634f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -147,10 +147,6 @@ private static class ExpressionBasedQueryParameterSetterFactory extends QueryPar this.parameters = parameters; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.QueryParameterSetterFactory#create(org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding, java.lang.String) - */ @Nullable @Override public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery declaredQuery) { @@ -201,10 +197,6 @@ private static class BasicQueryParameterSetterFactory extends QueryParameterSett this.parameters = parameters; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.QueryParameterSetterFactory#create(org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding, java.lang.String) - */ @Override public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery declaredQuery) { @@ -286,10 +278,6 @@ private static class CriteriaQueryParameterSetterFactory extends QueryParameterS this.expressions = metadata; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.QueryParameterSetterFactory#create(org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding, java.lang.String) - */ @Override public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery declaredQuery) { @@ -359,30 +347,18 @@ private ParameterImpl(Class parameterType, @Nullable String name, @Nullable I this.parameterType = parameterType; } - /* - * (non-Javadoc) - * @see jakarta.persistence.Parameter#getName() - */ @Nullable @Override public String getName() { return name; } - /* - * (non-Javadoc) - * @see jakarta.persistence.Parameter#getPosition() - */ @Nullable @Override public Integer getPosition() { return position; } - /* - * (non-Javadoc) - * @see jakarta.persistence.Parameter#getParameterType() - */ @Override public Class getParameterType() { return parameterType; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index 7927b1e8c8..f7e242c68b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -80,19 +80,11 @@ private static boolean useNamedParameters(QueryMethod method) { return false; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#createQuery(JpaParametersParameterAccessor) - */ @Override protected StoredProcedureQuery createQuery(JpaParametersParameterAccessor accessor) { return applyHints(doCreateQuery(accessor), getQueryMethod()); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateQuery(JpaParametersParameterAccessor) - */ @Override protected StoredProcedureQuery doCreateQuery(JpaParametersParameterAccessor accessor) { @@ -102,10 +94,6 @@ protected StoredProcedureQuery doCreateQuery(JpaParametersParameterAccessor acce return parameterBinder.get().bind(storedProcedure, metadata, accessor); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#doCreateCountQuery(JpaParametersParameterAccessor) - */ @Override protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { throw new UnsupportedOperationException("StoredProcedureQuery does not support count queries!"); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 3760477ae6..770f0dbedc 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -89,19 +89,11 @@ String getProjection() { return QueryUtils.getProjection(query); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#getParameterBindings() - */ @Override public List getParameterBindings() { return bindings; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#deriveCountQuery(java.lang.String, java.lang.String) - */ @Override @SuppressWarnings("deprecation") public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable String countQueryProjection) { @@ -110,65 +102,37 @@ public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable Str .of(countQuery != null ? countQuery : QueryUtils.createCountQueryFor(query, countQueryProjection)); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#usesJdbcStyleParameters() - */ @Override public boolean usesJdbcStyleParameters() { return usesJdbcStyleParameters; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#getQueryString() - */ @Override public String getQueryString() { return query; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#getAlias() - */ @Override @Nullable public String getAlias() { return alias; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#hasConstructorExpression() - */ @Override public boolean hasConstructorExpression() { return hasConstructorExpression; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#isDefaultProjection() - */ @Override public boolean isDefaultProjection() { return getProjection().equalsIgnoreCase(alias); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#hasNamedParameter() - */ @Override public boolean hasNamedParameter() { return bindings.stream().anyMatch(b -> b.getName() != null); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.DeclaredQuery#usesPaging() - */ @Override public boolean usesPaging() { return containsPageableInSpel; @@ -554,10 +518,6 @@ public boolean isExpression() { return this.expression != null; } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { @@ -570,10 +530,6 @@ public int hashCode() { return result; } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -587,10 +543,6 @@ public boolean equals(Object obj) { && nullSafeEquals(this.expression, that.expression); } - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { return String.format("ParameterBinding [name: %s, position: %d, expression: %s]", getName(), getPosition(), @@ -633,10 +585,6 @@ static class InParameterBinding extends ParameterBinding { super(null, position, expression); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding#prepare(java.lang.Object) - */ @Override public Object prepare(@Nullable Object value) { @@ -763,10 +711,6 @@ public Object prepare(@Nullable Object value) { } } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -779,10 +723,6 @@ public boolean equals(Object obj) { return super.equals(obj) && this.type.equals(that.type); } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { @@ -793,10 +733,6 @@ public int hashCode() { return result; } - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { return String.format("LikeBinding [name: %s, position: %d, type: %s]", getName(), getPosition(), type); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index d9bc85ca57..2e067cf257 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -60,19 +60,11 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B private @Nullable ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader) - */ @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryProxyPostProcessor#postProcess(org.springframework.aop.framework.ProxyFactory, org.springframework.data.repository.core.RepositoryInformation) - */ @Override public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) { factory.addAdvice(new CrudMethodMetadataPopulatingMethodInterceptor(repositoryInformation)); @@ -132,10 +124,6 @@ static MethodInvocation currentInvocation() throws IllegalStateException { return mi; } - /* - * (non-Javadoc) - * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) - */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { @@ -245,47 +233,27 @@ private static org.springframework.data.jpa.repository.support.QueryHints findQu return queryHints; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getLockModeType() - */ @Nullable @Override public LockModeType getLockModeType() { return lockModeType; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getQueryHints() - */ @Override public org.springframework.data.jpa.repository.support.QueryHints getQueryHints() { return queryHints; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getQueryHintsForCount() - */ @Override public org.springframework.data.jpa.repository.support.QueryHints getQueryHintsForCount() { return queryHintsForCount; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getEntityGraph() - */ @Override public Optional getEntityGraph() { return entityGraph; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getMethod() - */ @Override public Method getMethod() { return method; @@ -294,28 +262,16 @@ public Method getMethod() { private static class ThreadBoundTargetSource implements TargetSource { - /* - * (non-Javadoc) - * @see org.springframework.aop.TargetSource#getTargetClass() - */ @Override public Class getTargetClass() { return CrudMethodMetadata.class; } - /* - * (non-Javadoc) - * @see org.springframework.aop.TargetSource#isStatic() - */ @Override public boolean isStatic() { return false; } - /* - * (non-Javadoc) - * @see org.springframework.aop.TargetSource#getTarget() - */ @Override public Object getTarget() { @@ -323,10 +279,6 @@ public Object getTarget() { return TransactionSynchronizationManager.getResource(invocation.getMethod()); } - /* - * (non-Javadoc) - * @see org.springframework.aop.TargetSource#releaseTarget(java.lang.Object) - */ @Override public void releaseTarget(Object target) {} } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java index 53331763fe..548220413b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java @@ -56,10 +56,6 @@ public DefaultJpaContext(Set entityManagers) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaContext#getByManagedType(java.lang.Class) - */ @Override public EntityManager getEntityManagerByManagedType(Class type) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java index 2589bcb326..8c8c3892d0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java @@ -75,28 +75,16 @@ public static QueryHints of(JpaEntityInformation information, CrudMethodMe return new DefaultQueryHints(information, metadata, Optional.empty(), false); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#withFetchGraphs() - */ @Override public QueryHints withFetchGraphs(EntityManager em) { return new DefaultQueryHints(this.information, this.metadata, Optional.of(em), this.forCounts); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#forCounts() - */ @Override public QueryHints forCounts() { return new DefaultQueryHints(this.information, this.metadata, this.entityManager, true); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#forEach(java.util.function.BiConsumer) - */ @Override public void forEach(BiConsumer action) { combineHints().forEach(action); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index 35e9c75b86..7857181c2b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -47,19 +47,11 @@ */ public class EntityManagerBeanDefinitionRegistrarPostProcessor implements BeanFactoryPostProcessor, Ordered { - /* - * (non-Javadoc) - * @see org.springframework.core.Ordered#getOrder() - */ @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE + 10; } - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) - */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index a70f846c47..96a99684b2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -77,10 +77,6 @@ private FetchableFluentQueryByExample(Example example, Class entityType, C this.escapeCharacter = escapeCharacter; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort) - */ @Override public FetchableFluentQuery sortBy(Sort sort) { @@ -90,10 +86,6 @@ public FetchableFluentQuery sortBy(Sort sort) { countOperation, existsOperation, entityManager, escapeCharacter); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) - */ @Override public FetchableFluentQuery as(Class resultType) { @@ -106,10 +98,6 @@ public FetchableFluentQuery as(Class resultType) { countOperation, existsOperation, entityManager, escapeCharacter); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection) - */ @Override public FetchableFluentQuery project(Collection properties) { @@ -117,10 +105,6 @@ public FetchableFluentQuery project(Collection properties) { finder, countOperation, existsOperation, entityManager, escapeCharacter); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() - */ @Override public R oneValue() { @@ -136,10 +120,6 @@ public R oneValue() { return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue() - */ @Override public R firstValue() { @@ -151,10 +131,6 @@ public R firstValue() { return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() - */ @Override public List all() { @@ -163,19 +139,11 @@ public List all() { return convert(resultList); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) - */ @Override public Page page(Pageable pageable) { return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() - */ @Override public Stream stream() { @@ -184,19 +152,11 @@ public Stream stream() { .map(getConversionFunction()); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() - */ @Override public long count() { return countOperation.apply(example); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() - */ @Override public boolean exists() { return existsOperation.apply(example); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index 7f8522db0c..009d891594 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -80,10 +80,6 @@ private FetchableFluentQueryByPredicate(Predicate predicate, Class entityType this.entityManager = entityManager; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort) - */ @Override public FetchableFluentQuery sortBy(Sort sort) { @@ -93,10 +89,6 @@ public FetchableFluentQuery sortBy(Sort sort) { pagedFinder, countOperation, existsOperation, entityManager); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) - */ @Override public FetchableFluentQuery as(Class resultType) { @@ -110,10 +102,6 @@ public FetchableFluentQuery as(Class resultType) { pagedFinder, countOperation, existsOperation, entityManager); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection) - */ @Override public FetchableFluentQuery project(Collection properties) { @@ -121,10 +109,6 @@ public FetchableFluentQuery project(Collection properties) { finder, pagedFinder, countOperation, existsOperation, entityManager); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() - */ @Override public R oneValue() { @@ -139,10 +123,6 @@ public R oneValue() { return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue() - */ @Override public R firstValue() { @@ -153,28 +133,16 @@ public R firstValue() { return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() - */ @Override public List all() { return convert(createSortedAndProjectedQuery().fetch()); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) - */ @Override public Page page(Pageable pageable) { return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() - */ @Override public Stream stream() { @@ -183,19 +151,11 @@ public Stream stream() { .map(getConversionFunction()); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() - */ @Override public long count() { return countOperation.apply(predicate); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() - */ @Override public boolean exists() { return existsOperation.apply(predicate); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java index d7532f1c25..1dc6f0d1e3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -67,10 +67,6 @@ public JpaEntityInformationSupport(Class domainClass) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaEntityMetadata#getEntityName() - */ @Override public String getEntityName() { return metadata.getEntityName(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java index b5214145bc..ebff0a8181 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java @@ -37,19 +37,11 @@ public JpaEvaluationContextExtension(char escapeCharacter) { this.root = JpaRootObject.of(EscapeCharacter.of(escapeCharacter)); } - /* - * (non-Javadoc) - * @see org.springframework.data.spel.spi.EvaluationContextExtension#getExtensionId() - */ @Override public String getExtensionId() { return "jpa"; } - /* - * (non-Javadoc) - * @see org.springframework.data.spel.spi.EvaluationContextExtension#getRootObject() - */ @Override public Object getRootObject() { return root; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index 6717ad32d5..bc5238d620 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -90,10 +90,6 @@ public JpaMetamodelEntityInformation(Class domainClass, Metamodel metamodel) this.versionAttribute = findVersionAttribute(identifiableType, metamodel); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaEntityInformationSupport#getEntityName() - */ @Override public String getEntityName() { return entityName != null ? entityName : super.getEntityName(); @@ -139,10 +135,6 @@ public String getEntityName() { return findVersionAttribute((IdentifiableType) managedSuperType, metamodel); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.EntityInformation#getId(java.lang.Object) - */ @Override @Nullable @SuppressWarnings("unchecked") @@ -178,38 +170,22 @@ public ID getId(T entity) { return partialIdValueFound ? (ID) idWrapper.getWrappedInstance() : null; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.EntityInformation#getIdType() - */ @Override @SuppressWarnings("unchecked") public Class getIdType() { return (Class) idMetadata.getType(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaEntityInformation#getIdAttribute() - */ @Override public SingularAttribute getIdAttribute() { return idMetadata.getSimpleIdAttribute(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaEntityInformation#hasCompositeId() - */ @Override public boolean hasCompositeId() { return !idMetadata.hasSimpleId(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaEntityInformation#getIdAttributeNames() - */ @Override public Iterable getIdAttributeNames() { @@ -222,10 +198,6 @@ public Iterable getIdAttributeNames() { return attributeNames; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaEntityInformation#getCompositeIdAttributeValue(java.lang.Object, java.lang.String) - */ @Override public Object getCompositeIdAttributeValue(Object id, String idAttribute) { @@ -234,10 +206,6 @@ public Object getCompositeIdAttributeValue(Object id, String idAttribute) { return new DirectFieldAccessFallbackBeanWrapper(id).getPropertyValue(idAttribute); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.AbstractEntityInformation#isNew(java.lang.Object) - */ @Override public boolean isNew(T entity) { @@ -325,10 +293,6 @@ private static Class lookupIdClass(IdentifiableType type) { return attributes.iterator().next(); } - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ @Override public Iterator> iterator() { return attributes.iterator(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java index 562961c777..6009b5ac19 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java @@ -40,19 +40,11 @@ public JpaPersistableEntityInformation(Class domainClass, Metamodel metamodel super(domainClass, metamodel); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation#isNew(java.lang.Object) - */ @Override public boolean isNew(T entity) { return entity.isNew(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation#getId(java.lang.Object) - */ @Nullable @Override public ID getId(T entity) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 7fe9b0115c..a8a98c6cf4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -111,10 +111,6 @@ public JpaRepositoryFactory(EntityManager entityManager) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#setBeanClassLoader(java.lang.ClassLoader) - */ @Override public void setBeanClassLoader(ClassLoader classLoader) { @@ -155,10 +151,6 @@ public void setQueryMethodFactory(JpaQueryMethodFactory queryMethodFactory) { this.queryMethodFactory = queryMethodFactory; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata) - */ @Override protected final JpaRepositoryImplementation getTargetRepository(RepositoryInformation information) { @@ -187,19 +179,11 @@ public void setQueryMethodFactory(JpaQueryMethodFactory queryMethodFactory) { return (JpaRepositoryImplementation) repository; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata) - */ @Override protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { return SimpleJpaRepository.class; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getProjectionFactory(java.lang.ClassLoader, org.springframework.beans.factory.BeanFactory) - */ @Override protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFactory beanFactory) { @@ -210,10 +194,6 @@ protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFa return factory; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider) - */ @Override protected Optional getQueryLookupStrategy(@Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider) { @@ -222,10 +202,6 @@ protected Optional getQueryLookupStrategy(@Nullable Key key escapeCharacter)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class) - */ @Override @SuppressWarnings("unchecked") public JpaEntityInformation getEntityInformation(Class domainClass) { @@ -233,10 +209,6 @@ public JpaEntityInformation getEntityInformation(Class domainC return (JpaEntityInformation) JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryFragments(org.springframework.data.repository.core.RepositoryMetadata) - */ @Override protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) { @@ -323,10 +295,6 @@ public EclipseLinkProjectionQueryCreationListener(EntityManager em) { this.metamodel = JpaMetamodel.of(em.getMetamodel()); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.QueryCreationListener#onCreation(org.springframework.data.repository.query.RepositoryQuery) - */ @Override public void onCreation(AbstractJpaQuery query) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index b33ace36a8..a3eb78d48e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -69,10 +69,6 @@ public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#setMappingContext(org.springframework.data.mapping.context.MappingContext) - */ @Override public void setMappingContext(MappingContext mappingContext) { super.setMappingContext(mappingContext); @@ -104,10 +100,6 @@ public void setQueryMethodFactory(@Nullable JpaQueryMethodFactory factory) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport#doCreateRepositoryFactory() - */ @Override protected RepositoryFactorySupport doCreateRepositoryFactory() { @@ -132,10 +124,6 @@ protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityM return jpaRepositoryFactory; } - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() - */ @Override public void afterPropertiesSet() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java index 0bf32a19db..9663eaf767 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java @@ -37,28 +37,16 @@ public class MutableQueryHints implements QueryHints { private final MultiValueMap values = new LinkedMultiValueMap<>(); - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#withFetchGraphs(jakarta.persistence.EntityManager) - */ @Override public QueryHints withFetchGraphs(EntityManager em) { return this; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#forCounts() - */ @Override public QueryHints forCounts() { return this; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#forEach(java.util.function.BiConsumer) - */ @Override public void forEach(BiConsumer action) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java index 223c8a0f56..aabfbfefdf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java @@ -92,28 +92,16 @@ enum NoHints implements QueryHints { INSTANCE; - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#withFetchGraphs(jakarta.persistence.EntityManager) - */ @Override public QueryHints withFetchGraphs(EntityManager em) { return this; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#forCounts(jakarta.persistence.EntityManager) - */ @Override public QueryHints forCounts() { return this; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.QueryHints#forEach(java.util.function.BiConsumer) - */ @Override public void forEach(BiConsumer action) {} } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 0ccefd508d..84110594e1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -85,10 +85,6 @@ public QuerydslJpaPredicateExecutor(JpaEntityInformation entityInformation this.entityManager = entityManager; } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.querydsl.core.types.Predicate) - */ @Override public Optional findOne(Predicate predicate) { @@ -101,10 +97,6 @@ public Optional findOne(Predicate predicate) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate) - */ @Override public List findAll(Predicate predicate) { @@ -113,10 +105,6 @@ public List findAll(Predicate predicate) { return createQuery(predicate).select(path).fetch(); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, com.querydsl.core.types.OrderSpecifier[]) - */ @Override public List findAll(Predicate predicate, OrderSpecifier... orders) { @@ -126,10 +114,6 @@ public List findAll(Predicate predicate, OrderSpecifier... orders) { return executeSorted(createQuery(predicate).select(path), orders); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort) - */ @Override public List findAll(Predicate predicate, Sort sort) { @@ -139,10 +123,6 @@ public List findAll(Predicate predicate, Sort sort) { return executeSorted(createQuery(predicate).select(path), sort); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[]) - */ @Override public List findAll(OrderSpecifier... orders) { @@ -151,10 +131,6 @@ public List findAll(OrderSpecifier... orders) { return executeSorted(createQuery(new Predicate[0]).select(path), orders); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Pageable) - */ @Override public Page findAll(Predicate predicate, Pageable pageable) { @@ -167,10 +143,6 @@ public Page findAll(Predicate predicate, Pageable pageable) { return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function) - */ @SuppressWarnings("unchecked") @Override public R findBy(Predicate predicate, Function, R> queryFunction) { @@ -212,19 +184,11 @@ public R findBy(Predicate predicate, Function) fluentQuery); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.querydsl.core.types.Predicate) - */ @Override public long count(Predicate predicate) { return createQuery(predicate).fetchCount(); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.querydsl.core.types.Predicate) - */ @Override public boolean exists(Predicate predicate) { return createQuery(predicate).select(Expressions.ONE).fetchFirst() != null; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index 7570675340..f4f29a0c63 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -97,10 +97,6 @@ public QuerydslJpaRepository(JpaEntityInformation entityInformation, Enti this.entityManager = entityManager; } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findOne(com.querydsl.core.types.Predicate) - */ @Override public Optional findOne(Predicate predicate) { @@ -111,28 +107,16 @@ public Optional findOne(Predicate predicate) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate) - */ @Override public List findAll(Predicate predicate) { return createQuery(predicate).select(path).fetch(); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, com.querydsl.core.types.OrderSpecifier[]) - */ @Override public List findAll(Predicate predicate, OrderSpecifier... orders) { return executeSorted(createQuery(predicate).select(path), orders); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Sort) - */ @Override public List findAll(Predicate predicate, Sort sort) { @@ -141,10 +125,6 @@ public List findAll(Predicate predicate, Sort sort) { return executeSorted(createQuery(predicate).select(path), sort); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.OrderSpecifier[]) - */ @Override public List findAll(OrderSpecifier... orders) { @@ -153,10 +133,6 @@ public List findAll(OrderSpecifier... orders) { return executeSorted(createQuery(new Predicate[0]).select(path), orders); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Pageable) - */ @Override public Page findAll(Predicate predicate, Pageable pageable) { @@ -175,19 +151,11 @@ public R findBy(Predicate predicate, "Fluent Query API support for Querydsl is only found in QuerydslJpaPredicateExecutor."); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.querydsl.core.types.Predicate) - */ @Override public long count(Predicate predicate) { return createQuery(predicate).fetchCount(); } - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.querydsl.core.types.Predicate) - */ @Override public boolean exists(Predicate predicate) { return createQuery(predicate).fetchCount() > 0; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index fd412f8ebc..70255e57a0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -156,10 +156,6 @@ private String getCountQueryString() { return getQueryString(countQuery, entityInformation.getEntityName()); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable) - */ @Transactional @Override public void deleteById(ID id) { @@ -170,10 +166,6 @@ public void deleteById(ID id) { String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1))); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object) - */ @Override @Transactional @SuppressWarnings("unchecked") @@ -197,10 +189,6 @@ public void delete(T entity) { em.remove(em.contains(entity) ? entity : em.merge(entity)); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#deleteAllById(java.lang.Iterable) - */ @Override @Transactional public void deleteAllById(Iterable ids) { @@ -212,10 +200,6 @@ public void deleteAllById(Iterable ids) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#deleteAllByIdInBatch(java.lang.Iterable) - */ @Override @Transactional public void deleteAllByIdInBatch(Iterable ids) { @@ -235,10 +219,6 @@ public void deleteAllByIdInBatch(Iterable ids) { query.executeUpdate(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) - */ @Override @Transactional public void deleteAll(Iterable entities) { @@ -250,10 +230,6 @@ public void deleteAll(Iterable entities) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#deleteInBatch(java.lang.Iterable) - */ @Override @Transactional public void deleteAllInBatch(Iterable entities) { @@ -268,10 +244,6 @@ public void deleteAllInBatch(Iterable entities) { .executeUpdate(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.Repository#deleteAll() - */ @Override @Transactional public void deleteAll() { @@ -281,20 +253,12 @@ public void deleteAll() { } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#deleteAllInBatch() - */ @Override @Transactional public void deleteAllInBatch() { em.createQuery(getDeleteAllQueryString()).executeUpdate(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findById(java.io.Serializable) - */ @Override public Optional findById(ID id) { @@ -324,20 +288,12 @@ protected QueryHints getQueryHints() { return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#getOne(java.io.Serializable) - */ @Deprecated @Override public T getOne(ID id) { return getReferenceById(id); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#getById(java.io.Serializable) - */ @Deprecated @Override public T getById(ID id) { @@ -355,10 +311,6 @@ public T getReferenceById(ID id) { return em.getReference(getDomainClass(), id); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#existsById(java.io.Serializable) - */ @Override public boolean existsById(ID id) { @@ -399,19 +351,11 @@ public boolean existsById(ID id) { return query.getSingleResult() == 1L; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#findAll() - */ @Override public List findAll() { return getQuery(null, Sort.unsorted()).getResultList(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable) - */ @Override public List findAllById(Iterable ids) { @@ -440,19 +384,11 @@ public List findAllById(Iterable ids) { return query.setParameter(specification.parameter, idCollection).getResultList(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#findAll(org.springframework.data.domain.Sort) - */ @Override public List findAll(Sort sort) { return getQuery(null, sort).getResultList(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable) - */ @Override public Page findAll(Pageable pageable) { @@ -463,10 +399,6 @@ public Page findAll(Pageable pageable) { return findAll((Specification) null, pageable); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findOne(org.springframework.data.jpa.domain.Specification) - */ @Override public Optional findOne(@Nullable Specification spec) { @@ -477,19 +409,11 @@ public Optional findOne(@Nullable Specification spec) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification) - */ @Override public List findAll(@Nullable Specification spec) { return getQuery(spec, Sort.unsorted()).getResultList(); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Pageable) - */ @Override public Page findAll(@Nullable Specification spec, Pageable pageable) { @@ -498,19 +422,11 @@ public Page findAll(@Nullable Specification spec, Pageable pageable) { : readPage(query, getDomainClass(), pageable, spec); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Sort) - */ @Override public List findAll(@Nullable Specification spec, Sort sort) { return getQuery(spec, sort).getResultList(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryByExampleExecutor#findOne(org.springframework.data.domain.Example) - */ @Override public Optional findOne(Example example) { @@ -523,20 +439,12 @@ public Optional findOne(Example example) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryByExampleExecutor#count(org.springframework.data.domain.Example) - */ @Override public long count(Example example) { return executeCountQuery( getCountQuery(new ExampleSpecification(example, escapeCharacter), example.getProbeType())); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryByExampleExecutor#exists(org.springframework.data.domain.Example) - */ @Override public boolean exists(Example example) { @@ -548,30 +456,18 @@ public boolean exists(Example example) { return query.setMaxResults(1).getResultList().size() == 1; } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example) - */ @Override public List findAll(Example example) { return getQuery(new ExampleSpecification(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) .getResultList(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort) - */ @Override public List findAll(Example example, Sort sort) { return getQuery(new ExampleSpecification(example, escapeCharacter), example.getProbeType(), sort) .getResultList(); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Pageable) - */ @Override public Page findAll(Example example, Pageable pageable) { @@ -582,10 +478,6 @@ public Page findAll(Example example, Pageable pageable) { return isUnpaged(pageable) ? new PageImpl<>(query.getResultList()) : readPage(query, probeType, pageable, spec); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.QueryByExampleExecutor#findBy(org.springframework.data.domain.Example, java.util.function.Function) - */ @Override public R findBy(Example example, Function, R> queryFunction) { @@ -606,28 +498,16 @@ public R findBy(Example example, Function spec) { return executeCountQuery(getCountQuery(spec, getDomainClass())); } - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object) - */ @Transactional @Override public S save(S entity) { @@ -642,10 +522,6 @@ public S save(S entity) { } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#saveAndFlush(java.lang.Object) - */ @Transactional @Override public S saveAndFlush(S entity) { @@ -656,10 +532,6 @@ public S saveAndFlush(S entity) { return result; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#save(java.lang.Iterable) - */ @Transactional @Override public List saveAll(Iterable entities) { @@ -675,10 +547,6 @@ public List saveAll(Iterable entities) { return result; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#saveAllAndFlush(java.lang.Iterable) - */ @Transactional @Override public List saveAllAndFlush(Iterable entities) { @@ -689,10 +557,6 @@ public List saveAllAndFlush(Iterable entities) { return result; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaRepository#flush() - */ @Transactional @Override public void flush() { @@ -929,10 +793,6 @@ private static final class ByIdsSpecification implements Specification { this.entityInformation = entityInformation; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.domain.Specification#toPredicate(jakarta.persistence.criteria.Root, jakarta.persistence.criteria.CriteriaQuery, jakarta.persistence.criteria.CriteriaBuilder) - */ @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { @@ -972,10 +832,6 @@ private static class ExampleSpecification implements Specification { this.escapeCharacter = escapeCharacter; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.domain.Specification#toPredicate(jakarta.persistence.criteria.Root, jakarta.persistence.criteria.CriteriaQuery, jakarta.persistence.criteria.CriteriaBuilder) - */ @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { return QueryByExamplePredicateBuilder.getPredicate(root, cb, example, escapeCharacter); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java index 85e8aff1e2..7822e58476 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java @@ -92,10 +92,6 @@ public void setMappingFileNamePattern(String mappingFilePattern) { this.mappingFileNamePattern = mappingFilePattern; } - /* - * (non-Javadoc) - * @see org.springframework.context.ResourceLoaderAware#setResourceLoader(org.springframework.core.io.ResourceLoader) - */ @Override public void setResourceLoader(ResourceLoader resourceLoader) { @@ -105,10 +101,6 @@ public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } - /* - * (non-Javadoc) - * @see org.springframework.context.EnvironmentAware#setEnvironment(org.springframework.core.env.Environment) - */ @Override public void setEnvironment(Environment environment) { @@ -117,10 +109,6 @@ public void setEnvironment(Environment environment) { this.environment = environment; } - /* - * (non-Javadoc) - * @see org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor#postProcessPersistenceUnitInfo(org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo) - */ @Override public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index c51f385673..4d9b38df5c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -36,10 +36,6 @@ public class MergingPersistenceUnitManager extends DefaultPersistenceUnitManager private static final Log LOG = LogFactory.getLog(MergingPersistenceUnitManager.class); - /* - * (non-Javadoc) - * @see org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager#postProcessPersistenceUnitInfo(org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo) - */ @Override protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { @@ -53,10 +49,6 @@ protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { } } - /* - * (non-Javadoc) - * @see org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager#isPersistenceUnitOverrideAllowed() - */ @Override protected boolean isPersistenceUnitOverrideAllowed() { return true; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java index 63c1f03e00..0bd0394b87 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java @@ -215,10 +215,6 @@ public BeanDefinition getBeanDefinition() { return beanFactory.getBeanDefinition(beanName); } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object o) { @@ -239,10 +235,6 @@ public boolean equals(Object o) { return ObjectUtils.nullSafeEquals(beanFactory, that.beanFactory); } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { int result = ObjectUtils.nullSafeHashCode(beanName); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java index 4e3316ed2d..dcba8b9ea9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java @@ -31,10 +31,6 @@ class HibernateProxyDetector implements ProxyDetector { private static final Optional> HIBERNATE_PROXY = Optional.ofNullable(loadHibernateProxyType()); - /* - * (non-Javadoc) - * @see org.springframework.data.util.ProxyUtils.ProxyDetector#getUserType(java.lang.Class) - */ @Override public Class getUserType(Class type) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java index c2b753cefc..ed783022bb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java @@ -29,10 +29,6 @@ */ class JpaMetamodelCacheCleanup implements DisposableBean { - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.DisposableBean#destroy() - */ @Override public void destroy() throws Exception { JpaMetamodel.clear(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java index d0150723b8..c9056e206f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java @@ -44,10 +44,6 @@ public void setAuditor(AuditableUser auditor) { this.auditor = auditor; } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.AuditorAware#getCurrentAuditor() - */ @Override public Optional getCurrentAuditor() { return Optional.ofNullable(auditor); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java index 817e87c969..c9251da09b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java @@ -44,9 +44,6 @@ public void setDepartmentId(Long departmentId) { this.departmentId = departmentId; } - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { final int prime = 31; @@ -56,9 +53,6 @@ public int hashCode() { return result; } - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (this == obj) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java index 4aa52ae03f..fa67463cb6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java @@ -54,9 +54,6 @@ public void setDepartmentId(Long departmentId) { this.departmentId = departmentId; } - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { final int prime = 31; @@ -66,9 +63,6 @@ public int hashCode() { return result; } - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (this == obj) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java index 181f4fc63c..1cda6c3db0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java @@ -35,19 +35,11 @@ public class EntityWithAssignedId implements Persistable { private @Transient boolean isNew = true; - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Persistable#getId() - */ @Override public UUID getId() { return id; } - /* - * (non-Javadoc) - * @see org.springframework.data.domain.Persistable#isNew() - */ @Override public boolean isNew() { return isNew; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java index e9e34b9408..978daf4760 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java @@ -44,9 +44,6 @@ public void setDepartmentId(long departmentId) { this.departmentId = departmentId; } - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { final int prime = 31; @@ -56,9 +53,6 @@ public int hashCode() { return result; } - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (this == obj) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java index fecf304ec9..032134d9ea 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java @@ -63,10 +63,6 @@ public void setMailUser(MailUser mailUser) { this.mailUser = mailUser; } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { final int prime = 31; @@ -75,10 +71,6 @@ public int hashCode() { return result; } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (this == obj) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java index 9c3292a50b..7c764d8951 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java @@ -66,17 +66,11 @@ public Long getSecond() { return second; } - /* (non-Javadoc) - * @see org.springframework.data.domain.Persistable#getId() - */ @Override public PersistableWithIdClassPK getId() { return new PersistableWithIdClassPK(first, second); } - /* (non-Javadoc) - * @see org.springframework.data.domain.Persistable#isNew() - */ @Override public boolean isNew() { return this.isNew; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java index 1a67eca79d..a60f3f032c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java @@ -47,10 +47,6 @@ public void setSecond(Long second) { this.second = second; } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -67,10 +63,6 @@ public boolean equals(Object obj) { return nullSafeEquals(this.first, that.first) && nullSafeEquals(this.second, that.second); } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java index cedf4aa1ee..4fc01df870 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java @@ -40,10 +40,6 @@ public void setFirst(Long first) { this.first = first; } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -60,10 +56,6 @@ public boolean equals(Object obj) { return nullSafeEquals(this.first, that.first); } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java index 4e88344349..08c91b6ef2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java @@ -64,11 +64,6 @@ public String getName() { return name; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ @Override public String toString() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java index 14ac29e774..0631ffaf9f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java @@ -46,11 +46,6 @@ public SampleEntityPK(String first, String second) { this.second = second; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -67,11 +62,6 @@ public boolean equals(Object obj) { return this.first.equals(that.first) && this.second.equals(that.second); } - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java index b8678b8f30..ba1cef184a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClass.java @@ -22,10 +22,6 @@ static class SampleWithIdClassPK implements Serializable { Long first; Long second; - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -42,10 +38,6 @@ public boolean equals(Object obj) { return this.first.equals(that.first) && this.second.equals(that.second); } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { return first.hashCode() + second.hashCode(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java index 0ae5b0ee92..b7b5a13e57 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -361,11 +361,6 @@ public byte[] getBinaryData() { return binaryData; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -408,11 +403,6 @@ public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ @Override public String toString() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java index 38dd803a31..c4af51eb19 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java @@ -50,10 +50,6 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() { } } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessorUnitTests#getBeanFactory() - */ @Override DefaultListableBeanFactory getBeanFactory() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java index 326c48a4c4..b013c0d354 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java @@ -29,10 +29,6 @@ */ class AuditingNamespaceUnitTests extends AuditingBeanFactoryPostProcessorUnitTests { - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessorUnitTests#getConfigFile() - */ @Override String getConfigFile() { return "auditing-namespace-context.xml"; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java index 1363aa813c..1ad5297084 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java @@ -29,10 +29,6 @@ */ public class CustomEclipseLinkJpaVendorAdapter extends EclipseLinkJpaVendorAdapter { - /* - * (non-Javadoc) - * @see org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter#determineTargetDatabaseName(org.springframework.orm.jpa.vendor.Database) - */ @Override protected String determineTargetDatabaseName(Database database) { @@ -52,10 +48,6 @@ protected String determineTargetDatabaseName(Database database) { @SuppressWarnings("serial") public static class EclipseLinkHsqlPlatform extends HSQLPlatform { - /* - * (non-Javadoc) - * @see org.eclipse.persistence.internal.databaseaccess.DatabasePlatform#getProcedureCallHeader() - */ @Override public String getProcedureCallHeader() { return "CALL "; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java index 0564d09f24..4dade6a684 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java @@ -29,10 +29,6 @@ */ public class CustomHsqlHibernateJpaVendorAdaptor extends HibernateJpaVendorAdapter { - /* - * (non-Javadoc) - * @see org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter#determineDatabaseDialectClass(org.springframework.orm.jpa.vendor.Database) - */ @Override protected Class determineDatabaseDialectClass(Database database) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java index 5bccdf77af..335cd6cc52 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java @@ -28,13 +28,6 @@ @ContextConfiguration(locations = "classpath:config/namespace-autoconfig-typefilter-context.xml") class TypeFilterConfigTests extends AbstractRepositoryConfigTests { - /* - * (non-Javadoc) - * - * @see - * org.springframework.data.jpa.repository.config.AbstractRepositoryConfigTests - * #testContextCreation() - */ @Override void testContextCreation() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java index f7e35a1835..c463664409 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java @@ -39,13 +39,6 @@ public CustomGenericJpaRepository(JpaEntityInformation metadata, EntityMa super(metadata, entityManager); } - /* - * (non-Javadoc) - * - * @see - * org.springframework.data.jpa.repository.custom.CustomGenericRepository - * #customMethod(java.io.Serializable) - */ @Override public T customMethod(ID id) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java index 2a45fd3452..76f46ab52a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java @@ -42,10 +42,6 @@ public CustomGenericJpaRepositoryFactory(EntityManager entityManager) { super(entityManager); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaRepositoryFactory#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata, jakarta.persistence.EntityManager) - */ @Override @SuppressWarnings("unchecked") protected SimpleJpaRepository getTargetRepository(RepositoryInformation information, EntityManager em) { @@ -55,10 +51,6 @@ public CustomGenericJpaRepositoryFactory(EntityManager entityManager) { return new CustomGenericJpaRepository(entityMetadata, em); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaRepositoryFactory#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata) - */ @Override protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { return CustomGenericJpaRepository.class; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java index 9d7c19bbc9..a41344fd96 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java @@ -36,12 +36,6 @@ public CustomGenericJpaRepositoryFactoryBean(Class repositoryInterf super(repositoryInterface); } - /* - * (non-Javadoc) - * - * @see org.springframework.data.jpa.repository.support. - * GenericJpaRepositoryFactoryBean#getFactory() - */ @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java index 2889f95c37..a72f000b82 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java @@ -37,28 +37,16 @@ */ public interface RoleRepository extends CrudRepository, QuerydslPredicateExecutor { - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findAll() - */ @Override @Lock(LockModeType.READ) @QueryHints(@QueryHint(name = "foo", value = "bar")) Iterable findAll(); - /* - * (non-Javadoc) - * @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable) - */ @Override @Lock(LockModeType.READ) @QueryHints(@QueryHint(name = "foo", value = "bar")) Optional findById(Integer id); - /* - * (non-Javadoc) - * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findOne(com.querydsl.core.types.Predicate) - */ @Override @Lock(LockModeType.READ) @QueryHints(@QueryHint(name = "foo", value = "bar")) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java index 648bb7da8d..6f23da4918 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java @@ -36,19 +36,11 @@ public UserRepositoryImpl(JpaContext context) { Assert.notNull(context, "JpaContext must not be null!"); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.sample.UserRepositoryCustom#someCustomMethod(org.springframework.data.jpa.domain.sample.User) - */ @Override public void someCustomMethod(User u) { LOG.debug("Some custom method was invoked!"); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.sample.UserRepositoryCustom#findByOverrridingMethod() - */ @Override public void findByOverrridingMethod() { LOG.debug("A method overriding a finder was invoked!"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java index a06707e0f6..28c12dc6ef 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java @@ -170,10 +170,6 @@ public DummyJpaRepositoryFactoryBean(Class repositoryInterface) { super(repositoryInterface); } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.predicateExecutor.support.JpaRepositoryFactoryBean#doCreateRepositoryFactory() - */ @Override protected RepositoryFactorySupport doCreateRepositoryFactory() { return factory; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java index c3995296f6..828431f815 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java @@ -26,10 +26,6 @@ @ContextConfiguration("classpath:openjpa.xml") class OpenJpaJpaRepositoryTests extends JpaRepositoryTests { - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.support.JpaRepositoryTests#testCrudOperationsForCompoundKeyEntity() - */ @Override @Disabled void testCrudOperationsForCompoundKeyEntity() throws Exception { From ba68e26e9d40aab61d8d973dfb96671cb24fce1b Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Thu, 13 Jan 2022 18:18:15 +0100 Subject: [PATCH 125/821] Fixes JavaDoc problems. See #2232 --- .../org/springframework/data/jpa/repository/JpaRepository.java | 2 +- .../springframework/data/jpa/repository/query/QueryUtils.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index ba10d435a3..bf3de1f650 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -141,7 +141,7 @@ default void deleteInBatch(Iterable entities) { /** * Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is * implemented this is very likely to always return an instance and throw an - * {@link javax.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers + * {@link jakarta.persistence.EntityNotFoundException} on first access. Some of them will reject invalid identifiers * immediately. * * @param id must not be {@literal null}. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index c9010d9d74..c42fef8b2d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -472,7 +472,6 @@ public static String createCountQueryFor(String originalQuery) { * @param originalQuery must not be {@literal null}. * @param countProjection may be {@literal null}. * @return a query String to be used a count query for pagination. Guaranteed to be not {@literal null}. - * @return a query String to be used a count query for pagination. Guaranteed to be not {@literal null}. * @since 1.6 * @deprecated use {@link DeclaredQuery#deriveCountQuery(String, String)} instead. */ From 0fd8737024379bd9c245cc602bbcdc6d08c46425 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Jan 2022 14:34:47 +0100 Subject: [PATCH 126/821] Prepare 3.0 M1 (2022.0.0). See #2402 --- pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 3edbc5d5fa..a750a161cf 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 @@ -16,17 +15,18 @@ org.springframework.data.build spring-data-parent - 3.0.0-SNAPSHOT + 3.0.0-M1 - 16 + 16 + 3.0.2 5.6.0.Final 8.0.23 42.2.19 - 3.0.0-SNAPSHOT + 3.0.0-M1 0.10.3 org.hibernate @@ -209,8 +209,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From ee9af383325332bb96bd08f36a31d8402373be2e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Jan 2022 14:35:09 +0100 Subject: [PATCH 127/821] Release version 3.0 M1 (2022.0.0). See #2402 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index a750a161cf..b8c7f0b2bc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M1 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 5e82ca77cc..09caad559b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-SNAPSHOT + 3.0.0-M1 org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M1 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index bba8571672..0c8bc944f5 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M1 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 5960d72cca..1c159a2b42 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-SNAPSHOT + 3.0.0-M1 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M1 ../pom.xml From ba1350429f9b6c051e043a0290673376dab7b4aa Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Jan 2022 14:43:19 +0100 Subject: [PATCH 128/821] Prepare next development iteration. See #2402 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index b8c7f0b2bc..a750a161cf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M1 + 3.0.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 09caad559b..5e82ca77cc 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-M1 + 3.0.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.0.0-M1 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 0c8bc944f5..bba8571672 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M1 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 1c159a2b42..5960d72cca 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-M1 + 3.0.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M1 + 3.0.0-SNAPSHOT ../pom.xml From 408237b2769f3642c982d9dc4bd067efe0cd1718 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Jan 2022 14:43:22 +0100 Subject: [PATCH 129/821] After release cleanups. See #2402 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index a750a161cf..07101c166f 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-M1 + 3.0.0-SNAPSHOT @@ -26,7 +26,7 @@ 5.6.0.Final 8.0.23 42.2.19 - 3.0.0-M1 + 3.0.0-SNAPSHOT 0.10.3 org.hibernate @@ -209,8 +209,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From a1faffe4afa7a05715ea98dc7163fcf8e0affef2 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 25 Jan 2022 09:15:52 +0100 Subject: [PATCH 130/821] Fix potential NullPointerException in JpaQueryCreator. With the commit for GH-2363, we introduced an unguarded call to ReturnedType.getTypeToRead(), which could return null under certain conditions. Unfortunately Spring Data JPA dod not contain a test case that triggered that scenario. This commit switches to ReturnedType.getReturnedType() which is non-nullable and lets us inspect the calls results for interfaces, which we need to create the JPA query properly. Fixes GH-2408 --- .../data/jpa/repository/query/JpaQueryCreator.java | 2 +- .../data/jpa/repository/UserRepositoryTests.java | 8 ++++++++ .../data/jpa/repository/sample/UserRepository.java | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 7584cd6994..6777a4e8b9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -164,7 +164,7 @@ protected CriteriaQuery complete(@Nullable Predicate predicate selections.add(toExpressionRecursively(root, path, true).alias(property)); } - Class typeToRead = returnedType.getTypeToRead(); + Class typeToRead = returnedType.getReturnedType(); query = typeToRead.isInterface() ? query.multiselect(selections) 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 e481be31fa..a07ea34028 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 @@ -2630,6 +2630,14 @@ void readsDtoProjections() { assertThat(repository.findAllDtoProjectedBy()).hasSize(4); } + @Test // GH-2408, GH-2363 + void readsDerivedInterfaceProjections() { + + flushTestUsers(); + + assertThat(repository.findAllInterfaceProjectedBy()).hasSize(4); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); 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 32b0b36767..6570a5ea87 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 @@ -614,6 +614,9 @@ Page findAllOrderedBySpecialNameMultipleParams(@Param("name") String name, // #2363 List findAllDtoProjectedBy(); + // GH-2408 + List findAllInterfaceProjectedBy(); + interface RolesAndFirstname { String getFirstname(); From 621eb18dbd5a8b6551145ef1ec9d043492894fe7 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 25 Jan 2022 09:17:06 +0100 Subject: [PATCH 131/821] Polishing. Indentation in testcases (spaces -> tabs). Additional imports. --- .../data/jpa/repository/UserRepositoryTests.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) 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 a07ea34028..e89a581daf 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 @@ -57,6 +57,8 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; +import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; +import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; @@ -1011,16 +1013,16 @@ void looksUpEntityReferenceUsingGetById() { assertThat(result).isEqualTo(firstUser); } - @Test // GH-2232 - void looksUpEntityReferenceUsingGetReferenceById() { + @Test // GH-2232 + void looksUpEntityReferenceUsingGetReferenceById() { - flushTestUsers(); + flushTestUsers(); - User result = repository.getReferenceById(firstUser.getId()); - assertThat(result).isEqualTo(firstUser); - } + User result = repository.getReferenceById(firstUser.getId()); + assertThat(result).isEqualTo(firstUser); + } - @Test // DATAJPA-415 + @Test // DATAJPA-415 void invokesQueryWithVarargsParametersCorrectly() { flushTestUsers(); From 881bfbda79ec79a20fdf02a6e627a65a1bed76d7 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 25 Jan 2022 10:32:20 -0600 Subject: [PATCH 132/821] Fix 'noSuchProperty' test case. The error message for nonexistent properties inside query derivation now wraps the named property with single quotes. This breaks a test case that was looking for spaces before and after. Forward ported to 3.0.x See #2418. --- .../jpa/repository/query/PartTreeJpaQueryIntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 58025e45e2..974734efb4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -222,7 +222,7 @@ void errorsDueToMissingPropertyContainNameOfMethodAndInterface() throws Exceptio assertThatExceptionOfType(IllegalArgumentException.class) // .isThrownBy(() -> new PartTreeJpaQuery(method, entityManager)) // .withMessageContaining("findByNoSuchProperty") // the method being analyzed - .withMessageContaining(" noSuchProperty ") // the property we are looking for + .withMessageContaining("'noSuchProperty'") // the property we are looking for .withMessageContaining("UserRepository"); // the repository } From 892e4e5da228927685a87d86dee519095b54a5d2 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 26 Jan 2022 22:52:33 +0100 Subject: [PATCH 133/821] Guard against Hibernate exposing non-embeddables as embeddables on versions > 5.4.21. Hibernate 5.4.22 started including types managed by custom user types in the embeddables exposed via JPA's Metamodel API. This causes those to be considered types to explicitly map (read: unfold) in Spring Data REST. We now exposed a cleaned up view on this via a tweak in JpaPersistentPropertyImpl (ultimately in JpaMetamodel) looking for @Embeddable annotation on types allegedly considered embeddable by Hibernate. Fixes #2421. --- .../mapping/JpaPersistentPropertyImpl.java | 2 +- .../data/jpa/util/JpaMetamodel.java | 36 ++++++++++++ .../data/jpa/util/JpaMetamodelUnitTests.java | 58 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index 08fb038954..9f5efda42d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -109,7 +109,7 @@ public JpaPersistentPropertyImpl(JpaMetamodel metamodel, Property property, this.isIdProperty = Lazy.of(() -> ID_ANNOTATIONS.stream().anyMatch(it -> isAnnotationPresent(it)) // || metamodel.isSingleIdAttribute(getOwner().getType(), getName(), getType())); - this.isEntity = Lazy.of(() -> metamodel.isJpaManaged(getActualType())); + this.isEntity = Lazy.of(() -> metamodel.isMappedType(getActualType())); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java index 8331db055c..d417e2edd4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java @@ -16,15 +16,20 @@ package org.springframework.data.jpa.util; import java.util.Collection; +import java.util.EnumSet; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import jakarta.persistence.Embeddable; import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.ManagedType; import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; +import jakarta.persistence.metamodel.Type.PersistenceType; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.data.util.Lazy; import org.springframework.data.util.StreamUtils; import org.springframework.util.Assert; @@ -39,10 +44,13 @@ public class JpaMetamodel { private static final Map CACHE = new ConcurrentHashMap<>(4); + private static final Set ENTITY_OR_MAPPED_SUPERCLASS = EnumSet.of(PersistenceType.ENTITY, + PersistenceType.MAPPED_SUPERCLASS); private final Metamodel metamodel; private Lazy>> managedTypes; + private Lazy>> jpaEmbeddables; /** * Creates a new {@link JpaMetamodel} for the given JPA {@link Metamodel}. @@ -54,10 +62,17 @@ private JpaMetamodel(Metamodel metamodel) { Assert.notNull(metamodel, "Metamodel must not be null!"); this.metamodel = metamodel; + this.managedTypes = Lazy.of(() -> metamodel.getManagedTypes().stream() // .map(ManagedType::getJavaType) // .filter(it -> it != null) // .collect(StreamUtils.toUnmodifiableSet())); + + this.jpaEmbeddables = Lazy.of(() -> metamodel.getEmbeddables().stream() // + .map(ManagedType::getJavaType) + .filter(it -> it != null) + .filter(it -> AnnotatedElementUtils.isAnnotated(it, Embeddable.class)) + .collect(StreamUtils.toUnmodifiableSet())); } public static JpaMetamodel of(Metamodel metamodel) { @@ -96,6 +111,27 @@ public boolean isSingleIdAttribute(Class entity, String name, Class attrib .orElse(false); } + /** + * Returns whether the given type is considered a mapped type, i.e. an actually JPA persisted entity, mapped + * superclass or native JPA embeddable. + * + * @param entity must not be {@literal null}. + * @return + */ + public boolean isMappedType(Class entity) { + + Assert.notNull(entity, "Type must not be null!"); + + if (!isJpaManaged(entity)) { + return false; + } + + ManagedType managedType = metamodel.managedType(entity); + + return !managedType.getPersistenceType().equals(PersistenceType.EMBEDDABLE) + || jpaEmbeddables.get().contains(entity); + } + /** * Wipes the static cache of {@link Metamodel} to {@link JpaMetamodel}. */ diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java index 37fc84555a..8aab83f2b0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java @@ -18,10 +18,18 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.metamodel.EmbeddableType; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.ManagedType; import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.Type.PersistenceType; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -60,4 +68,54 @@ void cacheIsEffectiveUnlessCleared() { JpaMetamodel.clear(); assertThat(model).isNotEqualTo(JpaMetamodel.of(metamodel)); } + + @Test // #2421 + void doesNotConsiderNonNativeEmbeddablesJpaManaged() { + + JpaMetamodel model = JpaMetamodel.of(metamodel); + + ManagedType entity = getEntity(Wrapper.class); + ManagedType embeddable = getEmbeddable(ExplicitEmbeddable.class); + ManagedType inner = getEmbeddable(Inner.class); + + doReturn(new HashSet<>(Arrays.asList(entity, embeddable, inner))).when(metamodel).getManagedTypes(); + doReturn(new HashSet<>(Arrays.asList(embeddable, inner))).when(metamodel).getEmbeddables(); + + assertThat(model.isMappedType(Wrapper.class)).isTrue(); + assertThat(model.isMappedType(ExplicitEmbeddable.class)).isTrue(); + assertThat(model.isMappedType(Inner.class)).isFalse(); + } + + private EmbeddableType getEmbeddable(Class type) { + + EmbeddableType managedType = getManagedType(type, EmbeddableType.class); + doReturn(PersistenceType.EMBEDDABLE).when(managedType).getPersistenceType(); + + return managedType; + } + + private EntityType getEntity(Class type) { + + EntityType managedType = getManagedType(type, EntityType.class); + doReturn(PersistenceType.ENTITY).when(managedType).getPersistenceType(); + + return managedType; + } + + private > T getManagedType(Class type, Class baseType) { + + T managedType = mock(baseType); + doReturn(type).when(managedType).getJavaType(); + doReturn(managedType).when(metamodel).managedType(type); + + return managedType; + } + + @Entity + static class Wrapper {} + + @Embeddable + static class ExplicitEmbeddable {} + + static class Inner {} } From 428ddb3b40b5f90c81fb6701d9491239d77c7d8f Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 18 Jan 2022 16:50:46 -0600 Subject: [PATCH 134/821] Externalize build properties. Closes #2413. --- Jenkinsfile | 10 ++++++++-- ci/pipeline.properties | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 ci/pipeline.properties diff --git a/Jenkinsfile b/Jenkinsfile index 4a11f0b168..3fc4894261 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,3 +1,9 @@ +def p = [:] +node { + checkout scm + p = readProperties interpolate: true, file: 'ci/pipeline.properties' +} + pipeline { agent none @@ -31,7 +37,7 @@ pipeline { steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('openjdk:17-bullseye').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') { + docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}" sh 'PROFILE=all-dbs ci/test.sh' sh "ci/clean.sh" @@ -61,7 +67,7 @@ pipeline { steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { - docker.image('openjdk:17-bullseye').inside('-v $HOME:/tmp/jenkins-home') { + docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + diff --git a/ci/pipeline.properties b/ci/pipeline.properties new file mode 100644 index 0000000000..5d91102d25 --- /dev/null +++ b/ci/pipeline.properties @@ -0,0 +1,24 @@ +# Java versions +java.main.tag=8u312-b07-jdk +java.next.tag=11.0.13_8-jdk +java.lts.tag=17.0.1_12-jdk + +# Docker container images - standard +docker.java.main.image=eclipse-temurin:${java.main.tag} +docker.java.next.image=eclipse-temurin:${java.next.tag} +docker.java.lts.image=eclipse-temurin:${java.lts.tag} + +# Supported versions of MongoDB +docker.mongodb.4.0.version=4.0.23 +docker.mongodb.4.4.version=4.4.4 +docker.mongodb.5.0.version=5.0.3 + +# Supported versions of Redis +docker.redis.6.version=6.2.4 + +# Supported versions of Cassandra +docker.cassandra.3.version=3.11.10 + +# Docker environment settings +docker.java.inside.basic=-v $HOME:/tmp/jenkins-home +docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home From 1737389c7b555616e84c9addd58c54522fd9259f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 4 Feb 2022 09:33:51 +0100 Subject: [PATCH 135/821] Polishing. Extract docker credentials into properties file. See #2413 --- Jenkinsfile | 8 ++++---- ci/pipeline.properties | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3fc4894261..91894a748c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -18,7 +18,7 @@ pipeline { } stages { - stage("test: baseline (jdk17)") { + stage("test: baseline (Java 17)") { when { beforeAgent(true) anyOf { @@ -31,12 +31,12 @@ pipeline { } options { timeout(time: 30, unit: 'MINUTES') } environment { - DOCKER_HUB = credentials('hub.docker.com-springbuildmaster') + DOCKER_HUB = credentials("${p['docker.credentials']}") ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') } steps { script { - docker.withRegistry('', 'hub.docker.com-springbuildmaster') { + docker.withRegistry(p['docker.registry'], p['docker.credentials']) { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}" sh 'PROFILE=all-dbs ci/test.sh' @@ -66,7 +66,7 @@ pipeline { steps { script { - docker.withRegistry('', 'hub.docker.com-springbuildmaster') { + docker.withRegistry(p['docker.registry'], p['docker.credentials']) { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + '-Dartifactory.server=https://repo.spring.io ' + diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 5d91102d25..59c20600ed 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,15 +1,10 @@ # Java versions -java.main.tag=8u312-b07-jdk -java.next.tag=11.0.13_8-jdk -java.lts.tag=17.0.1_12-jdk +java.main.tag=17.0.2_8-jdk # Docker container images - standard docker.java.main.image=eclipse-temurin:${java.main.tag} -docker.java.next.image=eclipse-temurin:${java.next.tag} -docker.java.lts.image=eclipse-temurin:${java.lts.tag} # Supported versions of MongoDB -docker.mongodb.4.0.version=4.0.23 docker.mongodb.4.4.version=4.4.4 docker.mongodb.5.0.version=5.0.3 @@ -17,8 +12,12 @@ docker.mongodb.5.0.version=5.0.3 docker.redis.6.version=6.2.4 # Supported versions of Cassandra -docker.cassandra.3.version=3.11.10 +docker.cassandra.3.version=3.11.11 # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home + +# Credentials +docker.registry= +docker.credentials=hub.docker.com-springbuildmaster From 9b9cb5aff53e49d0ea1869637e6b72dcc9bb95b0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 4 Feb 2022 09:49:12 +0100 Subject: [PATCH 136/821] Polishing. Extract artifactory credentials into properties file. See #2413 --- Jenkinsfile | 4 ++-- ci/pipeline.properties | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 91894a748c..02988c7680 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -32,7 +32,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { DOCKER_HUB = credentials("${p['docker.credentials']}") - ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + ARTIFACTORY = credentials("${p['artifactory.credentials']}") } steps { script { @@ -61,7 +61,7 @@ pipeline { options { timeout(time: 20, unit: 'MINUTES') } environment { - ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + ARTIFACTORY = credentials("${p['artifactory.credentials']}") } steps { diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 59c20600ed..02d3783039 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -21,3 +21,4 @@ docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock - # Credentials docker.registry= docker.credentials=hub.docker.com-springbuildmaster +artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c From 0b94f0d78da1a04c5c55b8387d4d1bd318b82d92 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 28 Jan 2022 15:20:20 +0100 Subject: [PATCH 137/821] Follow through with the split of CRUD repository and sorting repository. Closes #2436 See spring-projects/spring-data-commons#2537 --- .../org/springframework/data/jpa/repository/JpaRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index bf3de1f650..fd68531c88 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -21,6 +21,7 @@ import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; +import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; @@ -36,7 +37,7 @@ * @author Greg Turnquist */ @NoRepositoryBean -public interface JpaRepository extends PagingAndSortingRepository, QueryByExampleExecutor { +public interface JpaRepository extends CrudRepository,PagingAndSortingRepository, QueryByExampleExecutor { @Override List findAll(); From 2f13a5eb922b804a2e17626e20ac514c35080026 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 15 Feb 2022 09:00:21 -0600 Subject: [PATCH 138/821] Update CI properties. See #2407 --- ci/pipeline.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 02d3783039..60ce02f476 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -2,7 +2,7 @@ java.main.tag=17.0.2_8-jdk # Docker container images - standard -docker.java.main.image=eclipse-temurin:${java.main.tag} +docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} # Supported versions of MongoDB docker.mongodb.4.4.version=4.4.4 From a7fd176643db9399e2e3cd305ac3213131ebde6e Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 7 Jan 2022 09:25:19 -0600 Subject: [PATCH 139/821] Ignore fields with Optional.empty when performing Query by Example. The Auditable interface introduces Optional getters, which when combined with Query by Example results in cryptic errors. By ignoring a probe's field that contains an Optional.empty, Query by Example works properly. NOTE: This fix actually tests outside the originally detected scope of Auditable, verifying that ALL Optional.empty() fields are properly handled. Closes #2176 Original pull request #2401 --- .../QueryByExamplePredicateBuilder.java | 5 ++ .../domain/sample/UserWithOptionalField.java | 60 +++++++++++++++ .../UserWithOptionalFieldRepository.java | 23 ++++++ .../QueryByExampleWithOptionalEmptyTests.java | 73 +++++++++++++++++++ .../test/resources/META-INF/persistence.xml | 1 + 5 files changed, 162 insertions(+) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java index c896e87ee2..d5e0219cba 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java @@ -56,6 +56,7 @@ * @author Mark Paluch * @author Oliver Gierke * @author Jens Schauder + * @author Greg Turnquist * @since 1.10 */ public class QueryByExamplePredicateBuilder { @@ -146,6 +147,10 @@ static List getPredicates(String path, CriteriaBuilder cb, Path fr Object attributeValue = optionalValue.get(); + if (attributeValue == Optional.empty()) { + continue; + } + if (attribute.getPersistentAttributeType().equals(PersistentAttributeType.EMBEDDED) || (isAssociation(attribute) && !(from instanceof From))) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java new file mode 100644 index 0000000000..f11eef3f2b --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java @@ -0,0 +1,60 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import lombok.Data; + +import java.util.Optional; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import org.springframework.lang.Nullable; + +/** + * @author Greg Turnquist + */ +@Entity +@Data +public class UserWithOptionalField { + + @Id @GeneratedValue private Long id; + private String name; + private String role; + + public UserWithOptionalField() { + + this.id = null; + this.name = null; + this.role = null; + } + + public UserWithOptionalField(String name, @Nullable String role) { + + this(); + this.name = name; + this.role = role; + } + + public Optional getRole() { + return Optional.ofNullable(this.role); + } + + public void setRole(Optional role) { + this.role = role.orElse(null); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java new file mode 100644 index 0000000000..edf5f9d2ea --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @author Greg Turnquist + */ +public interface UserWithOptionalFieldRepository extends JpaRepository {} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java new file mode 100644 index 0000000000..59d6080227 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.support; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.data.domain.Example; +import org.springframework.data.jpa.domain.sample.UserWithOptionalField; +import org.springframework.data.jpa.domain.sample.UserWithOptionalFieldRepository; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Integration test for {@link org.springframework.data.repository.query.QueryByExampleExecutor} involving + * {@link Optional#empty()}. + * + * @author Greg Turnquist + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration +public class QueryByExampleWithOptionalEmptyTests { + + @Autowired UserWithOptionalFieldRepository repository; + UserWithOptionalField user; + + @Test + void queryByExampleTreatsEmptyOptionalsLikeNulls() { + + // given + UserWithOptionalField user = new UserWithOptionalField(); + user.setName("Greg"); + repository.saveAndFlush(user); + + // when + UserWithOptionalField probe = new UserWithOptionalField(); + probe.setName("Greg"); + Example example = Example.of(probe); + + // then + List results = repository.findAll(example); + + assertThat(results).hasSize(1); + assertThat(results).extracting(UserWithOptionalField::getName).containsExactly("Greg"); + } + + @Configuration + @EnableJpaRepositories(basePackageClasses = UserWithOptionalFieldRepository.class) + @ImportResource("classpath:infrastructure.xml") + static class JpaRepositoryConfig {} + +} diff --git a/spring-data-jpa/src/test/resources/META-INF/persistence.xml b/spring-data-jpa/src/test/resources/META-INF/persistence.xml index 2a35e280d3..79adc56fa9 100644 --- a/spring-data-jpa/src/test/resources/META-INF/persistence.xml +++ b/spring-data-jpa/src/test/resources/META-INF/persistence.xml @@ -45,6 +45,7 @@ org.springframework.data.jpa.domain.sample.Site org.springframework.data.jpa.domain.sample.SpecialUser org.springframework.data.jpa.domain.sample.User + org.springframework.data.jpa.domain.sample.UserWithOptionalField org.springframework.data.jpa.domain.sample.VersionedUser org.springframework.data.jpa.domain.sample.Dummy org.springframework.data.jpa.domain.sample.SampleWithIdClassIncludingEntity From 21e042460d093a972172c07add3463e567e01616 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 4 Feb 2022 13:51:47 +0100 Subject: [PATCH 140/821] Polishing. Removed Given/When/Then comments. They are of limited use, especially since it is debatable what belongs in which section. See #2176 Original pull request #2401 --- .../domain/support/QueryByExampleWithOptionalEmptyTests.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java index 59d6080227..23bc7a115a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java @@ -48,17 +48,14 @@ public class QueryByExampleWithOptionalEmptyTests { @Test void queryByExampleTreatsEmptyOptionalsLikeNulls() { - // given UserWithOptionalField user = new UserWithOptionalField(); user.setName("Greg"); repository.saveAndFlush(user); - // when UserWithOptionalField probe = new UserWithOptionalField(); probe.setName("Greg"); Example example = Example.of(probe); - // then List results = repository.findAll(example); assertThat(results).hasSize(1); From 242adf5365911b3318cfa815a0f0f1f7c3143274 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Mon, 7 Feb 2022 12:24:09 +0100 Subject: [PATCH 141/821] Removed unnecessary interface modifier in `BeanDefinitionNames`. Original pull request #2432 All fields of an interface are by default `public static final`. To make the code more readable the redundancy is removed. --- .../data/jpa/repository/config/BeanDefinitionNames.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java index f592b701f9..322488aad4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ */ interface BeanDefinitionNames { - public static final String JPA_MAPPING_CONTEXT_BEAN_NAME = "jpaMappingContext"; - public static final String JPA_CONTEXT_BEAN_NAME = "jpaContext"; - public static final String EM_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME = "emBeanDefinitionRegistrarPostProcessor"; + String JPA_MAPPING_CONTEXT_BEAN_NAME = "jpaMappingContext"; + String JPA_CONTEXT_BEAN_NAME = "jpaContext"; + String EM_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME = "emBeanDefinitionRegistrarPostProcessor"; } From 71c40f1f006bed7ef007ca507cada9c2b34f84c3 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Mon, 7 Feb 2022 12:15:27 +0100 Subject: [PATCH 142/821] Remove unnecessary `toString()` call. There were few explicit `toString()` calls that are not needed, since `toString()` will be called by default. Original pull request #2431 --- .../data/jpa/convert/QueryByExamplePredicateBuilder.java | 4 ++-- .../data/jpa/repository/query/StringQuery.java | 8 ++++---- .../ClasspathScanningPersistenceUnitPostProcessor.java | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java index d5e0219cba..6e45701f98 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -278,7 +278,7 @@ public String toString() { StringBuilder sb = new StringBuilder(); if (parent != null) { - sb.append(parent.toString()); + sb.append(parent); sb.append(" -"); sb.append(name); sb.append("-> "); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 770f0dbedc..fef47a6f9f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -700,11 +700,11 @@ public Object prepare(@Nullable Object value) { switch (type) { case STARTING_WITH: - return String.format("%s%%", value.toString()); + return String.format("%s%%", value); case ENDING_WITH: - return String.format("%%%s", value.toString()); + return String.format("%%%s", value); case CONTAINING: - return String.format("%%%s%%", value.toString()); + return String.format("%%%s%%", value); case LIKE: default: return value; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java index 7822e58476..3b85ad6535 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -179,7 +179,7 @@ private Set scanForMappingFileLocations() { mappingFileUris.add(resourcePathInClasspath); } catch (IOException e) { - throw new IllegalStateException(String.format("Couldn't get URI for %s!", resource.toString()), e); + throw new IllegalStateException(String.format("Couldn't get URI for %s!", resource), e); } } From 6018c7c5f7efe9545999266114835cd192e3490c Mon Sep 17 00:00:00 2001 From: Ernst-Jan van der Laan Date: Fri, 21 Jan 2022 15:45:46 +0100 Subject: [PATCH 143/821] Support compound IdClass ID's when calling deleteAllByIdInBatch. Convert ID's to entities and pass them to deleteAllInBatch. Closes #2414 Original pull request #2419 --- .../support/SimpleJpaRepository.java | 20 ++++++++++---- .../RepositoryWithCompositeKeyTests.java | 27 ++++++++++++++++++- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 70255e57a0..e36b094525 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -81,6 +81,7 @@ * @author Jesse Wouters * @author Greg Turnquist * @author Yanming Zhou + * @author Ernst-Jan van der Laan */ @Repository @Transactional(readOnly = true) @@ -210,13 +211,22 @@ public void deleteAllByIdInBatch(Iterable ids) { return; } - String queryString = String.format(DELETE_ALL_QUERY_BY_ID_STRING, entityInformation.getEntityName(), - entityInformation.getIdAttribute().getName()); + if (entityInformation.hasCompositeId()) { + // XXX Hibernate just creates an empty Entity when doing the getById. + // Others might do a select right away causing a big performance penalty. + // See JavaDoc for getById. + List entities = new ArrayList<>(); + ids.forEach(id -> entities.add(getById(id))); + deleteAllInBatch(entities); + } else { + String queryString = String.format(DELETE_ALL_QUERY_BY_ID_STRING, entityInformation.getEntityName(), + entityInformation.getIdAttribute().getName()); - Query query = em.createQuery(queryString); - query.setParameter("ids", ids); + Query query = em.createQuery(queryString); + query.setParameter("ids", ids); - query.executeUpdate(); + query.executeUpdate(); + } } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index c055a41b90..150f9486c3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -20,9 +20,10 @@ import java.util.Arrays; import java.util.List; +import jakarta.persistence.EntityManager; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -47,6 +48,7 @@ * @author Thomas Darimont * @author Mark Paluch * @author Jens Schauder + * @author Ernst-Jan van der Laan */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SampleConfig.class) @@ -55,6 +57,7 @@ public class RepositoryWithCompositeKeyTests { @Autowired EmployeeRepositoryWithIdClass employeeRepositoryWithIdClass; @Autowired EmployeeRepositoryWithEmbeddedId employeeRepositoryWithEmbeddedId; + @Autowired EntityManager em; /** * @see Final JPA 2.0 @@ -126,6 +129,28 @@ void shouldSupportFindAllWithPageableAndEntityWithIdClass() throws Exception { assertThat(page.getTotalElements()).isEqualTo(1L); } + @Test // DATAJPA-2414 + void shouldSupportDeleteAllByIdInBatchWithIdClass() throws Exception { + + IdClassExampleDepartment dep = new IdClassExampleDepartment(); + dep.setName("TestDepartment"); + dep.setDepartmentId(-1); + + IdClassExampleEmployee emp = new IdClassExampleEmployee(); + emp.setDepartment(dep); + emp = employeeRepositoryWithIdClass.save(emp); + + IdClassExampleEmployeePK key = new IdClassExampleEmployeePK(emp.getEmpId(), dep.getDepartmentId()); + assertThat(employeeRepositoryWithIdClass.findById(key)).isNotEmpty(); + + employeeRepositoryWithIdClass.deleteAllByIdInBatch(Arrays.asList(key)); + + em.flush(); + em.clear(); + + assertThat(employeeRepositoryWithIdClass.findById(key)).isEmpty(); + } + @Test // DATAJPA-497 void sortByEmbeddedPkFieldInCompositePkWithEmbeddedIdInQueryDsl() { From 0a3fb228604cf02239a39d51ad42919cd65730dd Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 8 Feb 2022 14:50:00 +0100 Subject: [PATCH 144/821] Polishing. Formatting. Fix warnings. See #2414 Original pull request #2419 --- .../support/SimpleJpaRepository.java | 44 +++++++------------ .../RepositoryWithCompositeKeyTests.java | 17 +++---- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index e36b094525..3c1639f04f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -34,7 +34,6 @@ import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Order; import jakarta.persistence.criteria.ParameterExpression; import jakarta.persistence.criteria.Path; import jakarta.persistence.criteria.Predicate; @@ -212,13 +211,13 @@ public void deleteAllByIdInBatch(Iterable ids) { } if (entityInformation.hasCompositeId()) { - // XXX Hibernate just creates an empty Entity when doing the getById. - // Others might do a select right away causing a big performance penalty. - // See JavaDoc for getById. + List entities = new ArrayList<>(); - ids.forEach(id -> entities.add(getById(id))); + // generate entity (proxies) without accessing the database. + ids.forEach(id -> entities.add(getReferenceById(id))); deleteAllInBatch(entities); } else { + String queryString = String.format(DELETE_ALL_QUERY_BY_ID_STRING, entityInformation.getEntityName(), entityInformation.getIdAttribute().getName()); @@ -292,7 +291,6 @@ public Optional findById(ID id) { * Returns {@link QueryHints} with the query hints based on the current {@link CrudMethodMetadata} and potential * {@link EntityGraph} information. * - * @return */ protected QueryHints getQueryHints() { return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata); @@ -377,7 +375,7 @@ public List findAllById(Iterable ids) { if (entityInformation.hasCompositeId()) { - List results = new ArrayList(); + List results = new ArrayList<>(); for (ID id : ids) { findById(id).ifPresent(results::add); @@ -388,7 +386,7 @@ public List findAllById(Iterable ids) { Collection idCollection = Streamable.of(ids).toList(); - ByIdsSpecification specification = new ByIdsSpecification(entityInformation); + ByIdsSpecification specification = new ByIdsSpecification<>(entityInformation); TypedQuery query = getQuery(specification, Sort.unsorted()); return query.setParameter(specification.parameter, idCollection).getResultList(); @@ -403,7 +401,7 @@ public List findAll(Sort sort) { public Page findAll(Pageable pageable) { if (isUnpaged(pageable)) { - return new PageImpl(findAll()); + return new PageImpl<>(findAll()); } return findAll((Specification) null, pageable); @@ -428,7 +426,7 @@ public List findAll(@Nullable Specification spec) { public Page findAll(@Nullable Specification spec, Pageable pageable) { TypedQuery query = getQuery(spec, pageable); - return isUnpaged(pageable) ? new PageImpl(query.getResultList()) + return isUnpaged(pageable) ? new PageImpl<>(query.getResultList()) : readPage(query, getDomainClass(), pageable, spec); } @@ -442,7 +440,7 @@ public Optional findOne(Example example) { try { return Optional - .of(getQuery(new ExampleSpecification(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) + .of(getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) .getSingleResult()); } catch (NoResultException e) { return Optional.empty(); @@ -452,7 +450,7 @@ public Optional findOne(Example example) { @Override public long count(Example example) { return executeCountQuery( - getCountQuery(new ExampleSpecification(example, escapeCharacter), example.getProbeType())); + getCountQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType())); } @Override @@ -468,13 +466,13 @@ public boolean exists(Example example) { @Override public List findAll(Example example) { - return getQuery(new ExampleSpecification(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) + return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) .getResultList(); } @Override public List findAll(Example example, Sort sort) { - return getQuery(new ExampleSpecification(example, escapeCharacter), example.getProbeType(), sort) + return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), sort) .getResultList(); } @@ -548,7 +546,7 @@ public List saveAll(Iterable entities) { Assert.notNull(entities, "Entities must not be null!"); - List result = new ArrayList(); + List result = new ArrayList<>(); for (S entity : entities) { result.add(save(entity)); @@ -580,7 +578,6 @@ public void flush() { * @param query must not be {@literal null}. * @param spec can be {@literal null}. * @param pageable must not be {@literal null}. - * @return * @deprecated use {@link #readPage(TypedQuery, Class, Pageable, Specification)} instead */ @Deprecated @@ -596,7 +593,6 @@ protected Page readPage(TypedQuery query, Pageable pageable, @Nullable Spe * @param domainClass must not be {@literal null}. * @param spec can be {@literal null}. * @param pageable can be {@literal null}. - * @return */ protected Page readPage(TypedQuery query, final Class domainClass, Pageable pageable, @Nullable Specification spec) { @@ -615,7 +611,6 @@ protected Page readPage(TypedQuery query, final Class dom * * @param spec can be {@literal null}. * @param pageable must not be {@literal null}. - * @return */ protected TypedQuery getQuery(@Nullable Specification spec, Pageable pageable) { @@ -629,7 +624,6 @@ protected TypedQuery getQuery(@Nullable Specification spec, Pageable pagea * @param spec can be {@literal null}. * @param domainClass must not be {@literal null}. * @param pageable must not be {@literal null}. - * @return */ protected TypedQuery getQuery(@Nullable Specification spec, Class domainClass, Pageable pageable) { @@ -643,7 +637,6 @@ protected TypedQuery getQuery(@Nullable Specification spec, * * @param spec can be {@literal null}. * @param sort must not be {@literal null}. - * @return */ protected TypedQuery getQuery(@Nullable Specification spec, Sort sort) { return getQuery(spec, getDomainClass(), sort); @@ -655,7 +648,6 @@ protected TypedQuery getQuery(@Nullable Specification spec, Sort sort) { * @param spec can be {@literal null}. * @param domainClass must not be {@literal null}. * @param sort must not be {@literal null}. - * @return */ protected TypedQuery getQuery(@Nullable Specification spec, Class domainClass, Sort sort) { @@ -676,7 +668,6 @@ protected TypedQuery getQuery(@Nullable Specification spec, * Creates a new count query for the given {@link Specification}. * * @param spec can be {@literal null}. - * @return * @deprecated override {@link #getCountQuery(Specification, Class)} instead */ @Deprecated @@ -689,7 +680,6 @@ protected TypedQuery getCountQuery(@Nullable Specification spec) { * * @param spec can be {@literal null}. * @param domainClass must not be {@literal null}. - * @return */ protected TypedQuery getCountQuery(@Nullable Specification spec, Class domainClass) { @@ -705,7 +695,7 @@ protected TypedQuery getCountQuery(@Nullable Specification emptyList()); + query.orderBy(Collections.emptyList()); return em.createQuery(query); } @@ -716,7 +706,6 @@ protected TypedQuery getCountQuery(@Nullable Specification Root applySpecificationToCriteria(@Nullable Specification spec, Class domainClass, CriteriaQuery query) { @@ -762,7 +751,6 @@ private void applyQueryHints(Query query) { * Executes a count query and transparently sums up all values returned. * * @param query must not be {@literal null}. - * @return */ private static long executeCountQuery(TypedQuery query) { @@ -830,8 +818,8 @@ private static class ExampleSpecification implements Specification { /** * Creates new {@link ExampleSpecification}. * - * @param example - * @param escapeCharacter + * @param example the example to base the specification of. Must not be {@literal null}. + * @param escapeCharacter the escape character to use for like expressions. Must not be {@literal null}. */ ExampleSpecification(Example example, EscapeCharacter escapeCharacter) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index 150f9486c3..18f1574afe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.*; import java.util.Arrays; +import java.util.Collections; import java.util.List; import jakarta.persistence.EntityManager; @@ -113,7 +114,7 @@ void shouldSupportSavingEntitiesWithCompositeKeyClassesWithEmbeddedIdsAndDerived } @Test // DATAJPA-472, DATAJPA-912 - void shouldSupportFindAllWithPageableAndEntityWithIdClass() throws Exception { + void shouldSupportFindAllWithPageableAndEntityWithIdClass() { IdClassExampleDepartment dep = new IdClassExampleDepartment(); dep.setName("TestDepartment"); @@ -121,7 +122,7 @@ void shouldSupportFindAllWithPageableAndEntityWithIdClass() throws Exception { IdClassExampleEmployee emp = new IdClassExampleEmployee(); emp.setDepartment(dep); - emp = employeeRepositoryWithIdClass.save(emp); + employeeRepositoryWithIdClass.save(emp); Page page = employeeRepositoryWithIdClass.findAll(PageRequest.of(0, 1)); @@ -130,7 +131,7 @@ void shouldSupportFindAllWithPageableAndEntityWithIdClass() throws Exception { } @Test // DATAJPA-2414 - void shouldSupportDeleteAllByIdInBatchWithIdClass() throws Exception { + void shouldSupportDeleteAllByIdInBatchWithIdClass() { IdClassExampleDepartment dep = new IdClassExampleDepartment(); dep.setName("TestDepartment"); @@ -143,7 +144,7 @@ void shouldSupportDeleteAllByIdInBatchWithIdClass() throws Exception { IdClassExampleEmployeePK key = new IdClassExampleEmployeePK(emp.getEmpId(), dep.getDepartmentId()); assertThat(employeeRepositoryWithIdClass.findById(key)).isNotEmpty(); - employeeRepositoryWithIdClass.deleteAllByIdInBatch(Arrays.asList(key)); + employeeRepositoryWithIdClass.deleteAllByIdInBatch(Collections.singletonList(key)); em.flush(); em.clear(); @@ -170,7 +171,7 @@ void sortByEmbeddedPkFieldInCompositePkWithEmbeddedIdInQueryDsl() { EmbeddedIdExampleEmployee emp2 = new EmbeddedIdExampleEmployee(); emp2.setEmployeePk(new EmbeddedIdExampleEmployeePK(2L, null)); emp2.setDepartment(dep1); - emp2 = employeeRepositoryWithEmbeddedId.save(emp2); + employeeRepositoryWithEmbeddedId.save(emp2); EmbeddedIdExampleEmployee emp3 = new EmbeddedIdExampleEmployee(); emp3.setEmployeePk(new EmbeddedIdExampleEmployeePK(1L, null)); @@ -206,7 +207,7 @@ void sortByEmbeddedPkFieldInCompositePkWithIdClassInQueryDsl() { IdClassExampleEmployee emp2 = new IdClassExampleEmployee(); emp2.setEmpId(2L); emp2.setDepartment(dep1); - emp2 = employeeRepositoryWithIdClass.save(emp2); + employeeRepositoryWithIdClass.save(emp2); IdClassExampleEmployee emp3 = new IdClassExampleEmployee(); emp3.setEmpId(1L); @@ -276,7 +277,7 @@ void shouldAllowFindAllWithIdsForEntitiesWithCompoundIdClassKeys() { IdClassExampleEmployee emp1 = new IdClassExampleEmployee(); emp1.setEmpId(3L); emp1.setDepartment(dep2); - emp1 = employeeRepositoryWithIdClass.save(emp1); + employeeRepositoryWithIdClass.save(emp1); IdClassExampleDepartment dep1 = new IdClassExampleDepartment(); dep1.setDepartmentId(1L); @@ -285,7 +286,7 @@ void shouldAllowFindAllWithIdsForEntitiesWithCompoundIdClassKeys() { IdClassExampleEmployee emp2 = new IdClassExampleEmployee(); emp2.setEmpId(2L); emp2.setDepartment(dep1); - emp2 = employeeRepositoryWithIdClass.save(emp2); + employeeRepositoryWithIdClass.save(emp2); IdClassExampleEmployeePK emp1PK = new IdClassExampleEmployeePK(); emp1PK.setDepartment(2L); From 0ccf4a148d61e2bced65f3de38bca325d4023c80 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Tue, 8 Feb 2022 17:14:23 +0100 Subject: [PATCH 145/821] Remove of useless `throws ...` in jUnit tests. A lot of jUnit tests had in the method header a `throws` that is useless. For example `throws Exception` on a method where no exception is thrown. Removing this noice will make the tests more redable. Original pull request #2433 --- ...itingBeanFactoryPostProcessorUnitTests.java | 4 ++-- .../support/AuditingNamespaceUnitTests.java | 4 ++-- ...clipseLinkNamespaceUserRepositoryTests.java | 8 ++++---- .../EclipseLinkUserRepositoryFinderTests.java | 4 ++-- .../jpa/repository/ORMInfrastructureTests.java | 5 ++--- ...penJpaParentRepositoryIntegrationTests.java | 4 ++-- .../OpenJpaUserRepositoryFinderTests.java | 4 ++-- .../ParentRepositoryIntegrationTests.java | 4 ++-- .../RepositoryWithIdClassKeyTests.java | 4 ++-- .../RoleRepositoryIntegrationTests.java | 6 +++--- .../repository/UserRepositoryFinderTests.java | 6 +++--- ...aRepositoryConfigDefinitionParserTests.java | 4 ++-- .../JpaRepositoryConfigExtensionUnitTests.java | 4 ++-- .../repository/query/Jpa21UtilsUnitTests.java | 4 ++-- .../query/JpaQueryMethodUnitTests.java | 8 ++++---- .../query/ParameterBinderUnitTests.java | 9 ++++----- .../repository/query/QueryUtilsUnitTests.java | 18 +++++++++--------- .../DefaultJpaContextIntegrationTests.java | 2 +- .../support/EntityManagerFactoryRefTests.java | 6 +++--- .../JpaEntityInformationSupportUnitTests.java | 4 ++-- .../support/JpaRepositoryFactoryUnitTests.java | 13 ++++++------- .../repository/support/JpaRepositoryTests.java | 9 +++++---- .../support/OpenJpaJpaRepositoryTests.java | 4 ++-- .../QuerydslJpaPredicateExecutorUnitTests.java | 14 +++++++------- .../support/QuerydslJpaRepositoryTests.java | 8 ++++---- .../QuerydslRepositorySupportTests.java | 6 +++--- .../support/TransactionalRepositoryTests.java | 12 ++++++------ 27 files changed, 88 insertions(+), 90 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java index 8923c240a8..a3c4c48498 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,7 +59,7 @@ DefaultListableBeanFactory getBeanFactory() { } @Test - void beanConfigurerAspectShouldBeConfiguredAfterPostProcessing() throws Exception { + void beanConfigurerAspectShouldBeConfiguredAfterPostProcessing() { processor.postProcessBeanFactory(beanFactory); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java index b013c0d354..1d870a5b8f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ String getConfigFile() { } @Test - void registersBeanDefinitions() throws Exception { + void registersBeanDefinitions() { BeanDefinition definition = beanFactory.getBeanDefinition(AuditingEntityListener.class.getName()); PropertyValue propertyValue = definition.getPropertyValues().getPropertyValue("auditingHandler"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java index 98dd3b6c5f..2dbc99d010 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -106,7 +106,7 @@ void bindsNativeQueryResultsToProjectionByName() {} * Ignores the test. Reconsider once https://bugs.eclipse.org/bugs/show_bug.cgi?id=533240 is fixed. */ @Override - void findByEmptyArrayOfIntegers() throws Exception {} + void findByEmptyArrayOfIntegers() {} /** * Ignores the test. Reconsider once https://bugs.eclipse.org/bugs/show_bug.cgi?id=533240 is fixed. @@ -119,13 +119,13 @@ void findByAgeWithEmptyArrayOfIntegersOrFirstName() { * Ignores the test. Reconsider once https://bugs.eclipse.org/bugs/show_bug.cgi?id=533240 is fixed. */ @Override - void findByEmptyCollectionOfIntegers() throws Exception {} + void findByEmptyCollectionOfIntegers() {} /** * Ignores the test. Reconsider once https://bugs.eclipse.org/bugs/show_bug.cgi?id=533240 is fixed. */ @Override - void findByEmptyCollectionOfStrings() throws Exception {} + void findByEmptyCollectionOfStrings() {} /** * Ignores the test for EclipseLink. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java index c36e2a310c..d20bede602 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ class EclipseLinkUserRepositoryFinderTests extends UserRepositoryFinderTests { @Disabled @Override - void executesNotInQueryCorrectly() throws Exception {} + void executesNotInQueryCorrectly() {} @Disabled @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java index 0ee4a51551..0ab0674303 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,10 +40,9 @@ public class ORMInfrastructureTests { /** * Tests, that the context got initialized and injected correctly. * - * @throws Exception */ @Test - void contextInitialized() throws Exception { + void contextInitialized() { assertThat(context).isNotNull(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java index 6df1365683..6183bfd0ac 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,5 @@ class OpenJpaParentRepositoryIntegrationTests extends ParentRepositoryIntegratio @Override @Disabled - void testWithJoin() throws Exception {} + void testWithJoin() {} } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java index da0075a0ab..15ae9bb62c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,5 +29,5 @@ class OpenJpaUserRepositoryFinderTests extends UserRepositoryFinderTests { @Disabled @Override - void findsByLastnameIgnoringCaseLike() throws Exception {} + void findsByLastnameIgnoringCaseLike() {} } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java index 56884c4530..bcafd2e896 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -84,7 +84,7 @@ public Predicate toPredicate(Root root, CriteriaQuery query, Criteria } @Test // DATAJPA-287 - void testWithJoin() throws Exception { + void testWithJoin() { Page page = repository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java index f6b56ce671..9c4cb8d593 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,7 +61,7 @@ public class RepositoryWithIdClassKeyTests { * Specification 2.4.1.3 Derived Identities Example 2 */ @Test // DATAJPA-413 - void shouldSaveAndLoadEntitiesWithDerivedIdentities() throws Exception { + void shouldSaveAndLoadEntitiesWithDerivedIdentities() { Site site = siteRepository.save(new Site()); Item item = itemRepository.save(new Item(123, 456)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java index bc7a6dbfca..caf33c3a1c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ public class RoleRepositoryIntegrationTests { @Autowired RoleRepository repository; @Test - void createsRole() throws Exception { + void createsRole() { Role reference = new Role("ADMIN"); Role result = repository.save(reference); @@ -53,7 +53,7 @@ void createsRole() throws Exception { } @Test - void updatesRole() throws Exception { + void updatesRole() { Role reference = new Role("ADMIN"); Role result = repository.save(reference); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 8fe2714a53..ff2c484004 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -144,7 +144,7 @@ void executesInKeywordForPageCorrectly() { } @Test - void executesNotInQueryCorrectly() throws Exception { + void executesNotInQueryCorrectly() { List result = userRepository.findByFirstnameNotIn(Arrays.asList("Dave", "Carter")); @@ -160,7 +160,7 @@ void findsByLastnameIgnoringCase() { } @Test // DATAJPA-92 - void findsByLastnameIgnoringCaseLike() throws Exception { + void findsByLastnameIgnoringCaseLike() { List result = userRepository.findByLastnameIgnoringCaseLike("BeAUfo%"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java index e73a148700..e5c45f9d35 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ class JpaRepositoryConfigDefinitionParserTests { @Test - void getsTransactionManagerSet() throws Exception { + void getsTransactionManagerSet() { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java index 11d65209b8..d9d62f8da4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -92,7 +92,7 @@ void doesNotRegisterProcessorIfAutoRegistered() { } @Test // DATAJPA-525 - void guardsAgainstNullJavaTypesReturnedFromJpaMetamodel() throws Exception { + void guardsAgainstNullJavaTypesReturnedFromJpaMetamodel() { ApplicationContext context = mock(ApplicationContext.class); EntityManagerFactory emf = mock(EntityManagerFactory.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java index 03783c5631..19ec8cdad5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ class Jpa21UtilsUnitTests { @Test // DATAJPA-696 - void shouldBuildCorrectSubgraphForJpaEntityGraph() throws Exception { + void shouldBuildCorrectSubgraphForJpaEntityGraph() { EntityGraph entityGraph = mock(EntityGraph.class); Subgraph subgraph = mock(Subgraph.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 7fc18fb564..dd84a156ac 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -275,7 +275,7 @@ void returnsDefaultCountQueryNameBasedOnConfiguredNamedQueryName() throws Except } @Test // DATAJPA-185 - void rejectsInvalidNamedParameter() throws Exception { + void rejectsInvalidNamedParameter() { assertThatThrownBy(() -> getQueryMethod(InvalidRepository.class, "findByAnnotatedQuery", String.class)) .isInstanceOf(IllegalStateException.class) @@ -344,8 +344,8 @@ void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethodFindOne doReturn(User.class).when(metadata).getDomainType(); doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any()); - JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne", Integer.class), metadata, - factory, extractor); + JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne", Integer.class), + metadata, factory, extractor); assertThat(method.getEntityGraph()).isNotNull(); assertThat(method.getEntityGraph().getName()).isEqualTo("User.detail"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java index 2c522cf8da..acaf70cd0b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -123,7 +123,7 @@ void bindWorksWithNullForPageable() throws Exception { } @Test - void usesIndexedParametersIfNoParamAnnotationPresent() throws Exception { + void usesIndexedParametersIfNoParamAnnotationPresent() { Object[] values = { "foo" }; bind(useIndexedParameters, values); @@ -131,7 +131,7 @@ void usesIndexedParametersIfNoParamAnnotationPresent() throws Exception { } @Test - void usesParameterNameIfAnnotated() throws Exception { + void usesParameterNameIfAnnotated() { when(query.setParameter(eq("username"), any())).thenReturn(query); @@ -185,8 +185,7 @@ void shouldSetTemporalQueryParameterToTimestamp() throws Exception { } @Test // DATAJPA-107 - void shouldThrowIllegalArgumentExceptionIfIsAnnotatedWithTemporalParamAndParameterTypeIsNotDate() - throws Exception { + void shouldThrowIllegalArgumentExceptionIfIsAnnotatedWithTemporalParamAndParameterTypeIsNotDate() throws Exception { Method method = SampleRepository.class.getMethod("invalidWithTemporalTypeParameter", String.class); assertThatIllegalArgumentException().isThrownBy(() -> new JpaParameters(method)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 00794830ae..b2640fea25 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ class QueryUtilsUnitTests { private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?"; @Test - void createsCountQueryCorrectly() throws Exception { + void createsCountQueryCorrectly() { assertCountQuery(QUERY, COUNT_QUERY); } @@ -64,47 +64,47 @@ void createsCountQueriesCorrectlyForCapitalLetterJPQL() { } @Test - void createsCountQueryForDistinctQueries() throws Exception { + void createsCountQueryForDistinctQueries() { assertCountQuery("select distinct u from User u where u.foo = ?", "select count(distinct u) from User u where u.foo = ?"); } @Test - void createsCountQueryForConstructorQueries() throws Exception { + void createsCountQueryForConstructorQueries() { assertCountQuery("select distinct new User(u.name) from User u where u.foo = ?", "select count(distinct u) from User u where u.foo = ?"); } @Test - void createsCountQueryForJoins() throws Exception { + void createsCountQueryForJoins() { assertCountQuery("select distinct new User(u.name) from User u left outer join u.roles r WHERE r = ?", "select count(distinct u) from User u left outer join u.roles r WHERE r = ?"); } @Test - void createsCountQueryForQueriesWithSubSelects() throws Exception { + void createsCountQueryForQueriesWithSubSelects() { assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role)", "select count(u) from User u left outer join u.roles r where r in (select r from Role)"); } @Test - void createsCountQueryForAliasesCorrectly() throws Exception { + void createsCountQueryForAliasesCorrectly() { assertCountQuery("select u from User as u", "select count(u) from User as u"); } @Test - void allowsShortJpaSyntax() throws Exception { + void allowsShortJpaSyntax() { assertCountQuery(SIMPLE_QUERY, COUNT_QUERY); } @Test - void detectsAliasCorrectly() throws Exception { + void detectsAliasCorrectly() { assertThat(detectAlias(QUERY)).isEqualTo("u"); assertThat(detectAlias(SIMPLE_QUERY)).isEqualTo("u"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java index 31daa73660..d93dea1475 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java index bcdfdee43a..79fcc98a88 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,13 +43,13 @@ public class EntityManagerFactoryRefTests { @Test @Transactional - void useUserRepository() throws Exception { + void useUserRepository() { userRepository.saveAndFlush(new User("firstname", "lastname", "foo@bar.de")); } @Test @Transactional("transactionManager-2") - void useAuditableUserRepository() throws Exception { + void useAuditableUserRepository() { auditableUserRepository.saveAndFlush(new AuditableUser()); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java index 37ae21a4e1..320af7d181 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ public class JpaEntityInformationSupportUnitTests { @Mock Metamodel metaModel; @Test - void usesSimpleClassNameIfNoEntityNameGiven() throws Exception { + void usesSimpleClassNameIfNoEntityNameGiven() { JpaEntityInformation information = new DummyJpaEntityInformation<>(User.class); assertThat(information.getEntityName()).isEqualTo("User"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index 11f466f56a..f922babaf9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,7 +91,7 @@ public JpaEntityInformation getEntityInformation(Class domainC * @throws Exception */ @Test - void setsUpBasicInstanceCorrectly() throws Exception { + void setsUpBasicInstanceCorrectly() { assertThat(factory.getRepository(SimpleSampleRepository.class)).isNotNull(); } @@ -111,10 +111,9 @@ void allowsCallingOfObjectMethods() { * implementation could be found. Furthremore the exception has to contain the name of the predicateExecutor interface * as for a large predicateExecutor configuration it's hard to find out where this error occured. * - * @throws Exception */ @Test - void capturesMissingCustomImplementationAndProvidesInterfacename() throws Exception { + void capturesMissingCustomImplementationAndProvidesInterfacename() { try { factory.getRepository(SampleRepository.class); @@ -194,14 +193,14 @@ private interface QueryDslSampleRepository extends SimpleSampleRepository, Query } -static class CustomJpaRepository extends SimpleJpaRepository { + static class CustomJpaRepository extends SimpleJpaRepository { - CustomJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager) { + CustomJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); } } - /** + /** * Implementation of the custom predicateExecutor interface. * * @author Oliver Gierke diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java index 159c1fdc4a..ea5e179173 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,7 +62,7 @@ void setUp() { } @Test - void testCrudOperationsForCompoundKeyEntity() throws Exception { + void testCrudOperationsForCompoundKeyEntity() { SampleEntity entity = new SampleEntity("foo", "bar"); repository.saveAndFlush(entity); @@ -90,7 +90,7 @@ void executesCrudOperationsForEntityWithIdClass() { } @Test // DATAJPA-266 - void testExistsForDomainObjectsWithCompositeKeys() throws Exception { + void testExistsForDomainObjectsWithCompositeKeys() { PersistableWithIdClass s1 = idClassRepository.save(new PersistableWithIdClass(1L, 1L)); PersistableWithIdClass s2 = idClassRepository.save(new PersistableWithIdClass(2L, 2L)); @@ -123,7 +123,8 @@ void deleteAllByIdInBatch() { repository.saveAll(Arrays.asList(one, two, three)); repository.flush(); - repository.deleteAllByIdInBatch(Arrays.asList(new SampleEntityPK("one", "eins"),new SampleEntityPK("three", "drei"))); + repository + .deleteAllByIdInBatch(Arrays.asList(new SampleEntityPK("one", "eins"), new SampleEntityPK("three", "drei"))); assertThat(repository.findAll()).containsExactly(two); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java index 828431f815..d76fa2555f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,6 @@ class OpenJpaJpaRepositoryTests extends JpaRepositoryTests { @Override @Disabled - void testCrudOperationsForCompoundKeyEntity() throws Exception { + void testCrudOperationsForCompoundKeyEntity() { } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index 52d3608a43..59b5d58870 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,7 +97,7 @@ void setUp() { } @Test - void executesPredicatesCorrectly() throws Exception { + void executesPredicatesCorrectly() { BooleanExpression isCalledDave = user.firstname.eq("Dave"); BooleanExpression isBeauford = user.lastname.eq("Beauford"); @@ -108,7 +108,7 @@ void executesPredicatesCorrectly() throws Exception { } @Test - void executesStringBasedPredicatesCorrectly() throws Exception { + void executesStringBasedPredicatesCorrectly() { PathBuilder builder = new PathBuilderFactory().create(User.class); @@ -268,7 +268,7 @@ void shouldSupportSortByOperatorWithDateExpressions() { } @Test // DATAJPA-665 - void shouldSupportExistsWithPredicate() throws Exception { + void shouldSupportExistsWithPredicate() { assertThat(predicateExecutor.exists(user.firstname.eq("Dave"))).isEqualTo(true); assertThat(predicateExecutor.exists(user.firstname.eq("Unknown"))).isEqualTo(false); @@ -511,9 +511,9 @@ void findByFluentPredicateWithComplexPropertyPathsDoesntLoadsRequestedPaths() { em.clear(); assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder( // - dave.getFirstname(), // - oliver.getFirstname() // - ); + dave.getFirstname(), // + oliver.getFirstname() // + ); assertThat(users).allMatch(u -> u.getRoles().isEmpty()); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java index e438d4e2b0..97fcd9cd4f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,7 +91,7 @@ void setUp() { } @Test - void executesPredicatesCorrectly() throws Exception { + void executesPredicatesCorrectly() { BooleanExpression isCalledDave = user.firstname.eq("Dave"); BooleanExpression isBeauford = user.lastname.eq("Beauford"); @@ -102,7 +102,7 @@ void executesPredicatesCorrectly() throws Exception { } @Test - void executesStringBasedPredicatesCorrectly() throws Exception { + void executesStringBasedPredicatesCorrectly() { PathBuilder builder = new PathBuilderFactory().create(User.class); @@ -273,7 +273,7 @@ void shouldSupportSortByOperatorWithDateExpressions() { } @Test // DATAJPA-665 - void shouldSupportExistsWithPredicate() throws Exception { + void shouldSupportExistsWithPredicate() { assertThat(repository.exists(user.firstname.eq("Dave"))).isEqualTo(true); assertThat(repository.exists(user.firstname.eq("Unknown"))).isEqualTo(false); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java index 78ce578977..afcdb8e428 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,7 +67,7 @@ void setup() { } @Test - void readsUsersCorrectly() throws Exception { + void readsUsersCorrectly() { List result = repository.findUsersByLastname("Matthews"); assertThat(result.size()).isEqualTo(1); @@ -79,7 +79,7 @@ void readsUsersCorrectly() throws Exception { } @Test - void updatesUsersCorrectly() throws Exception { + void updatesUsersCorrectly() { long updates = repository.updateLastnamesTo("Foo"); assertThat(updates).isEqualTo(2L); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java index 8918a0abc1..9739d2606e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,35 +58,35 @@ void tearDown() { } @Test - void simpleManipulatingOperation() throws Exception { + void simpleManipulatingOperation() { repository.saveAndFlush(new User("foo", "bar", "foo@bar.de")); assertThat(transactionManager.getTransactionRequests()).isEqualTo(1); } @Test - void unannotatedFinder() throws Exception { + void unannotatedFinder() { repository.findByEmailAddress("foo@bar.de"); assertThat(transactionManager.getTransactionRequests()).isEqualTo(0); } @Test - void invokeTransactionalFinder() throws Exception { + void invokeTransactionalFinder() { repository.findByAnnotatedQuery("foo@bar.de"); assertThat(transactionManager.getTransactionRequests()).isEqualTo(1); } @Test - void invokeRedeclaredMethod() throws Exception { + void invokeRedeclaredMethod() { repository.findById(1); assertThat(transactionManager.getDefinition().isReadOnly()).isFalse(); } @Test // DATACMNS-649 - void invokeRedeclaredDeleteMethodWithoutTransactionDeclaration() throws Exception { + void invokeRedeclaredDeleteMethodWithoutTransactionDeclaration() { User user = repository.saveAndFlush(new User("foo", "bar", "foo@bar.de")); repository.deleteById(user.getId()); From 02937e2b058f50a9349f5b0214e4f2c3e78c6c29 Mon Sep 17 00:00:00 2001 From: jonyschak Date: Sun, 13 Feb 2022 01:31:57 -0600 Subject: [PATCH 146/821] Correcting sort object reference in FetchableFluentQueryByExample.sortBy and FetchableFluentQueryByPredicate.sortBy Closes #2438 Original pull request #2439 --- .../FetchableFluentQueryByExample.java | 7 ++-- .../FetchableFluentQueryByPredicate.java | 7 ++-- ...etchableFluentQueryByExampleUnitTests.java | 42 +++++++++++++++++++ ...chableFluentQueryByPredicateUnitTests.java | 41 ++++++++++++++++++ 4 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index 96a99684b2..f2f0d7c55f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +45,7 @@ * @author Greg Turnquist * @author Mark Paluch * @author Jens Schauder + * @author J.R. Onyschak * @since 2.6 */ class FetchableFluentQueryByExample extends FluentQuerySupport implements FetchableFluentQuery { @@ -82,8 +83,8 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null!"); - return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort.and(sort), properties, finder, - countOperation, existsOperation, entityManager, escapeCharacter); + return new FetchableFluentQueryByExample<>(example, entityType, resultType, this.sort.and(sort), properties, + finder, countOperation, existsOperation, entityManager, escapeCharacter); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index 009d891594..20fe4b7172 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ * @author Greg Turnquist * @author Mark Paluch * @author Jens Schauder + * @author J.R. Onyschak * @since 2.6 */ class FetchableFluentQueryByPredicate extends FluentQuerySupport implements FetchableFluentQuery { @@ -85,8 +86,8 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null!"); - return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort.and(sort), properties, finder, - pagedFinder, countOperation, existsOperation, entityManager); + return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, this.sort.and(sort), properties, + finder, pagedFinder, countOperation, existsOperation, entityManager); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java new file mode 100644 index 0000000000..0faf4593a6 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.data.jpa.repository.support.FetchableFluentQueryByExample; + +/** + * Unit tests for {@link FetchableFluentQueryByExample}. + * + * @author J.R. Onyschak + */ +class FetchableFluentQueryByExampleUnitTests { + + @Test // GH-2438 + @SuppressWarnings({ "rawtypes", "unchecked" }) + void multipleSortBy() { + Sort s1 = Sort.by(Order.by("s1")); + Sort s2 = Sort.by(Order.by("s2")); + FetchableFluentQueryByExample f = new FetchableFluentQueryByExample(Example.of(""), null, null, null, null, null); + f = (FetchableFluentQueryByExample) f.sortBy(s1).sortBy(s2); + assertThat(f.sort).isEqualTo(s1.and(s2)); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java new file mode 100644 index 0000000000..7f59794c13 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.data.jpa.repository.support.FetchableFluentQueryByPredicate; + +/** + * Unit tests for {@link FetchableFluentQueryByPredicate}. + * + * @author J.R. Onyschak + */ +class FetchableFluentQueryByPredicateUnitTests { + + @Test // GH-2438 + @SuppressWarnings({ "rawtypes", "unchecked" }) + void multipleSortBy() { + Sort s1 = Sort.by(Order.by("s1")); + Sort s2 = Sort.by(Order.by("s2")); + FetchableFluentQueryByPredicate f = new FetchableFluentQueryByPredicate(null, null, null, null, null, null, null); + f = (FetchableFluentQueryByPredicate) f.sortBy(s1).sortBy(s2); + assertThat(f.sort).isEqualTo(s1.and(s2)); + } +} From 5a4ca13b8ca5d62b20743077f7d9198d6c03f76a Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 15 Feb 2022 10:55:20 +0100 Subject: [PATCH 147/821] Polishing. See #2438 Original pull request #2439 --- .../support/FetchableFluentQueryByExampleUnitTests.java | 1 + .../support/FetchableFluentQueryByPredicateUnitTests.java | 1 + 2 files changed, 2 insertions(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java index 0faf4593a6..c29581e3c4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java @@ -33,6 +33,7 @@ class FetchableFluentQueryByExampleUnitTests { @Test // GH-2438 @SuppressWarnings({ "rawtypes", "unchecked" }) void multipleSortBy() { + Sort s1 = Sort.by(Order.by("s1")); Sort s2 = Sort.by(Order.by("s2")); FetchableFluentQueryByExample f = new FetchableFluentQueryByExample(Example.of(""), null, null, null, null, null); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java index 7f59794c13..70fb3fd9a3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java @@ -32,6 +32,7 @@ class FetchableFluentQueryByPredicateUnitTests { @Test // GH-2438 @SuppressWarnings({ "rawtypes", "unchecked" }) void multipleSortBy() { + Sort s1 = Sort.by(Order.by("s1")); Sort s2 = Sort.by(Order.by("s2")); FetchableFluentQueryByPredicate f = new FetchableFluentQueryByPredicate(null, null, null, null, null, null, null); From 57b9b2198b2c3be38193fb2d1d07e9f36a2315f1 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 17 Feb 2022 13:44:01 +0100 Subject: [PATCH 148/821] Update copyright year to 2022. See: #2443 --- .../data/envers/repository/config/EnableEnversRepositories.java | 2 +- .../repository/support/DefaultRevisionEntityInformation.java | 2 +- .../data/envers/repository/support/DefaultRevisionMetadata.java | 2 +- .../envers/repository/support/EnversRevisionRepository.java | 2 +- .../repository/support/EnversRevisionRepositoryFactoryBean.java | 2 +- .../envers/repository/support/EnversRevisionRepositoryImpl.java | 2 +- .../repository/support/ReflectionRevisionEntityInformation.java | 2 +- .../src/test/java/org/springframework/data/envers/Config.java | 2 +- .../repository/support/DefaultRevisionMetadataUnitTests.java | 2 +- .../support/EnversRevisionRepositoryImplUnitTests.java | 2 +- .../repository/support/QueryDslRepositoryIntegrationTests.java | 2 +- .../envers/repository/support/RepositoryIntegrationTests.java | 2 +- .../org/springframework/data/envers/sample/AbstractEntity.java | 2 +- .../java/org/springframework/data/envers/sample/Country.java | 2 +- .../data/envers/sample/CountryQueryDslRepository.java | 2 +- .../springframework/data/envers/sample/CountryRepository.java | 2 +- .../java/org/springframework/data/envers/sample/License.java | 2 +- .../springframework/data/envers/sample/LicenseRepository.java | 2 +- .../java/org/springframework/data/envers/sample/QCountry.java | 2 +- .../data/jpa/convert/threeten/Jsr310JpaConverters.java | 2 +- .../org/springframework/data/jpa/domain/AbstractAuditable.java | 2 +- .../springframework/data/jpa/domain/AbstractPersistable.java | 2 +- .../main/java/org/springframework/data/jpa/domain/JpaSort.java | 2 +- .../java/org/springframework/data/jpa/domain/Specification.java | 2 +- .../data/jpa/domain/SpecificationComposition.java | 2 +- .../jpa/domain/support/AuditingBeanFactoryPostProcessor.java | 2 +- .../data/jpa/domain/support/AuditingEntityListener.java | 2 +- .../data/jpa/mapping/JpaMetamodelMappingContext.java | 2 +- .../springframework/data/jpa/mapping/JpaPersistentEntity.java | 2 +- .../data/jpa/mapping/JpaPersistentEntityImpl.java | 2 +- .../springframework/data/jpa/mapping/JpaPersistentProperty.java | 2 +- .../data/jpa/mapping/JpaPersistentPropertyImpl.java | 2 +- .../data/jpa/projection/CollectionAwareProjectionFactory.java | 2 +- .../org/springframework/data/jpa/provider/HibernateUtils.java | 2 +- .../org/springframework/data/jpa/provider/JpaClassUtils.java | 2 +- .../org/springframework/data/jpa/provider/ProxyIdAccessor.java | 2 +- .../org/springframework/data/jpa/provider/QueryExtractor.java | 2 +- .../org/springframework/data/jpa/repository/EntityGraph.java | 2 +- .../org/springframework/data/jpa/repository/JpaContext.java | 2 +- .../org/springframework/data/jpa/repository/JpaRepository.java | 2 +- .../data/jpa/repository/JpaSpecificationExecutor.java | 2 +- .../main/java/org/springframework/data/jpa/repository/Lock.java | 2 +- .../java/org/springframework/data/jpa/repository/Modifying.java | 2 +- .../java/org/springframework/data/jpa/repository/Query.java | 2 +- .../org/springframework/data/jpa/repository/QueryHints.java | 2 +- .../java/org/springframework/data/jpa/repository/Temporal.java | 2 +- .../data/jpa/repository/cdi/JpaRepositoryBean.java | 2 +- .../data/jpa/repository/cdi/JpaRepositoryExtension.java | 2 +- .../jpa/repository/config/AuditingBeanDefinitionParser.java | 2 +- .../data/jpa/repository/config/EnableJpaAuditing.java | 2 +- .../data/jpa/repository/config/EnableJpaRepositories.java | 2 +- .../data/jpa/repository/config/InspectionClassLoader.java | 2 +- .../data/jpa/repository/config/JpaAuditingRegistrar.java | 2 +- .../config/JpaMetamodelMappingContextFactoryBean.java | 2 +- .../data/jpa/repository/config/JpaRepositoriesRegistrar.java | 2 +- .../jpa/repository/config/JpaRepositoryConfigExtension.java | 2 +- .../jpa/repository/config/JpaRepositoryNameSpaceHandler.java | 2 +- .../data/jpa/repository/query/AbstractJpaQuery.java | 2 +- .../data/jpa/repository/query/AbstractStringBasedJpaQuery.java | 2 +- .../data/jpa/repository/query/DeclaredQuery.java | 2 +- .../data/jpa/repository/query/DefaultJpaEntityMetadata.java | 2 +- .../data/jpa/repository/query/DefaultJpaQueryMethodFactory.java | 2 +- .../data/jpa/repository/query/EmptyDeclaredQuery.java | 2 +- .../data/jpa/repository/query/EscapeCharacter.java | 2 +- .../data/jpa/repository/query/ExpressionBasedStringQuery.java | 2 +- .../jpa/repository/query/InvalidJpaQueryMethodException.java | 2 +- .../springframework/data/jpa/repository/query/Jpa21Utils.java | 2 +- .../data/jpa/repository/query/JpaCountQueryCreator.java | 2 +- .../data/jpa/repository/query/JpaEntityGraph.java | 2 +- .../data/jpa/repository/query/JpaEntityMetadata.java | 2 +- .../data/jpa/repository/query/JpaParameters.java | 2 +- .../jpa/repository/query/JpaParametersParameterAccessor.java | 2 +- .../data/jpa/repository/query/JpaQueryCreator.java | 2 +- .../data/jpa/repository/query/JpaQueryExecution.java | 2 +- .../data/jpa/repository/query/JpaQueryFactory.java | 2 +- .../data/jpa/repository/query/JpaQueryLookupStrategy.java | 2 +- .../data/jpa/repository/query/JpaQueryMethod.java | 2 +- .../data/jpa/repository/query/JpaQueryMethodFactory.java | 2 +- .../data/jpa/repository/query/JpaResultConverters.java | 2 +- .../springframework/data/jpa/repository/query/NamedQuery.java | 2 +- .../data/jpa/repository/query/NativeJpaQuery.java | 2 +- .../data/jpa/repository/query/ParameterBinder.java | 2 +- .../data/jpa/repository/query/ParameterBinderFactory.java | 2 +- .../data/jpa/repository/query/ParameterMetadataProvider.java | 2 +- .../data/jpa/repository/query/PartTreeJpaQuery.java | 2 +- .../springframework/data/jpa/repository/query/Procedure.java | 2 +- .../data/jpa/repository/query/ProcedureParameter.java | 2 +- .../data/jpa/repository/query/QueryParameterSetter.java | 2 +- .../data/jpa/repository/query/QueryParameterSetterFactory.java | 2 +- .../springframework/data/jpa/repository/query/QueryUtils.java | 2 +- .../data/jpa/repository/query/SimpleJpaQuery.java | 2 +- .../jpa/repository/query/StoredProcedureAttributeSource.java | 2 +- .../data/jpa/repository/query/StoredProcedureAttributes.java | 2 +- .../data/jpa/repository/query/StoredProcedureJpaQuery.java | 2 +- .../data/jpa/repository/support/CrudMethodMetadata.java | 2 +- .../jpa/repository/support/CrudMethodMetadataPostProcessor.java | 2 +- .../data/jpa/repository/support/DefaultJpaContext.java | 2 +- .../data/jpa/repository/support/DefaultQueryHints.java | 2 +- .../data/jpa/repository/support/EntityGraphFactory.java | 2 +- .../EntityManagerBeanDefinitionRegistrarPostProcessor.java | 2 +- .../data/jpa/repository/support/FluentQuerySupport.java | 2 +- .../data/jpa/repository/support/JpaEntityInformation.java | 2 +- .../jpa/repository/support/JpaEntityInformationSupport.java | 2 +- .../jpa/repository/support/JpaEvaluationContextExtension.java | 2 +- .../jpa/repository/support/JpaMetamodelEntityInformation.java | 2 +- .../jpa/repository/support/JpaPersistableEntityInformation.java | 2 +- .../data/jpa/repository/support/JpaRepositoryFactory.java | 2 +- .../data/jpa/repository/support/JpaRepositoryFactoryBean.java | 2 +- .../jpa/repository/support/JpaRepositoryImplementation.java | 2 +- .../data/jpa/repository/support/MutableQueryHints.java | 2 +- .../data/jpa/repository/support/QueryHintValue.java | 2 +- .../springframework/data/jpa/repository/support/QueryHints.java | 2 +- .../springframework/data/jpa/repository/support/Querydsl.java | 2 +- .../jpa/repository/support/QuerydslJpaPredicateExecutor.java | 2 +- .../data/jpa/repository/support/QuerydslJpaRepository.java | 2 +- .../data/jpa/repository/support/QuerydslRepositorySupport.java | 2 +- .../data/jpa/repository/support/SimpleJpaRepository.java | 2 +- .../data/jpa/support/MergingPersistenceUnitManager.java | 2 +- .../org/springframework/data/jpa/util/BeanDefinitionUtils.java | 2 +- .../springframework/data/jpa/util/HibernateProxyDetector.java | 2 +- .../java/org/springframework/data/jpa/util/JpaMetamodel.java | 2 +- .../springframework/data/jpa/util/JpaMetamodelCacheCleanup.java | 2 +- .../jpa/convert/QueryByExamplePredicateBuilderUnitTests.java | 2 +- .../data/jpa/convert/threeten/DateTimeSample.java | 2 +- .../convert/threeten/Jsr310JpaConvertersIntegrationTests.java | 2 +- .../data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java | 2 +- .../java/org/springframework/data/jpa/domain/JpaSortTests.java | 2 +- .../springframework/data/jpa/domain/SpecificationUnitTests.java | 2 +- .../data/jpa/domain/sample/AbstractAnnotatedAuditable.java | 2 +- .../data/jpa/domain/sample/AbstractMappedType.java | 2 +- .../org/springframework/data/jpa/domain/sample/Account.java | 2 +- .../org/springframework/data/jpa/domain/sample/Address.java | 2 +- .../data/jpa/domain/sample/AnnotatedAuditableUser.java | 2 +- .../springframework/data/jpa/domain/sample/AuditableRole.java | 2 +- .../springframework/data/jpa/domain/sample/AuditableUser.java | 2 +- .../data/jpa/domain/sample/AuditorAwareStub.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Child.java | 2 +- .../springframework/data/jpa/domain/sample/ConcreteType1.java | 2 +- .../springframework/data/jpa/domain/sample/ConcreteType2.java | 2 +- .../data/jpa/domain/sample/CustomAbstractPersistable.java | 2 +- .../org/springframework/data/jpa/domain/sample/Customer.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Dummy.java | 2 +- .../data/jpa/domain/sample/EmbeddedIdExampleDepartment.java | 2 +- .../data/jpa/domain/sample/EmbeddedIdExampleEmployee.java | 2 +- .../data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java | 2 +- .../data/jpa/domain/sample/EntityWithAssignedId.java | 2 +- .../data/jpa/domain/sample/IdClassExampleDepartment.java | 2 +- .../data/jpa/domain/sample/IdClassExampleEmployee.java | 2 +- .../data/jpa/domain/sample/IdClassExampleEmployeePK.java | 2 +- .../org/springframework/data/jpa/domain/sample/Invoice.java | 2 +- .../org/springframework/data/jpa/domain/sample/InvoiceItem.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Item.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/ItemId.java | 2 +- .../org/springframework/data/jpa/domain/sample/ItemSite.java | 2 +- .../org/springframework/data/jpa/domain/sample/ItemSiteId.java | 2 +- .../org/springframework/data/jpa/domain/sample/MailMessage.java | 2 +- .../org/springframework/data/jpa/domain/sample/MailSender.java | 2 +- .../org/springframework/data/jpa/domain/sample/MailUser.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Order.java | 2 +- .../springframework/data/jpa/domain/sample/OrmXmlEntity.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Parent.java | 2 +- .../data/jpa/domain/sample/PersistableWithIdClass.java | 2 +- .../data/jpa/domain/sample/PersistableWithIdClassPK.java | 2 +- .../data/jpa/domain/sample/PersistableWithSingleIdClass.java | 2 +- .../data/jpa/domain/sample/PersistableWithSingleIdClassPK.java | 2 +- .../data/jpa/domain/sample/PrimitiveVersionProperty.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Role.java | 2 +- .../springframework/data/jpa/domain/sample/SampleEntity.java | 2 +- .../springframework/data/jpa/domain/sample/SampleEntityPK.java | 2 +- .../data/jpa/domain/sample/SampleWithPrimitiveId.java | 2 +- .../data/jpa/domain/sample/SampleWithTimestampVersion.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Site.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/User.java | 2 +- .../data/jpa/domain/sample/UserSpecifications.java | 2 +- .../springframework/data/jpa/domain/sample/VersionedUser.java | 2 +- .../support/AbstractAttributeConverterIntegrationTests.java | 2 +- .../AnnotationAuditingBeanFactoryPostProcessorUnitTests.java | 2 +- .../data/jpa/domain/support/AuditingEntityListenerTests.java | 2 +- .../infrastructure/EclipseLinkMetamodelIntegrationTests.java | 2 +- .../jpa/infrastructure/HibernateMetamodelIntegrationTests.java | 2 +- .../data/jpa/infrastructure/HibernateTestUtils.java | 2 +- .../data/jpa/infrastructure/MetamodelIntegrationTests.java | 2 +- .../jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java | 2 +- .../jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java | 2 +- .../data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java | 2 +- .../data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java | 2 +- .../data/jpa/provider/PersistenceProviderIntegrationTests.java | 2 +- .../data/jpa/provider/PersistenceProviderUnitTests.java | 2 +- .../jpa/repository/AbstractPersistableIntegrationTests.java | 2 +- .../data/jpa/repository/CrudMethodMetadataUnitTests.java | 2 +- .../repository/CustomAbstractPersistableIntegrationTests.java | 2 +- .../data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java | 2 +- .../jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java | 2 +- ...EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java | 2 +- .../repository/EclipseLinkParentRepositoryIntegrationTests.java | 2 +- .../EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java | 2 +- .../repository/EclipseLinkStoredProcedureIntegrationTests.java | 2 +- .../EntityGraphRepositoryMethodsIntegrationTests.java | 2 +- .../jpa/repository/EntityWithAssignedIdIntegrationTests.java | 2 +- .../data/jpa/repository/JavaConfigUserRepositoryTests.java | 2 +- .../jpa/repository/MappedTypeRepositoryIntegrationTests.java | 2 +- .../data/jpa/repository/NamespaceUserRepositoryTests.java | 2 +- .../OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java | 2 +- .../jpa/repository/OpenJpaNamespaceUserRepositoryTests.java | 2 +- .../OpenJpaRepositoryWithCompositeKeyIntegrationTests.java | 2 +- .../jpa/repository/OpenJpaStoredProcedureIntegrationTests.java | 2 +- .../data/jpa/repository/RedeclaringRepositoryMethodsTests.java | 2 +- .../data/jpa/repository/RepositoryWithCompositeKeyTests.java | 2 +- .../org/springframework/data/jpa/repository/SPR8954Tests.java | 2 +- .../data/jpa/repository/SimpleJpaParameterBindingTests.java | 2 +- .../data/jpa/repository/StoredProcedureIntegrationTests.java | 2 +- .../UserRepositoryStoredProcedureIntegrationTests.java | 2 +- .../data/jpa/repository/UserRepositoryTests.java | 2 +- .../data/jpa/repository/cdi/CdiExtensionIntegrationTests.java | 2 +- .../data/jpa/repository/cdi/EntityManagerFactoryProducer.java | 2 +- .../jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java | 2 +- .../org/springframework/data/jpa/repository/cdi/Person.java | 2 +- .../org/springframework/data/jpa/repository/cdi/PersonDB.java | 2 +- .../data/jpa/repository/cdi/PersonRepository.java | 2 +- .../jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java | 2 +- .../jpa/repository/cdi/QualifiedCustomizedUserRepository.java | 2 +- .../repository/cdi/QualifiedCustomizedUserRepositoryBean.java | 2 +- .../repository/cdi/QualifiedCustomizedUserRepositoryCustom.java | 2 +- .../data/jpa/repository/cdi/QualifiedEntityManagerProducer.java | 2 +- .../data/jpa/repository/cdi/QualifiedFragment.java | 2 +- .../data/jpa/repository/cdi/QualifiedFragmentBean.java | 2 +- .../data/jpa/repository/cdi/QualifiedPersonRepository.java | 2 +- .../data/jpa/repository/cdi/RepositoryConsumer.java | 2 +- .../data/jpa/repository/cdi/SamplePersonRepository.java | 2 +- .../data/jpa/repository/cdi/SamplePersonRepositoryCustom.java | 2 +- .../data/jpa/repository/cdi/SamplePersonRepositoryImpl.java | 2 +- .../springframework/data/jpa/repository/cdi/Transactional.java | 2 +- .../data/jpa/repository/cdi/TransactionalInterceptor.java | 2 +- .../jpa/repository/cdi/UnqualifiedEntityManagerProducer.java | 2 +- .../data/jpa/repository/cdi/UnqualifiedPersonRepository.java | 2 +- .../org/springframework/data/jpa/repository/cdi/UserDB.java | 2 +- .../config/AbstractAuditingViaJavaConfigRepositoriesTests.java | 2 +- .../jpa/repository/config/AbstractRepositoryConfigTests.java | 2 +- .../config/AllowNestedRepositoriesRepositoryConfigTests.java | 2 +- .../repository/config/AuditingBeanDefinitionParserTests.java | 2 +- .../repository/config/CustomRepositoryFactoryConfigTests.java | 2 +- .../config/DefaultAuditingViaJavaConfigRepositoriesTests.java | 2 +- .../config/ExplicitAuditingViaJavaConfigRepositoriesTests.java | 2 +- .../data/jpa/repository/config/InfrastructureConfig.java | 2 +- .../jpa/repository/config/InspectionClassLoaderUnitTests.java | 2 +- .../jpa/repository/config/JpaAuditingRegistrarUnitTests.java | 2 +- .../config/JpaRepositoriesRegistrarIntegrationTests.java | 2 +- .../repository/config/JpaRepositoriesRegistrarUnitTests.java | 2 +- .../repository/config/NestedRepositoriesJavaConfigTests.java | 2 +- .../data/jpa/repository/config/QueryLookupStrategyTests.java | 2 +- .../data/jpa/repository/config/RepositoriesJavaConfigTests.java | 2 +- .../data/jpa/repository/config/RepositoryAutoConfigTests.java | 2 +- .../data/jpa/repository/config/RepositoryConfigTests.java | 2 +- .../data/jpa/repository/config/TypeFilterConfigTests.java | 2 +- .../data/jpa/repository/custom/CustomGenericJpaRepository.java | 2 +- .../repository/custom/CustomGenericJpaRepositoryFactory.java | 2 +- .../custom/CustomGenericJpaRepositoryFactoryBean.java | 2 +- .../data/jpa/repository/custom/CustomGenericRepository.java | 2 +- .../jpa/repository/custom/UserCustomExtendedRepository.java | 2 +- .../procedures/MySqlStoredProcedureIntegrationTests.java | 2 +- .../procedures/PostgresStoredProcedureIntegrationTests.java | 2 +- .../repository/projections/ProjectionJoinIntegrationTests.java | 2 +- .../jpa/repository/projections/ProjectionsIntegrationTests.java | 2 +- .../data/jpa/repository/query/AbstractJpaQueryTests.java | 2 +- .../query/AbstractStringBasedJpaQueryIntegrationTests.java | 2 +- .../query/CustomNonBindableJpaParametersIntegrationTests.java | 2 +- .../data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java | 2 +- .../EclipseLinkParameterMetadataProviderIntegrationTests.java | 2 +- .../repository/query/EclipseLinkQueryUtilsIntegrationTests.java | 2 +- .../data/jpa/repository/query/EscapeCharacterUnitTests.java | 2 +- .../repository/query/ExpressionBasedStringQueryUnitTests.java | 2 +- .../data/jpa/repository/query/Jpa21UtilsTests.java | 2 +- .../repository/query/JpaCountQueryCreatorIntegrationTests.java | 2 +- .../data/jpa/repository/query/JpaParametersUnitTests.java | 2 +- .../data/jpa/repository/query/JpaQueryExecutionUnitTests.java | 2 +- .../jpa/repository/query/JpaQueryLookupStrategyUnitTests.java | 2 +- .../data/jpa/repository/query/LikeBindingUnitTests.java | 2 +- .../query/NamedOrIndexedQueryParameterSetterUnitTests.java | 2 +- .../data/jpa/repository/query/NamedQueryUnitTests.java | 2 +- .../data/jpa/repository/query/OpenJpaJpa21UtilsTests.java | 2 +- .../query/OpenJpaParameterMetadataProviderIntegrationTests.java | 2 +- .../jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java | 2 +- .../jpa/repository/query/ParameterBindingParserUnitTests.java | 2 +- .../jpa/repository/query/ParameterExpressionProviderTests.java | 2 +- .../query/ParameterMetadataProviderIntegrationTests.java | 2 +- .../repository/query/ParameterMetadataProviderUnitTests.java | 2 +- .../jpa/repository/query/PartTreeJpaQueryIntegrationTests.java | 2 +- .../repository/query/QueryParameterSetterFactoryUnitTests.java | 2 +- .../data/jpa/repository/query/QueryUtilsIntegrationTests.java | 2 +- .../data/jpa/repository/query/SimpleJpaQueryUnitTests.java | 2 +- .../query/StoredProcedureAttributeSourceUnitTests.java | 2 +- .../repository/query/StoredProcedureAttributesUnitTests.java | 2 +- .../data/jpa/repository/query/StringQueryUnitTests.java | 2 +- .../data/jpa/repository/query/TupleConverterUnitTests.java | 2 +- .../jpa/repository/sample/AnnotatedAuditableUserRepository.java | 2 +- .../data/jpa/repository/sample/AuditableUserRepository.java | 2 +- .../data/jpa/repository/sample/CategoryRepository.java | 2 +- .../data/jpa/repository/sample/ClassWithNestedRepository.java | 2 +- .../data/jpa/repository/sample/ConcreteRepository1.java | 2 +- .../data/jpa/repository/sample/ConcreteRepository2.java | 2 +- .../repository/sample/CustomAbstractPersistableRepository.java | 2 +- .../data/jpa/repository/sample/DummyRepository.java | 2 +- .../jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java | 2 +- .../jpa/repository/sample/EmployeeRepositoryWithIdClass.java | 2 +- .../jpa/repository/sample/EntityWithAssignedIdRepository.java | 2 +- .../data/jpa/repository/sample/ItemRepository.java | 2 +- .../data/jpa/repository/sample/ItemSiteRepository.java | 2 +- .../data/jpa/repository/sample/MailMessageRepository.java | 2 +- .../data/jpa/repository/sample/MappedTypeRepository.java | 2 +- .../springframework/data/jpa/repository/sample/NameOnlyDto.java | 2 +- .../data/jpa/repository/sample/ParentRepository.java | 2 +- .../data/jpa/repository/sample/ProductRepository.java | 2 +- .../sample/RedeclaringRepositoryMethodsRepository.java | 2 +- .../RepositoryMethodsWithEntityGraphConfigRepository.java | 2 +- .../data/jpa/repository/sample/RoleRepository.java | 2 +- .../data/jpa/repository/sample/SampleConfig.java | 2 +- .../jpa/repository/sample/SampleEvaluationContextExtension.java | 2 +- .../data/jpa/repository/sample/SiteRepository.java | 2 +- .../data/jpa/repository/sample/UserRepository.java | 2 +- .../data/jpa/repository/sample/UserRepositoryCustom.java | 2 +- .../data/jpa/repository/sample/UserRepositoryImpl.java | 2 +- .../CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java | 2 +- .../data/jpa/repository/support/DefaultJpaContextUnitTests.java | 2 +- .../repository/support/DefaultJpaEntityMetadataUnitTest.java | 2 +- .../data/jpa/repository/support/DefaultQueryHintsTest.java | 2 +- .../support/DefaultTransactionDisablingIntegrationTests.java | 2 +- ...clipseLinkJpaMetamodelEntityInformationIntegrationTests.java | 2 +- .../jpa/repository/support/EclipseLinkJpaRepositoryTests.java | 2 +- .../jpa/repository/support/EclipseLinkProxyIdAccessorTests.java | 2 +- .../jpa/repository/support/EntityGraphFactoryUnitTests.java | 2 +- ...gerBeanDefinitionRegistrarPostProcessorIntegrationTests.java | 2 +- ...ityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java | 2 +- .../repository/support/EntityManagerFactoryRefUnitTests.java | 2 +- .../support/FetchableFluentQueryByExampleUnitTests.java | 2 +- .../support/FetchableFluentQueryByPredicateUnitTests.java | 2 +- .../JavaConfigDefaultTransactionDisablingIntegrationTests.java | 2 +- .../support/JpaMetamodelEntityInformationIntegrationTests.java | 2 +- .../support/JpaMetamodelEntityInformationUnitTests.java | 2 +- .../support/JpaPersistableEntityInformationUnitTests.java | 2 +- ...RepositoryFactoryBeanEntityPathResolverIntegrationTests.java | 2 +- .../repository/support/JpaRepositoryFactoryBeanUnitTests.java | 2 +- .../support/MailMessageRepositoryIntegrationTests.java | 2 +- .../data/jpa/repository/support/MutableQueryHintsUnitTests.java | 2 +- .../OpenJpaMetamodelEntityInformationIntegrationTests.java | 2 +- .../jpa/repository/support/OpenJpaProxyIdAccessorTests.java | 2 +- .../support/QSimpleEntityPathResolverUnitTests_Sample.java | 2 +- .../data/jpa/repository/support/QuerydslIntegrationTests.java | 2 +- .../support/QuerydslRepositorySupportIntegrationTests.java | 2 +- .../jpa/repository/support/SimpleJpaRepositoryUnitTests.java | 2 +- .../XmlConfigDefaultTransactionDisablingIntegrationTests.java | 2 +- .../ClasspathScanningPersistenceUnitPostProcessorUnitTests.java | 2 +- .../data/jpa/support/EntityManagerTestUtils.java | 2 +- .../jpa/support/MergingPersistenceUnitManagerUnitTests.java | 2 +- .../test/java/org/springframework/data/jpa/util/FixedDate.java | 2 +- .../data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java | 2 +- .../springframework/data/jpa/util/JpaMetamodelUnitTests.java | 2 +- src/main/asciidoc/index.adoc | 2 +- 357 files changed, 357 insertions(+), 357 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java index ba7582d347..663fe24250 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java index db9cab6aa1..e295529fe5 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java index ee2bbe30b8..bf7227bc94 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java index 79beff3d62..9d63891480 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java index 05bd785d86..1006e3aa3c 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index a9b61b37eb..abfad6287c 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java index 95ca40e5ff..65d22ff772 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java b/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java index 93ad2f51bd..0d30f435a4 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java index 9a41760826..44008e5e87 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java index fbbe1d0d04..df9acf0953 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java index 751673d305..c60011a146 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java index 126e903990..d75066dbd2 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java index 5d94361b20..6868bff5a5 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java index e0d79c654a..ce6e02de2c 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java index c78dc44ea5..cab7f48f13 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java index e9e80bf105..7a9f0fee84 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java index fd47bb1a6f..d9f68949b5 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java index 37a887db9a..e090011094 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java index 456805ce8f..c218e551ea 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java index 3ab2374ea4..ad8ce3f0bb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java index 45c6d7edad..2c4e1122dd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java index 2531f9f615..de1355b35c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index c29704cc32..dd98f192e7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java index 15fcd9173a..8fa8f766e9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java index 0dc3a976a3..70c864126b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java index 2fc1170873..d3581f9d99 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java index 8d453ca09f..ca73ad9d9b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index 33b0dab855..0d129cccfc 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java index 17c26c8e35..8feb18c024 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index 67cd5edd10..2165ce61bf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java index 4485fffe1d..c23744af15 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index 9f5efda42d..def06c3e78 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java index 64e0ca80b8..da3b398451 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java index 68001ab220..3c6a3556fe 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java index d756dfec1f..dc3be2c4a7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java index 69fc050a75..7690829620 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java index 9f6779c5c9..f2429b0107 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java index 2c319b87aa..1e520c8f03 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java index f40d4cab1e..b233ab2f93 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index fd68531c88..5e204ea30a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index 2dc128c79a..63c83f9963 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java index 0a9035d590..cd0389a6e9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java index 3d16e5238b..a31288d070 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java index 9193ceafd4..f12e7c39c4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java index 38447f2ae5..9ec19baa38 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java index 7e8b6b22fc..f94a2d5168 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index 203802b19e..84dec88f98 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index 01b1ed4b53..71634d5bc2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java index f77140c4b9..3b080f7b7a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java index 092e9c944b..69df7a7cb4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java index 80347540bc..802b831700 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java index 8aaf693748..4a5d3954f5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java index 9552190628..9e536f42ef 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index 8a8c841446..96fff0518d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java index 391a6b0ea8..d1ba38f816 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 5dab0e01ea..701548d565 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java index 91d16a010b..d9ba90bd20 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index dfd11cc60d..02f4ee9cc0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 4211f50c07..66f3886677 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java index 60b4906f87..9aa4555549 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java index edfc8cca3a..56b1551e9d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java index a992a66c89..62851a2d79 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java index 35896df5f7..cb4e18bda8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java index ee7c6945ac..840ea8a767 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index b907fdbf47..eb773c1986 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java index 7379d22331..d3a9e4f8c3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java index a86a362e6a..cff97cfa9e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index e36f9d27f2..af91145928 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java index a94a190592..e822ef9869 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java index ce961c9281..609c5ed054 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index 3f69741e1e..5a5ce1d22f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java index d32465a8d7..f01dbdc69e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 6777a4e8b9..ad61f599fe 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index bb8f0c931c..2cc12a5a1a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index a66d0c76f5..6eada129d2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 771d3f62cc..0db5d16bae 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index a830d269ce..c44bf7eb76 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java index ada28b905e..135d890692 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java index a1d3176bd2..fb80a62d54 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 5b7edddd62..73f7763a96 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index 8697e34243..f038669198 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index bac36d1f3b..6ffabbdb33 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java index f3e6ccd1ee..a16547cea6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 0f9e7ff682..9eeb694a63 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 69775ad6e0..28c08f47ba 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java index 7b7835152c..db62ce736d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java index ac06cd2d9c..0c67cf159c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index e2f75cc650..1defa4a3f4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index 9cce41634f..72986cf4b2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index c42fef8b2d..28bf1c7dcf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index 4acacfad41..cd0b4f228f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java index e560209df5..db63ce83c0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java index 077a7f8501..36e981c844 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index f7e242c68b..f4af59547f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java index 3933c51815..4aabf7d24a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 2e067cf257..69c9152ef5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java index 548220413b..64914eafc6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java index 8c8c3892d0..9d8d114516 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java index 68edc9d058..9e6c35a7ed 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index 7857181c2b..7cb65ab8a7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java index ab7d58ddc9..8b9c352ca0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java index 75bdb5fa80..0a75ea183f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java index 1dc6f0d1e3..afec2277ee 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java index ebff0a8181..f9495ae9e6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index bc5238d620..cdc386df0e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java index 6009b5ac19..cf9bf7519f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index a8a98c6cf4..9b6f2471e1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index a3eb78d48e..f093e1247f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java index b7b14bfc4e..c9d7962fb5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java index 9663eaf767..a7b292626a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java index 23f1d5cf2b..6dcd0da94b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java index aabfbfefdf..9952790e5e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index cb19fd8e8b..479337f34c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 84110594e1..de10c83bc2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index f4f29a0c63..51b6fafd47 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java index a4b45ff2c3..f4f54c7709 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 3c1639f04f..29b8d5e461 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index 4d9b38df5c..f26e1f800b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java index 0bd0394b87..aef4bc5ac4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java index dcba8b9ea9..0653837d5b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java index d417e2edd4..963e48d0a3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java index ed783022bb..17075fa707 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java index bb906d07c9..459604cb81 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java index c72d859eb5..8023c2720e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java index 494284c806..f51723d949 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java index 0d22dc2dbd..9988d26c97 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java index 54d3015d8d..574cb39126 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java index 2c903de6e4..84ca50c178 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java index c6727e55b3..60224d7ffe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java index 97f219b42e..213fb2a869 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java index 260d1f2999..93019b766e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java index 218bc4da99..cae480d065 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java index aabebee038..f52c5af12b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java index 278f7ebd6f..009470503a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java index 1f387058db..6d0ce9f632 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java index c9056e206f..b9f331f7ca 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java index b241f4c62d..94ce575d8d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java index 0cc846b0ef..ec5504a018 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java index 4ad450e502..efacee7266 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java index bb1991cac6..11a678a22d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java index d1cca062ec..632d3a45d9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java index 58171bb36d..48a5fc228e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java index c9251da09b..73507abc66 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java index fd661477a6..af25ce426f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java index fa67463cb6..fde9a12754 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java index 1cda6c3db0..649449dd77 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java index 978daf4760..7c69bfd6f9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java index a20587a4a8..5499a052ba 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java index e6cfb8d626..3469136741 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java index 8015e09f2b..61c9fb9cef 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java index 7c3c93f384..43206a0652 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java index dc50b4396e..3b7bf6004f 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java index 3fb434a60a..13e6b3f792 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java index 79436f221d..f465908470 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java index 202ca46cba..148d71b9db 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java index 25688a7a27..b27cc536d1 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java index 032134d9ea..5957026622 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java index 00f68fd7fc..9ff6e812f7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java index 18bc41b148..5fc14ce2b6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java index fe178d165b..d7f1246c12 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java index ebe99ad1cc..00be476528 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java index 7c764d8951..afa47f03e3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java index a60f3f032c..b76d35eb78 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java index 48f3e569f6..80ca79d417 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java index 4fc01df870..9397153d51 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java index 96f4936b56..0d75871baa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java index 08c91b6ef2..82efbac73b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java index a64dc68c7f..654838802c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java index 0631ffaf9f..6b100acc03 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java index de6877e77a..c5979f58c6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java index 31af57b649..7ca9ccef61 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java index 17cd65d319..2e5361aaef 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java index b7b5a13e57..13202e433c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java index eabe86dfc8..5ebd6a4535 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java index 558723b715..68ee1c9685 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java index d20b4f084f..22628d794f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java index c4af51eb19..da8e9a80b0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java index 422dffadeb..fa545395b8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java index 8cc4271f5e..e45e92937d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java index 95bfa58f6a..96273a6d96 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java index 4a7b9651bc..29ffe8ccb6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java index 116df91ba3..67156d04d4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java index dbdc14ba18..d4ff975cf5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java index 37426368f7..5f50b9f843 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java index cd8d706cc7..406837bf75 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index 376132cc26..42cff4e0ce 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java index c2564b7308..7d2b088265 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java index ee7cee3eeb..f7159719f7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java index f54f2d25c5..789dca91ef 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java index 24ac368f13..84236f7d22 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java index 8a49cda79f..72107459b5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java index 1ad5297084..c171a0f58b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java index 4dade6a684..b4dda054b0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java index cf7db769a0..45bc1e056d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java index 43aff6bfc1..6262abce8c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java index 4011ff3012..7854036800 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java index 92a013a310..bf06c43e8b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index 4373fc0164..67368194f8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java index 700b33b61a..0848bd6a18 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java index a29658513b..0d3999d483 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java index ac111c308b..bee615f112 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java index 80dc2b959e..2d333fb388 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java index f201cb227c..8dd4f9f7d6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java index f0e862ada0..f062fc4bff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java index 8ede70fd54..f4d458a512 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java index 61e46fec37..5886b0b6cf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java index 6a5bfbd998..51138c8abd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index 18f1574afe..657993cb52 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java index 00d83422d3..e6a83ade5e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java index 61911d7cf2..8ddf03b933 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java index d11352f5bd..65a9ac41d1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java index 2b62926c86..8ec50cc883 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 e89a581daf..aa411599e1 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 @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java index 0301fd5ab9..5f0a69407c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java index ede95017fb..bbddb6e31e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java index 75b014c69c..09b53c9a4f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java index b6139d748b..398c1e8a84 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java index 9a6e6433b7..bb927f5c49 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java index 5f36fc35cc..389c7b730e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java index ff8da27843..b03959c617 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java index af8c3948c7..bb0794c4c5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java index d163762450..93860c7397 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java index 158a56043d..faa112a39b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java index 74301ef100..07840ca545 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java index 3a2bdf58d8..0dfc7bbed0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java index d53214351a..beb2578c5d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java index b15c0b01c6..c8c033a6fe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java index e17a5dc04c..2dc0b545f3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java index 116bbed170..70e9c97889 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java index 428dfad5e9..5433db8803 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java index 260d1ef79a..18c23fb0b0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java index d8b31cbd62..cb7fc0aefe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java index a0697c3d01..1dcdea9c62 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java index 88dd3b669b..f17b285ff6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java index 1967825567..5b5631ffcd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java index 7b38a02cfb..130d7e5870 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java index b569d2a318..3dfa45e7dd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java index e0b82c7409..5545b1e708 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java index 2b21ab04fe..42c85d02fb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java index 9070d92091..bd36fdf8e1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java index ce3260339c..8a15d3b208 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java index 8b0e2ffd27..e4d59b8249 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java index ef6ddce991..24e39fe37a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java index a308e2488e..ac6e21b36a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java index 80adb029e0..03e3db11ba 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java index b8b4a3bf92..f61c5d5947 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java index 774718fb22..585e28d600 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java index e519eecef8..5b10d4f1f1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java index bfb6549f38..88bcd9b890 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java index f4b613e877..cb661bae29 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java index 94180be436..80633bb843 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java index 0cd5e2ddf9..c87614255b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java index dbb84010fe..a64b77948f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java index 335cd6cc52..1dfd1e00fb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java index c463664409..3c7f560a2b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java index 76f46ab52a..0ac035f661 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java index a41344fd96..5bcd29793f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java index e6a1b8979c..9087f904ea 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java index ded714557b..1ab2b1137c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java index 82dc54067b..4112d1825e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index b5f6a5c60d..5243cfce5a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java index b985357e88..e188672ef0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java index f41d69d0e5..0117b57691 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index bd175ef1f9..9f06195de0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index 059bc58242..a7254bc961 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java index 5367162444..5be0d3be7f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java index 0487e01362..98f75756af 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java index 8c4ae89c49..1b8539ef55 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java index 4948f8c8d6..d3edf8552a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java index b4b07435be..1edd78e9a7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index a757fda895..7a747cb986 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java index 42436c314d..b50bd96da5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java index 15b551e407..f42756621c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java index 229702392e..57c702a575 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java index 4f0b8ab3a9..179038c88f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index 4f85e56985..187df77ea7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java index fdf62d0006..a18391405b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java index 4d60b34863..9d59b2b503 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java index 913cc64c93..0bd6ff3cbe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java index dc67af46ef..b15353b52f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java index e8525ce758..af2dc6e670 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java index 9165b09454..203ba22602 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java index ae7e6a452e..7c4f764fec 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java index 7d501cf8cf..04b9e53b53 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java index 2407d0ba57..5923b36610 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java index 19d626ec90..fd3ba4a7cf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 974734efb4..e79fa55c61 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License import org.springframework.aop.framework.Advised; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java index e56a86ebc8..68e38e4925 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index a00f1763a6..8fde636585 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 2b3888ac05..077b0474f5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java index ab9c804c76..76d56669df 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java index 10f09fbe14..ea9698f5cc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 1c47c35de2..adec631c12 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java index 131fa07d46..b3c3f60abf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java index 81b3b331f6..da986021f0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java index 4e93c19659..004f131ae2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java index 0ab9d73661..fce45c4a2b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java index eba6d1d459..da3c8954f5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java index d20c7756ee..39dcd73955 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java index 57a2ee227e..692aa9d700 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java index 04cc2f987c..d8466e756e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java index ece4356bf8..1c43328396 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java index f43cf9ac21..e57839669c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java index 23911f359b..86c75537c8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java index 2a79764ee5..1a6e5eeeee 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java index cc83d92068..8afa950a6f 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java index 14f02baaf2..0914eb6cec 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java index dd679c7325..bb321fc3f9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java index 9b9e2c07df..2d8e7f006f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java index a54e4318c7..295d142eb9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java index 0ac144aeb5..0992c61d78 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java index 400d966e4b..de874a708c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java index 7e7690ac98..a605423e40 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java index 603fcd67f1..b48b1ec23b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java index a72f000b82..e166589fb4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java index 2eccc61af3..fb1ad9c33c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java index e2a8c05bde..1e207955ca 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java index deb90d2b92..fccba7a7df 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 6570a5ea87..e5a748e97a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java index a66d808767..d345ba7135 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java index 6f23da4918..0110d4560f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java index 80b1365611..1eab934632 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java index 4be4afddb5..9eda1e8c66 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java index 89ad5f4817..ead477af5f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java index 9574ea733b..bc6bdb9e01 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java index 069791f7a7..62cdfad6cf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java index cc30f2529b..a7669f7d0d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java index 2b78b23dbe..2574770e7e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java index b4a55e2f63..6d41f58577 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java index 448bd0adbd..8dac763c79 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java index 48762e1fb5..5ac7181898 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java index 9b92bc52d7..aa0da50512 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java index 38ca24a882..f507bb2890 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java index c29581e3c4..81b469b1ea 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java index 70fb3fd9a3..a3bb811baa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java index ae5e6bceb1..06b2145e30 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java index 5b90488556..b4391d39c5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java index ce1207c300..e3820d10b0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java index c379a031a6..cfcde4816f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java index c4488ebc49..41348a2de4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java index 28c12dc6ef..a60e1dd1cc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 the original author or authors. + * Copyright 2008-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java index 688ae55eba..64673e79ef 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java index 7a4b928a5e..bd66b3d4d3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java index 132494d69d..e2c09f6098 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java index 96d658c2e5..8122464dcb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java index 3530971a65..a229178ff3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java index ba4d78bc01..b980d68f15 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java index e5675fff31..cddc3c153d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index 9fabb41df0..ab9b5b67b6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java index 5d8fc12ae9..a9dcaf95e7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java index d06a73c34d..19edebe0e4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java index 67dd3e4eab..be55fbf169 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java index db0d5e5799..3c1729a353 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java index f4f1770705..44cb4dd28e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2021 the original author or authors. + * Copyright 2015-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java index 035f5d3717..d1175edb7d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java index 8aab83f2b0..5efaa467ac 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index 14cc7771fa..ce205ea720 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -6,7 +6,7 @@ ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1 :spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc :spring-framework-docs: https://docs.spring.io/spring-framework/docs/{springVersion}/spring-framework-reference/ -(C) 2008-2021 The original authors. +(C) 2008-2022 The original authors. NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. From 7b3dd770484770c7b37fdf7b90ede20f347ac046 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Mon, 21 Feb 2022 20:36:34 +0100 Subject: [PATCH 149/821] Added exists method with specification to `JpaSpecificationExecutor`. It is now possible to make existence checks based on a `Specification`. This is an addition to checking existence with using an `Example`. Closes #2388 Original pull request #2449 --- .../repository/JpaSpecificationExecutor.java | 10 +++++++++ .../support/SimpleJpaRepository.java | 21 ++++++++++++++----- .../jpa/domain/sample/UserSpecifications.java | 12 +++++++++++ .../jpa/repository/UserRepositoryTests.java | 12 +++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index 63c83f9963..e310f8709f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -29,6 +29,7 @@ * * @author Oliver Gierke * @author Christoph Strobl + * @author Diego Krupitza */ public interface JpaSpecificationExecutor { @@ -74,4 +75,13 @@ public interface JpaSpecificationExecutor { * @return the number of instances. */ long count(@Nullable Specification spec); + + /** + * Checks whether the data store contains elements that match the given {@link Specification}. + * + * @param spec the {@link Specification} to use for the existence check. Must not be {@literal null}. + * @return true if the data store contains elements that match the given {@link Specification} otherwise + * false. + */ + boolean exists(Specification spec); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 29b8d5e461..e24613e6f6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -40,11 +40,7 @@ import jakarta.persistence.criteria.Root; import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; +import org.springframework.data.domain.*; import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.provider.PersistenceProvider; @@ -81,6 +77,7 @@ * @author Greg Turnquist * @author Yanming Zhou * @author Ernst-Jan van der Laan + * @author Diego Krupitza */ @Repository @Transactional(readOnly = true) @@ -464,6 +461,20 @@ public boolean exists(Example example) { return query.setMaxResults(1).getResultList().size() == 1; } + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#exists(org.springframework.data.jpa.domain.Specification) + */ + @Override + public boolean exists(Specification spec) { + + CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(Integer.class); + cq.select(this.em.getCriteriaBuilder().literal(1)); + applySpecificationToCriteria(spec, getDomainClass(), cq); + TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); + return query.setMaxResults(1).getResultList().size() == 1; + } + @Override public List findAll(Example example) { return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java index 5ebd6a4535..e63e0f8509 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java @@ -26,6 +26,7 @@ * Collection of {@link Specification}s for a {@link User}. * * @author Oliver Gierke + * @author Diego Krupitza */ public class UserSpecifications { @@ -69,6 +70,17 @@ public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBu }; } + /** + * A {@link Specification} to do an age check. + * + * @param age upper (exclusive) bound of the age + * @return + */ + public static Specification userHasAgeLess(final Integer age) { + + return (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), age); + } + /** * A {@link Specification} to do a like-match on a {@link User}'s lastname but also adding a sort order on the * firstname. 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 aa411599e1..bd8664520e 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 @@ -96,6 +96,7 @@ * @author Sander Krabbenborg * @author Jesse Wouters * @author Greg Turnquist + * @author Diego Krupitza */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @@ -2640,6 +2641,17 @@ void readsDerivedInterfaceProjections() { assertThat(repository.findAllInterfaceProjectedBy()).hasSize(4); } + @Test // GH-2388 + void existsWithSpec() { + flushTestUsers(); + + Specification minorSpec = userHasAgeLess(18); + Specification hundredYearsOld = userHasAgeLess(100); + + assertThat(repository.exists(minorSpec)).isFalse(); + assertThat(repository.exists(hundredYearsOld)).isTrue(); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); From 47e2423612b2410ec284d1cb048d5a8815a705f3 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 22 Feb 2022 08:44:39 +0100 Subject: [PATCH 150/821] Polishing. Fixed import formatting. Removed (non-Javadoc) comment, since we don't use them anymore. Simplified some code and removed superfluous or wrong Javadoc in touched files. Formatting. See #2388 Original pull request #2449 --- .../support/SimpleJpaRepository.java | 14 +++---- .../jpa/domain/sample/UserSpecifications.java | 41 +++---------------- .../jpa/repository/UserRepositoryTests.java | 1 + 3 files changed, 12 insertions(+), 44 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index e24613e6f6..6ef501c066 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -40,7 +40,11 @@ import jakarta.persistence.criteria.Root; import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.data.domain.*; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.provider.PersistenceProvider; @@ -287,7 +291,6 @@ public Optional findById(ID id) { /** * Returns {@link QueryHints} with the query hints based on the current {@link CrudMethodMetadata} and potential * {@link EntityGraph} information. - * */ protected QueryHints getQueryHints() { return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata); @@ -461,10 +464,6 @@ public boolean exists(Example example) { return query.setMaxResults(1).getResultList().size() == 1; } - /* - * (non-Javadoc) - * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#exists(org.springframework.data.jpa.domain.Specification) - */ @Override public boolean exists(Specification spec) { @@ -483,8 +482,7 @@ public List findAll(Example example) { @Override public List findAll(Example example, Sort sort) { - return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), sort) - .getResultList(); + return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), sort).getResultList(); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java index e63e0f8509..5e0b710127 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java @@ -32,9 +32,6 @@ public class UserSpecifications { /** * A {@link Specification} to match on a {@link User}'s firstname. - * - * @param firstname - * @return */ public static Specification userHasFirstname(final String firstname) { @@ -43,9 +40,6 @@ public static Specification userHasFirstname(final String firstname) { /** * A {@link Specification} to match on a {@link User}'s lastname. - * - * @param firstname - * @return */ public static Specification userHasLastname(final String lastname) { @@ -54,27 +48,16 @@ public static Specification userHasLastname(final String lastname) { /** * A {@link Specification} to do a like-match on a {@link User}'s firstname. - * - * @param firstname - * @return */ public static Specification userHasFirstnameLike(final String expression) { - return new Specification() { - - @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { - - return cb.like(root.get("firstname").as(String.class), String.format("%%%s%%", expression)); - } - }; + return (root, query, cb) -> cb.like(root.get("firstname").as(String.class), String.format("%%%s%%", expression)); } /** * A {@link Specification} to do an age check. * * @param age upper (exclusive) bound of the age - * @return */ public static Specification userHasAgeLess(final Integer age) { @@ -84,33 +67,19 @@ public static Specification userHasAgeLess(final Integer age) { /** * A {@link Specification} to do a like-match on a {@link User}'s lastname but also adding a sort order on the * firstname. - * - * @param firstname - * @return */ public static Specification userHasLastnameLikeWithSort(final String expression) { - return new Specification() { - - @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + return (root, query, cb) -> { - query.orderBy(cb.asc(root.get("firstname"))); + query.orderBy(cb.asc(root.get("firstname"))); - return cb.like(root.get("lastname").as(String.class), String.format("%%%s%%", expression)); - } + return cb.like(root.get("lastname").as(String.class), String.format("%%%s%%", expression)); }; } private static Specification simplePropertySpec(final String property, final Object value) { - return new Specification() { - - @Override - public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder) { - - return builder.equal(root.get(property), value); - } - }; + return (root, query, builder) -> builder.equal(root.get(property), value); } } 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 bd8664520e..ba8affb9fd 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 @@ -2643,6 +2643,7 @@ void readsDerivedInterfaceProjections() { @Test // GH-2388 void existsWithSpec() { + flushTestUsers(); Specification minorSpec = userHasAgeLess(18); From 8c127f66b4c019b3ad0a0a81f91d99a55842c03b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Feb 2022 14:10:51 +0100 Subject: [PATCH 151/821] Update CI properties. See #2407 --- ci/pipeline.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 60ce02f476..43b4e65e48 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -5,14 +5,14 @@ java.main.tag=17.0.2_8-jdk docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} # Supported versions of MongoDB -docker.mongodb.4.4.version=4.4.4 -docker.mongodb.5.0.version=5.0.3 +docker.mongodb.4.4.version=4.4.12 +docker.mongodb.5.0.version=5.0.6 # Supported versions of Redis -docker.redis.6.version=6.2.4 +docker.redis.6.version=6.2.6 # Supported versions of Cassandra -docker.cassandra.3.version=3.11.11 +docker.cassandra.3.version=3.11.12 # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home From 1fe4cf598d7087ad8d434eb7b99bff90e033f164 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Sat, 22 Jan 2022 11:23:57 +0100 Subject: [PATCH 152/821] Introduce JSqlParser as option for parsing native queries. Introduce a library that makes it possible to parse native SQL statements. See #2409. --- pom.xml | 1 + spring-data-jpa/pom.xml | 8 + .../repository/query/AbstractJpaQuery.java | 16 +- .../query/AbstractStringBasedJpaQuery.java | 14 +- .../jpa/repository/query/DeclaredQuery.java | 15 +- .../query/DefaultQueryEnhancer.java | 74 ++ .../repository/query/EmptyDeclaredQuery.java | 2 +- .../query/ExpressionBasedStringQuery.java | 14 +- .../query/JSqlParserQueryEnhancer.java | 347 +++++++++ .../jpa/repository/query/JSqlParserUtils.java | 123 +++ .../jpa/repository/query/JpaQueryCreator.java | 182 +++-- .../jpa/repository/query/JpaQueryMethod.java | 2 +- .../data/jpa/repository/query/NamedQuery.java | 2 +- .../jpa/repository/query/QueryEnhancer.java | 116 +++ .../query/QueryEnhancerFactory.java | 75 ++ .../query/QueryParameterSetter.java | 3 +- .../data/jpa/repository/query/QueryUtils.java | 7 +- .../jpa/repository/query/StringQuery.java | 69 +- .../jpa/repository/UserRepositoryTests.java | 6 +- .../query/DefaultQueryUtilsUnitTests.java | 530 +++++++++++++ .../ExpressionBasedStringQueryUnitTests.java | 46 +- .../query/JpaQueryMethodUnitTests.java | 8 +- .../ParameterBindingParserUnitTests.java | 3 +- .../query/QueryEnhancerFactoryUnitTests.java | 46 ++ .../query/QueryEnhancerUnitTests.java | 707 ++++++++++++++++++ .../QueryParameterSetterFactoryUnitTests.java | 8 +- .../query/QueryUtilsIntegrationTests.java | 42 +- .../repository/query/QueryUtilsUnitTests.java | 20 +- .../query/StringQueryUnitTests.java | 189 ++--- 29 files changed, 2391 insertions(+), 284 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java diff --git a/pom.xml b/pom.xml index 07101c166f..a230dab8b6 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,7 @@ 42.2.19 3.0.0-SNAPSHOT 0.10.3 + 4.3 org.hibernate diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 5960d72cca..e4f17e4aca 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -226,6 +226,14 @@ test + + com.github.jsqlparser + jsqlparser + ${jsqlparser.version} + provided + true + + diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 02f4ee9cc0..7f950b018a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -15,14 +15,6 @@ */ package org.springframework.data.jpa.repository.query; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - import jakarta.persistence.EntityManager; import jakarta.persistence.LockModeType; import jakarta.persistence.Query; @@ -31,6 +23,14 @@ import jakarta.persistence.TupleElement; import jakarta.persistence.TypedQuery; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + import org.springframework.core.convert.converter.Converter; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.EntityGraph; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 66f3886677..2294ee18f4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -34,6 +34,7 @@ * @author Tom Hombergs * @author David Madden * @author Mark Paluch + * @author Diego Krupitza */ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { @@ -55,8 +56,8 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { * @param parser must not be {@literal null}. */ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, - @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { + @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, + SpelExpressionParser parser) { super(method, em); @@ -65,10 +66,12 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri Assert.notNull(parser, "Parser must not be null!"); this.evaluationContextProvider = evaluationContextProvider; - this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser); + this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser, + method.isNativeQuery()); DeclaredQuery countQuery = query.deriveCountQuery(countQueryString, method.getCountQueryProjection()); - this.countQuery = ExpressionBasedStringQuery.from(countQuery, method.getEntityInformation(), parser); + this.countQuery = ExpressionBasedStringQuery.from(countQuery, method.getEntityInformation(), parser, + method.isNativeQuery()); this.parser = parser; @@ -79,7 +82,8 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri @Override public Query doCreateQuery(JpaParametersParameterAccessor accessor) { - String sortedQueryString = QueryUtils.applySorting(query.getQueryString(), accessor.getSort(), query.getAlias()); + String sortedQueryString = QueryEnhancerFactory.forQuery(query) // + .applySorting(accessor.getSort(), query.getAlias()); ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor); Query query = createJpaQuery(sortedQueryString, processor.getReturnedType()); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java index 9aa4555549..2359260dab 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java @@ -24,6 +24,7 @@ * A wrapper for a String representation of a query offering information about the query. * * @author Jens Schauder + * @author Diego Krupitza * @since 2.0.3 */ interface DeclaredQuery { @@ -32,10 +33,11 @@ interface DeclaredQuery { * Creates a {@literal DeclaredQuery} from a query {@literal String}. * * @param query might be {@literal null} or empty. + * @param nativeQuery is a given query is native or not * @return a {@literal DeclaredQuery} instance even for a {@literal null} or empty argument. */ - static DeclaredQuery of(@Nullable String query) { - return ObjectUtils.isEmpty(query) ? EmptyDeclaredQuery.EMPTY_QUERY : new StringQuery(query); + static DeclaredQuery of(@Nullable String query, boolean nativeQuery) { + return ObjectUtils.isEmpty(query) ? EmptyDeclaredQuery.EMPTY_QUERY : new StringQuery(query, nativeQuery); } /** @@ -100,4 +102,13 @@ default boolean usesPaging() { * @since 2.0.6 */ boolean usesJdbcStyleParameters(); + + /** + * Return whether the query is a native query of not. + * + * @return true if native query otherwise false + */ + default boolean isNativeQuery() { + return false; + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java new file mode 100644 index 0000000000..7504d81356 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.springframework.data.domain.Sort; + +import java.util.Set; + +/** + * The implementation of {@link QueryEnhancer} using {@link QueryUtils}. + * + * @author Diego Krupitza + */ +public class DefaultQueryEnhancer implements QueryEnhancer { + + private final DeclaredQuery query; + + public DefaultQueryEnhancer(DeclaredQuery query) { + this.query = query; + } + + @Override + public String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable idAttributes) { + return QueryUtils.getExistsQueryString(entityName, countQueryPlaceHolder, idAttributes); + } + + @Override + public String getQueryString(String template, String entityName) { + return QueryUtils.getQueryString(template, entityName); + } + + @Override + public String applySorting(Sort sort, String alias) { + return QueryUtils.applySorting(this.query.getQueryString(), sort, alias); + } + + @Override + public String detectAlias() { + return QueryUtils.detectAlias(this.query.getQueryString()); + } + + @Override + public String createCountQueryFor(String countProjection) { + return QueryUtils.createCountQueryFor(this.query.getQueryString(), countProjection); + } + + @Override + public String getProjection() { + return QueryUtils.getProjection(this.query.getQueryString()); + } + + @Override + public Set getJoinAliases() { + return QueryUtils.getOuterJoinAliases(this.query.getQueryString()); + } + + @Override + public DeclaredQuery getQuery() { + return this.query; + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java index cb4e18bda8..725719785d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java @@ -69,7 +69,7 @@ public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable Str Assert.hasText(countQuery, "CountQuery must not be empty!"); - return DeclaredQuery.of(countQuery); + return DeclaredQuery.of(countQuery, false); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index eb773c1986..74fffaa869 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -36,6 +36,7 @@ * @author Oliver Gierke * @author Tom Hombergs * @author Michael J. Simons + * @author Diego Krupitza */ class ExpressionBasedStringQuery extends StringQuery { @@ -55,9 +56,11 @@ class ExpressionBasedStringQuery extends StringQuery { * @param query must not be {@literal null} or empty. * @param metadata must not be {@literal null}. * @param parser must not be {@literal null}. + * @param nativeQuery is a given query is native or not */ - public ExpressionBasedStringQuery(String query, JpaEntityMetadata metadata, SpelExpressionParser parser) { - super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser)); + public ExpressionBasedStringQuery(String query, JpaEntityMetadata metadata, SpelExpressionParser parser, + boolean nativeQuery) { + super(renderQueryIfExpressionOrReturnQuery(query, metadata, parser), nativeQuery && !containsExpression(query)); } /** @@ -66,11 +69,12 @@ public ExpressionBasedStringQuery(String query, JpaEntityMetadata metadata, S * @param query the original query. Must not be {@literal null}. * @param metadata the {@link JpaEntityMetadata} for the given entity. Must not be {@literal null}. * @param parser Parser for resolving SpEL expressions. Must not be {@literal null}. + * @param nativeQuery * @return A query supporting SpEL expressions. */ - static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata metadata, - SpelExpressionParser parser) { - return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser); + static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata metadata, SpelExpressionParser parser, + boolean nativeQuery) { + return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser, nativeQuery); } /** 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 new file mode 100644 index 0000000000..220ddc868c --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java @@ -0,0 +1,347 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.Function; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.select.*; +import net.sf.jsqlparser.util.SelectUtils; +import org.springframework.data.domain.Sort; +import org.springframework.data.util.Streamable; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.*; +import java.util.stream.Collectors; + +import static org.springframework.data.jpa.repository.query.JSqlParserUtils.*; +import static org.springframework.data.jpa.repository.query.QueryUtils.checkSortExpression; + +/** + * The implementation of {@link QueryEnhancer} using JSqlParser. + * + * @author Diego Krupitza + */ +public class JSqlParserQueryEnhancer implements QueryEnhancer { + + private static final String DEFAULT_TABLE_ALIAS = "x"; + + private final DeclaredQuery query; + + /** + * @param query the query we want to enhance. Must not be {@literal null}. + */ + public JSqlParserQueryEnhancer(DeclaredQuery query) { + this.query = query; + } + + @Override + public String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable idAttributes) { + final Table tableNameWithAlias = getTableWithAlias(entityName, DEFAULT_TABLE_ALIAS); + Function jSqlCount = getJSqlCount(Collections.singletonList(countQueryPlaceHolder), false); + + Select select = SelectUtils.buildSelectFromTableAndSelectItems(tableNameWithAlias, + new SelectExpressionItem(jSqlCount)); + + PlainSelect selectBody = (PlainSelect) select.getSelectBody(); + + List equalityExpressions = Streamable.of(idAttributes).stream() // + .map(field -> { + Expression tableNameField = new Column().withTable(tableNameWithAlias).withColumnName(field); + Expression inputField = new Column(":".concat(field)); + return new EqualsTo(tableNameField, inputField); + }).collect(Collectors.toList()); + + if (equalityExpressions.size() > 1) { + AndExpression rootOfWhereClause = concatenateWithAndExpression(equalityExpressions); + selectBody.setWhere(rootOfWhereClause); + } else if (equalityExpressions.size() == 1) { + selectBody.setWhere(equalityExpressions.get(0)); + } + + return selectBody.toString(); + } + + @Override + public String getQueryString(String template, String entityName) { + Assert.hasText(entityName, "Entity name must not be null or empty!"); + return String.format(template, entityName); + } + + @Override + public String applySorting(Sort sort, String alias) { + String queryString = query.getQueryString(); + Assert.hasText(queryString, "Query must not be null or empty!"); + + if (sort.isUnsorted()) { + return queryString; + } + + Select selectStatement = parseSelectStatement(queryString); + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); + + final Set joinAliases = getJoinAliases(selectBody); + + final Set selectionAliases = getSelectionAliases(selectBody); + + List orderByElements = sort.stream() // + .map(order -> getOrderClause(joinAliases, selectionAliases, alias, order)) // + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(selectBody.getOrderByElements())) { + selectBody.setOrderByElements(new ArrayList<>()); + } + + selectBody.getOrderByElements().addAll(orderByElements); + + return selectBody.toString(); + + } + + /** + * Returns the aliases used inside the selection part in the query. + * + * @param selectBody a {@link PlainSelect} containing a query. Must not be {@literal null}. + * @return a {@literal Set} containing all found aliases. Guaranteed to be not {@literal null}. + */ + private Set getSelectionAliases(PlainSelect selectBody) { + + if (CollectionUtils.isEmpty(selectBody.getSelectItems())) { + return new HashSet<>(); + } + + return selectBody.getSelectItems().stream() // + .filter(SelectExpressionItem.class::isInstance) // + .map(item -> ((SelectExpressionItem) item).getAlias()) // + .filter(Objects::nonNull) // + .map(Alias::getName) // + .collect(Collectors.toSet()); + } + + /** + * Returns the aliases used inside the selection part in the query. + * + * @return a {@literal Set} containing all found aliases. Guaranteed to be not {@literal null}. + */ + Set getSelectionAliases() { + Select selectStatement = parseSelectStatement(this.query.getQueryString()); + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); + return this.getSelectionAliases(selectBody); + } + + /** + * Returns the aliases used for {@code join}s. + * + * @param query a query string to extract the aliases of joins from. Must not be {@literal null}. + * @return a {@literal Set} of aliases used in the query. Guaranteed to be not {@literal null}. + */ + private Set getJoinAliases(String query) { + return getJoinAliases((PlainSelect) parseSelectStatement(query).getSelectBody()); + } + + /** + * Returns the aliases used for {@code join}s. + * + * @param selectBody the selection body to extract the aliases of joins from. Must not be {@literal null}. + * @return a {@literal Set} of aliases used in the query. Guaranteed to be not {@literal null}. + */ + private Set getJoinAliases(PlainSelect selectBody) { + + if (CollectionUtils.isEmpty(selectBody.getJoins())) { + return new HashSet<>(); + } + + return selectBody.getJoins().stream() // + .map(join -> join.getRightItem().getAlias()) // + .filter(Objects::nonNull) // + .map(Alias::getName) // + .collect(Collectors.toSet()); + } + + /** + * Returns the order clause for the given {@link Sort.Order}. Will prefix the clause with the given alias if the + * referenced property refers to a join alias, i.e. starts with {@code $alias.}. + * + * @param joinAliases the join aliases of the original query. Must not be {@literal null}. + * @param alias the alias for the root entity. May be {@literal null}. + * @param order the order object to build the clause for. Must not be {@literal null}. + * @return a {@link OrderByElement} containing an order clause. Guaranteed to be not {@literal null}. + */ + private OrderByElement getOrderClause(final Set joinAliases, final Set selectionAliases, + final String alias, final Sort.Order order) { + + final OrderByElement orderByElement = new OrderByElement(); + orderByElement.setAsc(order.getDirection().isAscending()); + orderByElement.setAscDescPresent(true); + + final String property = order.getProperty(); + + checkSortExpression(order); + + if (selectionAliases.contains(property)) { + Expression orderExpression = order.isIgnoreCase() ? getJSqlLower(property) : new Column(property); + + orderByElement.setExpression(orderExpression); + return orderByElement; + } + + boolean qualifyReference = joinAliases // + .parallelStream() // + .map(joinAlias -> joinAlias.concat(".")) // + .noneMatch(property::startsWith); + + boolean functionIndicator = property.contains("("); + + String reference = qualifyReference && !functionIndicator && StringUtils.hasText(alias) + ? String.format("%s.%s", alias, property) + : property; + Expression orderExpression = order.isIgnoreCase() ? getJSqlLower(reference) : new Column(reference); + orderByElement.setExpression(orderExpression); + return orderByElement; + } + + @Override + public String detectAlias() { + return detectAlias(this.query.getQueryString()); + } + + /** + * Resolves the alias for the entity to be retrieved from the given JPA query. Note that you only provide valid Query + * strings. Things such as from User u will throw an {@link IllegalArgumentException}. + * + * @param query must not be {@literal null}. + * @return Might return {@literal null}. + */ + private String detectAlias(String query) { + Select selectStatement = parseSelectStatement(query); + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); + return detectAlias(selectBody); + } + + /** + * Resolves the alias for the entity to be retrieved from the given {@link PlainSelect}. Note that you only provide + * valid Query strings. Things such as from User u will throw an {@link IllegalArgumentException}. + * + * @param selectBody must not be {@literal null}. + * @return Might return {@literal null}. + */ + private static String detectAlias(PlainSelect selectBody) { + Alias alias = selectBody.getFromItem().getAlias(); + return alias == null ? null : alias.getName(); + } + + @Override + public String createCountQueryFor(String countProjection) { + + Assert.hasText(this.query.getQueryString(), "OriginalQuery must not be null or empty!"); + + Select selectStatement = parseSelectStatement(this.query.getQueryString()); + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); + + // remove order by + selectBody.setOrderByElements(null); + + if (StringUtils.hasText(countProjection)) { + Function jSqlCount = getJSqlCount(Collections.singletonList(countProjection), false); + selectBody.setSelectItems(Collections.singletonList(new SelectExpressionItem(jSqlCount))); + return selectBody.toString(); + } + + boolean distinct = selectBody.getDistinct() != null; + selectBody.setDistinct(null); // reset possible distinct + + String tableAlias = detectAlias(selectBody); + + // is never null + List selectItems = selectBody.getSelectItems(); + + if (onlyASingleColumnProjection(selectItems)) { + SelectExpressionItem singleProjection = (SelectExpressionItem) selectItems.get(0); + + Column column = (Column) singleProjection.getExpression(); + String countProp = column.getFullyQualifiedName(); + + Function jSqlCount = getJSqlCount(Collections.singletonList(countProp), distinct); + selectBody.setSelectItems(Collections.singletonList(new SelectExpressionItem(jSqlCount))); + return selectBody.toString(); + } + + String countProp = tableAlias == null ? "*" : tableAlias; + + Function jSqlCount = getJSqlCount(Collections.singletonList(countProp), distinct); + selectBody.setSelectItems(Collections.singletonList(new SelectExpressionItem(jSqlCount))); + + return selectBody.toString(); + + } + + @Override + public String getProjection() { + Assert.hasText(query.getQueryString(), "Query must not be null or empty!"); + + Select selectStatement = parseSelectStatement(query.getQueryString()); + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); + + return selectBody.getSelectItems() // + .stream() // + .map(Object::toString) // + .collect(Collectors.joining(", ")).trim(); + } + + @Override + public Set getJoinAliases() { + return this.getJoinAliases(this.query.getQueryString()); + } + + /** + * Parses a query string with JSqlParser. + * + * @param query the query to parse + * @return the parsed query + */ + private static Select parseSelectStatement(String query) { + try { + return (Select) CCJSqlParserUtil.parse(query); + } catch (JSQLParserException e) { + throw new IllegalArgumentException("The query you provided is not a valid SQL Query!", e); + } + } + + /** + * Checks whether a given projection only contains a single column definition (aka without functions, etc) + * + * @param projection the projection to analyse + * @return true when the projection only contains a single column definition otherwise false + */ + private boolean onlyASingleColumnProjection(List projection) { + // this is unfortunately the only way to check without any hacky & hard string regex magic + return projection.size() == 1 && projection.get(0) instanceof SelectExpressionItem + && (((SelectExpressionItem) projection.get(0)).getExpression()) instanceof Column; + } + + @Override + public DeclaredQuery getQuery() { + return this.query; + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java new file mode 100644 index 0000000000..1cc4eb1d6a --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java @@ -0,0 +1,123 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.Function; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * A utility class for JSqlParser. + * + * @author Diego Krupitza + */ +public final class JSqlParserUtils { + + private JSqlParserUtils() { + } + + /** + * Generates a JSqlParser table from an entity name and an optional alias name + * + * @param entityName the name of the table + * @param alias the optional alias. Might be {@literal null} or empty + * @return the newly generated table + */ + public static Table getTableWithAlias(String entityName, String alias) { + Table table = new Table(entityName); + return StringUtils.hasText(alias) ? table.withAlias(new Alias(alias)) : table; + } + + /** + * Concatenates a list of expression with AND. + * + * @param expressions the list of expressions to concatenate. Has to be non empty and with size >= 2 + * @return the root of the concatenated expression + */ + public static AndExpression concatenateWithAndExpression(List expressions) { + + if (CollectionUtils.isEmpty(expressions) || expressions.size() == 1) { + throw new IllegalArgumentException( + "The list of expression has to be at least of length 2! Otherwise it is not possible to concatinate with an"); + } + + AndExpression rootAndExpression = new AndExpression(); + AndExpression currentLocation = rootAndExpression; + + // traverse the list with looking 1 element ahead + for (int i = 0; i < expressions.size(); i++) { + Expression currentExpression = expressions.get(i); + if (currentLocation.getLeftExpression() == null) { + currentLocation.setLeftExpression(currentExpression); + } else if (currentLocation.getRightExpression() == null && i == expressions.size() - 1) { + currentLocation.setRightExpression(currentExpression); + } else { + AndExpression nextAndExpression = new AndExpression(); + nextAndExpression.setLeftExpression(currentExpression); + + currentLocation.setRightExpression(nextAndExpression); + currentLocation = (AndExpression) currentLocation.getRightExpression(); + } + } + + return rootAndExpression; + } + + /** + * Generates a count function call, based on the {@code countFields}. + * + * @param countFields the non-empty list of fields that are used for counting + * @param distinct if it should be a distinct count + * @return the generated count function call + */ + public static Function getJSqlCount(final List countFields, final boolean distinct) { + List countColumns = countFields // + .stream() // + .map(Column::new) // + .collect(Collectors.toList()); + + ExpressionList countExpression = new ExpressionList(countColumns); + return new Function() // + .withName("count") // + .withParameters(countExpression) // + .withDistinct(distinct); + } + + /** + * Generates a lower function call, based on the {@code column}. + * + * @param column the non-empty column to use as param for lower + * @return the generated lower function call + */ + public static Function getJSqlLower(String column) { + List expressions = Collections.singletonList(new Column(column)); + ExpressionList lowerParamExpression = new ExpressionList(expressions); + return new Function() // + .withName("lower") // + .withParameters(lowerParamExpression); + } + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index ad61f599fe..4afd0a79ab 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -166,10 +166,8 @@ protected CriteriaQuery complete(@Nullable Predicate predicate Class typeToRead = returnedType.getReturnedType(); - query = typeToRead.isInterface() - ? query.multiselect(selections) - : query.select((Selection) builder.construct(typeToRead, - selections.toArray(new Selection[0]))); + query = typeToRead.isInterface() ? query.multiselect(selections) + : query.select((Selection) builder.construct(typeToRead, selections.toArray(new Selection[0]))); } else if (tree.isExistsProjection()) { @@ -241,83 +239,83 @@ public Predicate build() { Type type = part.getType(); switch (type) { - case BETWEEN: - ParameterMetadata first = provider.next(part); - ParameterMetadata second = provider.next(part); - return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression()); - case AFTER: - case GREATER_THAN: - return builder.greaterThan(getComparablePath(root, part), - provider.next(part, Comparable.class).getExpression()); - case GREATER_THAN_EQUAL: - return builder.greaterThanOrEqualTo(getComparablePath(root, part), - provider.next(part, Comparable.class).getExpression()); - case BEFORE: - case LESS_THAN: - return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression()); - case LESS_THAN_EQUAL: - return builder.lessThanOrEqualTo(getComparablePath(root, part), - provider.next(part, Comparable.class).getExpression()); - case IS_NULL: - return getTypedPath(root, part).isNull(); - case IS_NOT_NULL: - return getTypedPath(root, part).isNotNull(); - case NOT_IN: - // cast required for eclipselink workaround, see DATAJPA-433 - return upperIfIgnoreCase(getTypedPath(root, part)) - .in((Expression>) provider.next(part, Collection.class).getExpression()).not(); - case IN: - // cast required for eclipselink workaround, see DATAJPA-433 - return upperIfIgnoreCase(getTypedPath(root, part)) - .in((Expression>) provider.next(part, Collection.class).getExpression()); - case STARTING_WITH: - case ENDING_WITH: - case CONTAINING: - case NOT_CONTAINING: - - if (property.getLeafProperty().isCollection()) { - - Expression> propertyExpression = traversePath(root, property); - ParameterExpression parameterExpression = provider.next(part).getExpression(); - - // Can't just call .not() in case of negation as EclipseLink chokes on that. - return type.equals(NOT_CONTAINING) ? isNotMember(builder, parameterExpression, propertyExpression) - : isMember(builder, parameterExpression, propertyExpression); - } - - case LIKE: - case NOT_LIKE: - Expression stringPath = getTypedPath(root, part); - Expression propertyExpression = upperIfIgnoreCase(stringPath); - Expression parameterExpression = upperIfIgnoreCase(provider.next(part, String.class).getExpression()); - Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter()); - return type.equals(NOT_LIKE) || type.equals(NOT_CONTAINING) ? like.not() : like; - case TRUE: - Expression truePath = getTypedPath(root, part); - return builder.isTrue(truePath); - case FALSE: - Expression falsePath = getTypedPath(root, part); - return builder.isFalse(falsePath); - case SIMPLE_PROPERTY: - ParameterMetadata expression = provider.next(part); - Expression path = getTypedPath(root, part); - return expression.isIsNullParameter() ? path.isNull() - : builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression())); - case NEGATING_SIMPLE_PROPERTY: - return builder.notEqual(upperIfIgnoreCase(getTypedPath(root, part)), - upperIfIgnoreCase(provider.next(part).getExpression())); - case IS_EMPTY: - case IS_NOT_EMPTY: - - if (!property.getLeafProperty().isCollection()) { - throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties!"); - } - - Expression> collectionPath = traversePath(root, property); - return type.equals(IS_NOT_EMPTY) ? builder.isNotEmpty(collectionPath) : builder.isEmpty(collectionPath); - - default: - throw new IllegalArgumentException("Unsupported keyword " + type); + case BETWEEN: + ParameterMetadata first = provider.next(part); + ParameterMetadata second = provider.next(part); + return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression()); + case AFTER: + case GREATER_THAN: + return builder.greaterThan(getComparablePath(root, part), + provider.next(part, Comparable.class).getExpression()); + case GREATER_THAN_EQUAL: + return builder.greaterThanOrEqualTo(getComparablePath(root, part), + provider.next(part, Comparable.class).getExpression()); + case BEFORE: + case LESS_THAN: + return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression()); + case LESS_THAN_EQUAL: + return builder.lessThanOrEqualTo(getComparablePath(root, part), + provider.next(part, Comparable.class).getExpression()); + case IS_NULL: + return getTypedPath(root, part).isNull(); + case IS_NOT_NULL: + return getTypedPath(root, part).isNotNull(); + case NOT_IN: + // cast required for eclipselink workaround, see DATAJPA-433 + return upperIfIgnoreCase(getTypedPath(root, part)) + .in((Expression>) provider.next(part, Collection.class).getExpression()).not(); + case IN: + // cast required for eclipselink workaround, see DATAJPA-433 + return upperIfIgnoreCase(getTypedPath(root, part)) + .in((Expression>) provider.next(part, Collection.class).getExpression()); + case STARTING_WITH: + case ENDING_WITH: + case CONTAINING: + case NOT_CONTAINING: + + if (property.getLeafProperty().isCollection()) { + + Expression> propertyExpression = traversePath(root, property); + ParameterExpression parameterExpression = provider.next(part).getExpression(); + + // Can't just call .not() in case of negation as EclipseLink chokes on that. + return type.equals(NOT_CONTAINING) ? isNotMember(builder, parameterExpression, propertyExpression) + : isMember(builder, parameterExpression, propertyExpression); + } + + case LIKE: + case NOT_LIKE: + Expression stringPath = getTypedPath(root, part); + Expression propertyExpression = upperIfIgnoreCase(stringPath); + Expression parameterExpression = upperIfIgnoreCase(provider.next(part, String.class).getExpression()); + Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter()); + return type.equals(NOT_LIKE) || type.equals(NOT_CONTAINING) ? like.not() : like; + case TRUE: + Expression truePath = getTypedPath(root, part); + return builder.isTrue(truePath); + case FALSE: + Expression falsePath = getTypedPath(root, part); + return builder.isFalse(falsePath); + case SIMPLE_PROPERTY: + ParameterMetadata expression = provider.next(part); + Expression path = getTypedPath(root, part); + return expression.isIsNullParameter() ? path.isNull() + : builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression())); + case NEGATING_SIMPLE_PROPERTY: + return builder.notEqual(upperIfIgnoreCase(getTypedPath(root, part)), + upperIfIgnoreCase(provider.next(part).getExpression())); + case IS_EMPTY: + case IS_NOT_EMPTY: + + if (!property.getLeafProperty().isCollection()) { + throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties!"); + } + + Expression> collectionPath = traversePath(root, property); + return type.equals(IS_NOT_EMPTY) ? builder.isNotEmpty(collectionPath) : builder.isEmpty(collectionPath); + + default: + throw new IllegalArgumentException("Unsupported keyword " + type); } } @@ -342,22 +340,22 @@ private Expression upperIfIgnoreCase(Expression expression) switch (part.shouldIgnoreCase()) { - case ALWAYS: + case ALWAYS: - Assert.state(canUpperCase(expression), "Unable to ignore case of " + expression.getJavaType().getName() - + " types, the property '" + part.getProperty().getSegment() + "' must reference a String"); - return (Expression) builder.upper((Expression) expression); + Assert.state(canUpperCase(expression), "Unable to ignore case of " + expression.getJavaType().getName() + + " types, the property '" + part.getProperty().getSegment() + "' must reference a String"); + return (Expression) builder.upper((Expression) expression); - case WHEN_POSSIBLE: + case WHEN_POSSIBLE: - if (canUpperCase(expression)) { - return (Expression) builder.upper((Expression) expression); - } + if (canUpperCase(expression)) { + return (Expression) builder.upper((Expression) expression); + } - case NEVER: - default: + case NEVER: + default: - return (Expression) expression; + return (Expression) expression; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index c44bf7eb76..2c998d4db4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -155,7 +155,7 @@ private void assertParameterNamesInAnnotatedQuery() { String annotatedQuery = getAnnotatedQuery(); - if (!DeclaredQuery.of(annotatedQuery).hasNamedParameter()) { + if (!DeclaredQuery.of(annotatedQuery, this.isNativeQuery.get()).hasNamedParameter()) { return; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 73f7763a96..e79a39a3e8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -78,7 +78,7 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { Query query = em.createNamedQuery(queryName); String queryString = extractor.extractQueryString(query); - this.declaredQuery = DeclaredQuery.of(queryString); + this.declaredQuery = DeclaredQuery.of(queryString, false); boolean weNeedToCreateCountQuery = !namedCountQueryIsPresent && method.getParameters().hasPageableParameter(); boolean cantExtractQuery = !this.extractor.canExtractQuery(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java new file mode 100644 index 0000000000..b8fba6e998 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java @@ -0,0 +1,116 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.Set; + +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; + +/** + * This interface describes the API for enhancing a given Query. + * + * @author Diego Krupitza + */ +public interface QueryEnhancer { + + /** + * Returns the query string to execute an exists query for the given id attributes. + * + * @param entityName the name of the entity to create the query for, must not be {@literal null}. + * @param countQueryPlaceHolder the placeholder for the count clause, must not be {@literal null}. + * @param idAttributes the id attributes for the entity, must not be {@literal null}. + */ + String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable idAttributes); + + /** + * Returns the query string for the given class name. + * + * @param template must not be {@literal null}. + * @param entityName must not be {@literal null}. + * @return the template with placeholders replaced by the {@literal entityName}. Guaranteed to be not {@literal null}. + */ + String getQueryString(String template, String entityName); + + /** + * Adds {@literal order by} clause to the JPQL query. Uses the first alias to bind the sorting property to. + * + * @param sort the sort specification to apply. + * @return the modified query string. + */ + default String applySorting(Sort sort) { + return applySorting(sort, detectAlias()); + } + + /** + * Adds {@literal order by} clause to the JPQL query. + * + * @param sort the sort specification to apply. + * @param alias the alias to be used in the order by clause. May be {@literal null} or empty. + * @return the modified query string. + */ + String applySorting(Sort sort, @Nullable String alias); + + /** + * Resolves the alias for the entity to be retrieved from the given JPA query. + * + * @return Might return {@literal null}. + */ + @Nullable + String detectAlias(); + + /** + * Creates a count projected query from the given original query. + * + * @return Guaranteed to be not {@literal null}. + */ + default String createCountQueryFor() { + return createCountQueryFor(null); + } + + /** + * Creates a count projected query from the given original query using the provided countProjection. + * + * @param countProjection may be {@literal null}. + * @return a query String to be used a count query for pagination. Guaranteed to be not {@literal null}. + */ + String createCountQueryFor(@Nullable String countProjection); + + /** + * Returns whether the given JPQL query contains a constructor expression. + * + * @return whether the given JPQL query contains a constructor expression. + */ + default boolean hasConstructorExpression() { + return QueryUtils.hasConstructorExpression(getQuery().getQueryString()); + } + + /** + * Returns the projection part of the query, i.e. everything between {@code select} and {@code from}. + * + * @return the projection part of the query. + */ + String getProjection(); + + Set getJoinAliases(); + + /** + * Gets the query we want to use for enhancements. + * + * @return non null {@link DeclaredQuery} that wraps the query + */ + DeclaredQuery getQuery(); +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java new file mode 100644 index 0000000000..d6123ddee5 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Encapsulates different strategies for the creation of a {@link QueryEnhancer} from a {@link DeclaredQuery}. + * + * @author Diego Krupitza + */ +public final class QueryEnhancerFactory { + + private static final Log LOG = LogFactory.getLog(QueryEnhancerFactory.class); + + private static final boolean JSQLPARSER_IN_CLASSPATH = isJSqlParserInClassPath(); + + private QueryEnhancerFactory() { + } + + /** + * Creates a new {@link QueryEnhancer} for the given {@link DeclaredQuery}. + * + * @param query must not be {@literal null}. + * @return an implementation of {@link QueryEnhancer} that suits the query the most + */ + public static QueryEnhancer forQuery(DeclaredQuery query) { + if (qualifiesForJSqlParserUsage(query)) { + return new JSqlParserQueryEnhancer(query); + } else { + return new DefaultQueryEnhancer(query); + } + } + + /** + * Checks if a given query can be process with the JSqlParser under the condition that the parser is in the classpath. + * + * @param query the query we want to check + * @return true if JSqlParser is in the classpath and the query is classified as a native query otherwise + * false + */ + private static boolean qualifiesForJSqlParserUsage(DeclaredQuery query) { + return JSQLPARSER_IN_CLASSPATH && query.isNativeQuery(); + } + + /** + * Checks whether JSqlParser is in classpath or not. + * + * @return true when in classpath otherwise false + */ + private static boolean isJSqlParserInClassPath() { + try { + Class.forName("net.sf.jsqlparser.parser.JSqlParser", false, QueryEnhancerFactory.class.getClassLoader()); + LOG.info("JSqlParser is in classpath. If applicable JSqlParser will be used."); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index 1defa4a3f4..d14fea67e8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -49,7 +49,8 @@ interface QueryParameterSetter { void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling); /** Noop implementation */ - QueryParameterSetter NOOP = (query, values, errorHandling) -> {}; + QueryParameterSetter NOOP = (query, values, errorHandling) -> { + }; /** * {@link QueryParameterSetter} for named or indexed parameters that might have a {@link TemporalType} specified. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 28bf1c7dcf..dc20aad839 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -56,7 +56,7 @@ import org.springframework.util.StringUtils; /** - * Simple utility class to create JPA queries. + * Simple utility class to create JPA queries using the default implementation of a custom parser. * * @author Oliver Gierke * @author Kevin Raymond @@ -74,6 +74,7 @@ * @author Andriy Redko * @author Peter Großmann * @author Greg Turnquist + * @author Diego Krupitza */ public abstract class QueryUtils { @@ -282,7 +283,7 @@ public static String applySorting(String query, Sort sort, @Nullable String alia * @param joinAliases the join aliases of the original query. Must not be {@literal null}. * @param alias the alias for the root entity. May be {@literal null}. * @param order the order object to build the clause for. Must not be {@literal null}. - * @return a String containing a order clause. Guaranteed to be not {@literal null}. + * @return a String containing an order clause. Guaranteed to be not {@literal null}. */ private static String getOrderClause(Set joinAliases, Set selectionAlias, @Nullable String alias, Order order) { @@ -811,7 +812,7 @@ private static boolean isAlreadyInnerJoined(From from, String attribute) { * * @param order */ - private static void checkSortExpression(Order order) { + static void checkSortExpression(Order order) { if (order instanceof JpaOrder && ((JpaOrder) order).isUnsafe()) { return; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index fef47a6f9f..37f3af731a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -46,6 +46,7 @@ * @author Oliver Wehrens * @author Mark Paluch * @author Jens Schauder + * @author Diego Krupitza */ class StringQuery implements DeclaredQuery { @@ -55,6 +56,8 @@ class StringQuery implements DeclaredQuery { private final boolean hasConstructorExpression; private final boolean containsPageableInSpel; private final boolean usesJdbcStyleParameters; + private final boolean isNative; + private final QueryEnhancer queryEnhancer; /** * Creates a new {@link StringQuery} from the given JPQL query. @@ -62,10 +65,11 @@ class StringQuery implements DeclaredQuery { * @param query must not be {@literal null} or empty. */ @SuppressWarnings("deprecation") - StringQuery(String query) { + StringQuery(String query, boolean isNative) { Assert.hasText(query, "Query must not be null or empty!"); + this.isNative = isNative; this.bindings = new ArrayList<>(); this.containsPageableInSpel = query.contains("#pageable"); @@ -74,8 +78,10 @@ class StringQuery implements DeclaredQuery { this.bindings, queryMeta); this.usesJdbcStyleParameters = queryMeta.usesJdbcStyleParameters; - this.alias = QueryUtils.detectAlias(query); - this.hasConstructorExpression = QueryUtils.hasConstructorExpression(query); + + this.queryEnhancer = QueryEnhancerFactory.forQuery(this); + this.alias = this.queryEnhancer.detectAlias(); + this.hasConstructorExpression = this.queryEnhancer.hasConstructorExpression(); } /** @@ -86,7 +92,7 @@ boolean hasParameterBindings() { } String getProjection() { - return QueryUtils.getProjection(query); + return this.queryEnhancer.getProjection(); } @Override @@ -98,8 +104,9 @@ public List getParameterBindings() { @SuppressWarnings("deprecation") public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable String countQueryProjection) { - return DeclaredQuery - .of(countQuery != null ? countQuery : QueryUtils.createCountQueryFor(query, countQueryProjection)); + return DeclaredQuery.of( + countQuery != null ? countQuery : this.queryEnhancer.createCountQueryFor(countQueryProjection), // + this.isNative); } @Override @@ -138,6 +145,11 @@ public boolean usesPaging() { return containsPageableInSpel; } + @Override + public boolean isNativeQuery() { + return isNative; + } + /** * A parser that extracts the parameter bindings from a given query string. * @@ -227,7 +239,8 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St Integer parameterIndex = getParameterIndex(parameterIndexString); String typeSource = matcher.group(COMPARISION_TYPE_GROUP); - Assert.isTrue(parameterIndexString != null || parameterName != null, () -> String.format("We need either a name or an index! Offending query string: %s", query)); + Assert.isTrue(parameterIndexString != null || parameterName != null, + () -> String.format("We need either a name or an index! Offending query string: %s", query)); String expression = spelExtractor.getParameter(parameterName == null ? parameterIndexString : parameterName); String replacement = null; @@ -246,36 +259,36 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St switch (ParameterBindingType.of(typeSource)) { - case LIKE: + case LIKE: - Type likeType = LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); - replacement = matcher.group(3); + Type likeType = LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); + replacement = matcher.group(3); - if (parameterIndex != null) { - checkAndRegister(new LikeParameterBinding(parameterIndex, likeType, expression), bindings); - } else { - checkAndRegister(new LikeParameterBinding(parameterName, likeType, expression), bindings); + if (parameterIndex != null) { + checkAndRegister(new LikeParameterBinding(parameterIndex, likeType, expression), bindings); + } else { + checkAndRegister(new LikeParameterBinding(parameterName, likeType, expression), bindings); - replacement = ":" + parameterName; - } + replacement = ":" + parameterName; + } - break; + break; - case IN: + case IN: - if (parameterIndex != null) { - checkAndRegister(new InParameterBinding(parameterIndex, expression), bindings); - } else { - checkAndRegister(new InParameterBinding(parameterName, expression), bindings); - } + if (parameterIndex != null) { + checkAndRegister(new InParameterBinding(parameterIndex, expression), bindings); + } else { + checkAndRegister(new InParameterBinding(parameterName, expression), bindings); + } - break; + break; - case AS_IS: // fall-through we don't need a special parameter binding for the given parameter. - default: + case AS_IS: // fall-through we don't need a special parameter binding for the given parameter. + default: - bindings.add(parameterIndex != null ? new ParameterBinding(null, parameterIndex, expression) - : new ParameterBinding(parameterName, null, expression)); + bindings.add(parameterIndex != null ? new ParameterBinding(null, parameterIndex, expression) + : new ParameterBinding(parameterName, null, expression)); } if (replacement != null) { 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 ba8affb9fd..b9b7e1cf25 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 @@ -103,10 +103,12 @@ @Transactional public class UserRepositoryTests { - @PersistenceContext EntityManager em; + @PersistenceContext + EntityManager em; // CUT - @Autowired UserRepository repository; + @Autowired + UserRepository repository; // Test fixture private User firstUser; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java new file mode 100644 index 0000000000..ec08e3ea9d --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java @@ -0,0 +1,530 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jpa.repository.query.QueryUtils.*; + +import java.util.Collections; +import java.util.Set; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.data.jpa.domain.JpaSort; + +/** + * Unit test for {@link QueryUtils}. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @author Komi Innocent + * @author Christoph Strobl + * @author Jens Schauder + * @author Florian Lüdiger + * @author Grégoire Druant + * @author Mohammad Hewedy + * @author Greg Turnquist + */ +class DefaultQueryUtilsUnitTests { + + private static final String QUERY = "select u from User u"; + private static final String FQ_QUERY = "select u from org.acme.domain.User$Foo_Bar u"; + private static final String SIMPLE_QUERY = "from User u"; + private static final String COUNT_QUERY = "select count(u) from User u"; + + private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?"; + + @Test + void createsCountQueryCorrectly() { + assertCountQuery(QUERY, COUNT_QUERY); + } + + @Test + void createsCountQueriesCorrectlyForCapitalLetterJPQL() { + + assertCountQuery("FROM User u WHERE u.foo.bar = ?", "select count(u) FROM User u WHERE u.foo.bar = ?"); + + assertCountQuery("SELECT u FROM User u where u.foo.bar = ?", "select count(u) FROM User u where u.foo.bar = ?"); + } + + @Test + void createsCountQueryForDistinctQueries() { + + assertCountQuery("select distinct u from User u where u.foo = ?", + "select count(distinct u) from User u where u.foo = ?"); + } + + @Test + void createsCountQueryForConstructorQueries() { + + assertCountQuery("select distinct new User(u.name) from User u where u.foo = ?", + "select count(distinct u) from User u where u.foo = ?"); + } + + @Test + void createsCountQueryForJoins() { + + assertCountQuery("select distinct new User(u.name) from User u left outer join u.roles r WHERE r = ?", + "select count(distinct u) from User u left outer join u.roles r WHERE r = ?"); + } + + @Test + void createsCountQueryForQueriesWithSubSelects() { + + assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role)", + "select count(u) from User u left outer join u.roles r where r in (select r from Role)"); + } + + @Test + void createsCountQueryForAliasesCorrectly() { + + assertCountQuery("select u from User as u", "select count(u) from User as u"); + } + + @Test + void allowsShortJpaSyntax() { + + assertCountQuery(SIMPLE_QUERY, COUNT_QUERY); + } + + @Test + void detectsAliasCorrectly() { + + assertThat(detectAlias(QUERY)).isEqualTo("u"); + assertThat(detectAlias(SIMPLE_QUERY)).isEqualTo("u"); + assertThat(detectAlias(COUNT_QUERY)).isEqualTo("u"); + assertThat(detectAlias(QUERY_WITH_AS)).isEqualTo("u"); + assertThat(detectAlias("SELECT FROM USER U")).isEqualTo("U"); + assertThat(detectAlias("select u from User u")).isEqualTo("u"); + assertThat(detectAlias("select u from com.acme.User u")).isEqualTo("u"); + assertThat(detectAlias("select u from T05User u")).isEqualTo("u"); + } + + @Test + void allowsFullyQualifiedEntityNamesInQuery() { + + assertThat(detectAlias(FQ_QUERY)).isEqualTo("u"); + assertCountQuery(FQ_QUERY, "select count(u) from org.acme.domain.User$Foo_Bar u"); + } + + @Test // DATAJPA-252 + void detectsJoinAliasesCorrectly() { + + Set aliases = getOuterJoinAliases("select p from Person p left outer join x.foo b2_$ar where …"); + assertThat(aliases).hasSize(1); + assertThat(aliases).contains("b2_$ar"); + + aliases = getOuterJoinAliases("select p from Person p left join x.foo b2_$ar where …"); + assertThat(aliases).hasSize(1); + assertThat(aliases).contains("b2_$ar"); + + aliases = getOuterJoinAliases( + "select p from Person p left outer join x.foo as b2_$ar, left join x.bar as foo where …"); + assertThat(aliases).hasSize(2); + assertThat(aliases).contains("b2_$ar", "foo"); + + aliases = getOuterJoinAliases( + "select p from Person p left join x.foo as b2_$ar, left outer join x.bar foo where …"); + assertThat(aliases).hasSize(2); + assertThat(aliases).contains("b2_$ar", "foo"); + } + + @Test // DATAJPA-252 + void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { + + String query = "select p from Person p left join p.address address"; + assertThat(applySorting(query, Sort.by("address.city"))).endsWith("order by address.city asc"); + assertThat(applySorting(query, Sort.by("address.city", "lastname"), "p")) + .endsWith("order by address.city asc, p.lastname asc"); + } + + @Test // DATAJPA-252 + void extendsExistingOrderByClausesCorrectly() { + + String query = "select p from Person p order by p.lastname asc"; + assertThat(applySorting(query, Sort.by("firstname"), "p")).endsWith("order by p.lastname asc, p.firstname asc"); + } + + @Test // DATAJPA-296 + void appliesIgnoreCaseOrderingCorrectly() { + + Sort sort = Sort.by(Order.by("firstname").ignoreCase()); + + String query = "select p from Person p"; + assertThat(applySorting(query, sort, "p")).endsWith("order by lower(p.firstname) asc"); + } + + @Test // DATAJPA-296 + void appendsIgnoreCaseOrderingCorrectly() { + + Sort sort = Sort.by(Order.by("firstname").ignoreCase()); + + String query = "select p from Person p order by p.lastname asc"; + assertThat(applySorting(query, sort, "p")).endsWith("order by p.lastname asc, lower(p.firstname) asc"); + } + + @Test // DATAJPA-342 + void usesReturnedVariableInCOuntProjectionIfSet() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", + "select count(distinct m.genre) from Media m where m.user = ?1"); + } + + @Test // DATAJPA-343 + void projectsCOuntQueriesForQueriesWithSubselects() { + + assertCountQuery("select o from Foo o where cb.id in (select b from Bar b)", + "select count(o) from Foo o where cb.id in (select b from Bar b)"); + } + + @Test // DATAJPA-148 + void doesNotPrefixSortsIfFunction() { + + Sort sort = Sort.by("sum(foo)"); + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> applySorting("select p from Person p", sort, "p")); + } + + @Test // DATAJPA-377 + void removesOrderByInGeneratedCountQueryFromOriginalQueryIfPresent() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 OrDer By m.genre ASC", + "select count(distinct m.genre) from Media m where m.user = ?1"); + } + + @Test // DATAJPA-375 + void findsExistingOrderByIndependentOfCase() { + + Sort sort = Sort.by("lastname"); + String query = applySorting("select p from Person p ORDER BY p.firstname", sort, "p"); + assertThat(query).endsWith("ORDER BY p.firstname, p.lastname asc"); + } + + @Test // DATAJPA-409 + void createsCountQueryForNestedReferenceCorrectly() { + assertCountQuery("select a.b from A a", "select count(a.b) from A a"); + } + + @Test // DATAJPA-420 + void createsCountQueryForScalarSelects() { + assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p"); + } + + @Test // DATAJPA-456 + void createCountQueryFromTheGivenCountProjection() { + assertThat(createCountQueryFor("select p.lastname,p.firstname from Person p", "p.lastname")) + .isEqualTo("select count(p.lastname) from Person p"); + } + + @Test // DATAJPA-726 + void detectsAliassesInPlainJoins() { + + String query = "select p from Customer c join c.productOrder p where p.delayed = true"; + Sort sort = Sort.by("p.lineItems"); + + assertThat(applySorting(query, sort, "c")).endsWith("order by p.lineItems asc"); + } + + @Test // DATAJPA-736 + void supportsNonAsciiCharactersInEntityNames() { + assertThat(createCountQueryFor("select u from Usèr u")).isEqualTo("select count(u) from Usèr u"); + } + + @Test // DATAJPA-798 + void detectsAliasInQueryContainingLineBreaks() { + assertThat(detectAlias("select \n u \n from \n User \nu")).isEqualTo("u"); + } + + @Test // DATAJPA-815 + void doesPrefixPropertyWith() { + + String query = "from Cat c join Dog d"; + Sort sort = Sort.by("dPropertyStartingWithJoinAlias"); + + assertThat(applySorting(query, sort, "c")).endsWith("order by c.dPropertyStartingWithJoinAlias asc"); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionInDistinctQuery() { + assertThat(hasConstructorExpression("select distinct new Foo() from Bar b")).isTrue(); + } + + @Test // DATAJPA-938 + void detectsComplexConstructorExpression() { + + assertThat(hasConstructorExpression("select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) " // + + "from Bar lp join lp.investmentProduct ip " // + + "where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId " // + + "group by ip.id, ip.name, lp.accountId " // + + "order by ip.name ASC")).isTrue(); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionWithLineBreaks() { + assertThat(hasConstructorExpression("select new foo.bar.FooBar(\na.id) from DtoA a ")).isTrue(); + } + + @Test // DATAJPA-960 + void doesNotQualifySortIfNoAliasDetected() { + assertThat(applySorting("from mytable where ?1 is null", Sort.by("firstname"))).endsWith("order by firstname asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotAllowWhitespaceInSort() { + + Sort sort = Sort.by("case when foo then bar"); + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> applySorting("select p from Person p", sort, "p")); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixUnsageJpaSortFunctionCalls() { + + JpaSort sort = JpaSort.unsafe("sum(foo)"); + assertThat(applySorting("select p from Person p", sort, "p")).endsWith("order by sum(foo) asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixMultipleAliasedFunctionCalls() { + + String query = "SELECT AVG(m.price) AS avgPrice, SUM(m.stocks) AS sumStocks FROM Magazine m"; + Sort sort = Sort.by("avgPrice", "sumStocks"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by avgPrice asc, sumStocks asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixSingleAliasedFunctionCalls() { + + String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("avgPrice"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { + + String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("someOtherProperty"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by m.someOtherProperty asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainesAliasedFunctionForDifferentProperty() { + + String query = "SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("name", "avgPrice"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by m.name asc, avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() { + + String query = "SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m"; + Sort sort = Sort.by("trimmedName"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by trimmedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() { + + String query = "SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m"; + Sort sort = Sort.by("extendedName"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by extendedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { + + String query = "SELECT AVG(m.price) AS avg_price FROM Magazine m"; + Sort sort = Sort.by("avg_price"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by avg_price asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithDots() { + + String query = "SELECT AVG(m.price) AS m.avg FROM Magazine m"; + Sort sort = Sort.by("m.avg"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by m.avg asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpaces() { + + String query = "SELECT AVG( m.price ) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("avgPrice"); + + assertThat(applySorting(query, sort, "m")).endsWith("order by avgPrice asc"); + } + + @Test // DATAJPA-1000 + void discoversCorrectAliasForJoinFetch() { + + Set aliases = QueryUtils + .getOuterJoinAliases("SELECT DISTINCT user FROM User user LEFT JOIN FETCH user.authorities AS authority"); + + assertThat(aliases).containsExactly("authority"); + } + + @Test // DATAJPA-1171 + void doesNotContainStaticClauseInExistsQuery() { + + assertThat(QueryUtils.getExistsQueryString("entity", "x", Collections.singleton("id"))) // + .endsWith("WHERE x.id = :id"); + } + + @Test // DATAJPA-1363 + void discoversAliasWithComplexFunction() { + + assertThat(QueryUtils + .getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) // + .contains("myAlias"); + } + + @Test // DATAJPA-1506 + void detectsAliasWithGroupAndOrderBy() { + + assertThat(detectAlias("select * from User group by name")).isNull(); + assertThat(detectAlias("select * from User order by name")).isNull(); + assertThat(detectAlias("select * from User u group by name")).isEqualTo("u"); + assertThat(detectAlias("select * from User u order by name")).isEqualTo("u"); + } + + @Test // DATAJPA-1500 + void createCountQuerySupportsWhitespaceCharacters() { + + assertThat(createCountQueryFor("select * from User user\n" + // + " where user.age = 18\n" + // + " order by user.name\n ")).isEqualTo("select count(user) from User user\n" + // + " where user.age = 18\n "); + } + + @Test + void createCountQuerySupportsLineBreaksInSelectClause() { + + assertThat(createCountQueryFor("select user.age,\n" + // + " user.name\n" + // + " from User user\n" + // + " where user.age = 18\n" + // + " order\nby\nuser.name\n ")).isEqualTo("select count(user) from User user\n" + // + " where user.age = 18\n "); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFieldAliases() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("authorName"); + + String fullQuery = applySorting(query, sort); + + assertThat(fullQuery).endsWith("order by authorName asc"); + } + + @Test // GH-2280 + void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { + + String query = "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer"; + Sort sort = Sort.by(Order.by("name").ignoreCase()); + + String fullQuery = applySorting(query, sort); + + assertThat(fullQuery).isEqualTo( + "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(name) asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFunctionAliases() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("title"); + + String fullQuery = applySorting(query, sort); + + assertThat(fullQuery).endsWith("order by title asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForSimpleField() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("price"); + + String fullQuery = applySorting(query, sort); + + assertThat(fullQuery).endsWith("order by m.price asc"); + } + + @Test + void createCountQuerySupportsLineBreakRightAfterDistinct() { + + assertThat(createCountQueryFor("select\ndistinct\nuser.age,\n" + // + "user.name\n" + // + "from\nUser\nuser")).isEqualTo(createCountQueryFor("select\ndistinct user.age,\n" + // + "user.name\n" + // + "from\nUser\nuser")); + } + + @Test + void detectsAliasWithGroupAndOrderByWithLineBreaks() { + + assertThat(detectAlias("select * from User group\nby name")).isNull(); + assertThat(detectAlias("select * from User order\nby name")).isNull(); + assertThat(detectAlias("select * from User u group\nby name")).isEqualTo("u"); + assertThat(detectAlias("select * from User u order\nby name")).isEqualTo("u"); + assertThat(detectAlias("select * from User\nu\norder \n by name")).isEqualTo("u"); + } + + @Test // DATAJPA-1679 + void findProjectionClauseWithDistinct() { + + SoftAssertions.assertSoftly(sofly -> { + sofly.assertThat(QueryUtils.getProjection("select * from x")).isEqualTo("*"); + sofly.assertThat(QueryUtils.getProjection("select a, b, c from x")).isEqualTo("a, b, c"); + sofly.assertThat(QueryUtils.getProjection("select distinct a, b, c from x")).isEqualTo("a, b, c"); + sofly.assertThat(QueryUtils.getProjection("select DISTINCT a, b, c from x")).isEqualTo("a, b, c"); + }); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithSubselect() { + + // This is not a required behavior, in fact the opposite is, + // but it documents a current limitation. + // to fix this without breaking findProjectionClauseWithIncludedFrom we need a more sophisticated parser. + assertThat(QueryUtils.getProjection("select * from (select x from y)")).isNotEqualTo("*"); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithIncludedFrom() { + assertThat(QueryUtils.getProjection("select x, frommage, y from t")).isEqualTo("x, frommage, y"); + } + + private static void assertCountQuery(String originalQuery, String countQuery) { + assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index 7a747cb986..ac81488503 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -35,13 +35,15 @@ * @author Jens Schauder * @author Mark Paluch * @author Michael J. Simons + * @author Diego Krupitza */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class ExpressionBasedStringQueryUnitTests { private static final SpelExpressionParser SPEL_PARSER = new SpelExpressionParser(); - @Mock JpaEntityMetadata metadata; + @Mock + JpaEntityMetadata metadata; @Test // DATAJPA-170 void shouldReturnQueryWithDomainTypeExpressionReplacedWithSimpleDomainTypeName() { @@ -49,7 +51,7 @@ void shouldReturnQueryWithDomainTypeExpressionReplacedWithSimpleDomainTypeName() when(metadata.getEntityName()).thenReturn("User"); String source = "select from #{#entityName} u where u.firstname like :firstname"; - StringQuery query = new ExpressionBasedStringQuery(source, metadata, SPEL_PARSER); + StringQuery query = new ExpressionBasedStringQuery(source, metadata, SPEL_PARSER, false); assertThat(query.getQueryString()).isEqualTo("select from User u where u.firstname like :firstname"); } @@ -58,7 +60,7 @@ void renderAliasInExpressionQueryCorrectly() { when(metadata.getEntityName()).thenReturn("User"); - StringQuery query = new ExpressionBasedStringQuery("select u from #{#entityName} u", metadata, SPEL_PARSER); + StringQuery query = new ExpressionBasedStringQuery("select u from #{#entityName} u", metadata, SPEL_PARSER, true); assertThat(query.getAlias()).isEqualTo("u"); assertThat(query.getQueryString()).isEqualTo("select u from User u"); } @@ -71,7 +73,7 @@ void shouldDetectBindParameterCountCorrectly() { + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',:#{#networkRequest.server},'%')), '')) OR :#{#networkRequest.server} IS NULL)\"\n" + "+ \"AND (n.createdAt >= :#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=:#{#networkRequest.createdTime.endDateTime})\"\n" + "+ \"AND (n.updatedAt >= :#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=:#{#networkRequest.updatedTime.endDateTime})", - metadata, SPEL_PARSER); + metadata, SPEL_PARSER, false); assertThat(query.getParameterBindings()).hasSize(8); } @@ -80,13 +82,39 @@ void shouldDetectBindParameterCountCorrectly() { void shouldDetectBindParameterCountCorrectlyWithJDBCStyleParameters() { StringQuery query = new ExpressionBasedStringQuery( - "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )\"\n" - + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)\"\n" - + "+ \"AND (n.createdAt >= ?#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=?#{#networkRequest.createdTime.endDateTime})\"\n" - + "+ \"AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", - metadata, SPEL_PARSER); + "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )\"\n" + + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)\"\n" + + "+ \"AND (n.createdAt >= ?#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=?#{#networkRequest.createdTime.endDateTime})\"\n" + + "+ \"AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", + metadata, SPEL_PARSER, false); assertThat(query.getParameterBindings()).hasSize(8); } + @Test + void shouldDetectComplexNativeQueriesWithSpelAsNonNative() { + StringQuery query = new ExpressionBasedStringQuery( + "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )\"\n" + + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)\"\n" + + "+ \"AND (n.createdAt >= ?#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=?#{#networkRequest.createdTime.endDateTime})\"\n" + + "+ \"AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", + metadata, SPEL_PARSER, true); + + assertThat(query.isNativeQuery()).isFalse(); + } + + @Test + void shouldDetectSimpleNativeQueriesWithSpelAsNonNative() { + StringQuery query = new ExpressionBasedStringQuery("select n from #{#entityName} n", metadata, SPEL_PARSER, true); + + assertThat(query.isNativeQuery()).isFalse(); + } + + @Test + void shouldDetectSimpleNativeQueriesWithoutSpelAsNonNative() { + StringQuery query = new ExpressionBasedStringQuery("select u from User u", metadata, SPEL_PARSER, true); + + assertThat(query.isNativeQuery()).isTrue(); + } + } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index dd84a156ac..3488212569 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -74,8 +74,10 @@ public class JpaQueryMethodUnitTests { private static final String METHOD_NAME = "findByFirstname"; - @Mock QueryExtractor extractor; - @Mock RepositoryMetadata metadata; + @Mock + QueryExtractor extractor; + @Mock + RepositoryMetadata metadata; private ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); private Method invalidReturnType; @@ -518,7 +520,7 @@ interface InvalidRepository extends Repository { interface ValidRepository extends Repository { - @Query(value = "query", nativeQuery = true) + @Query(value = "Select u from User u where u.lastname = ?1", nativeQuery = true) List findByLastname(String lastname); @Query(name = "HateoasAwareSpringDataWebConfiguration.bar") diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java index 7c4f764fec..742dcf045a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java @@ -23,6 +23,7 @@ * Unit tests for the {@link ParameterBindingParser}. * * @author Jens Schauder + * @author Diego Krupitza */ class ParameterBindingParserUnitTests { @@ -65,7 +66,7 @@ void identificationOfParameters() { private void checkHasParameter(SoftAssertions softly, String query, boolean containsParameter, String label) { - StringQuery stringQuery = new StringQuery(query); + StringQuery stringQuery = new StringQuery(query, false); softly.assertThat(stringQuery.getParameterBindings().size()) // .describedAs(String.format("<%s> (%s)", query, label)) // diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java new file mode 100644 index 0000000000..1eb04239e7 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link QueryEnhancerFactory}. + * + * @author Diego Krupitza + */ +class QueryEnhancerFactoryUnitTests { + + @Test + void createsDefaultImplementationForNonNativeQuery() { + StringQuery query = new StringQuery("Select new User(u.firstname) from User u", false); + + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query); + assertThat(queryEnhancer) // + .isInstanceOf(DefaultQueryEnhancer.class); + } + + @Test + void createsJSqlImplementationForNativeQuery() { + StringQuery query = new StringQuery("Select * from User", true); + + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query); + assertThat(queryEnhancer) // + .isInstanceOf(JSqlParserQueryEnhancer.class); + } +} 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 new file mode 100644 index 0000000000..7ba11d74b7 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java @@ -0,0 +1,707 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.JpaSort; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.*; + +/** + * Unit test for {@link QueryEnhancer}. + * + * @author Diego Krupitza + */ +class QueryEnhancerUnitTests { + + private static final String QUERY = "select u from User u"; + private static final String FQ_QUERY = "select u from org.acme.domain.User$Foo_Bar u"; + private static final String SIMPLE_QUERY = "from User u"; + private static final String COUNT_QUERY = "select count(u) from User u"; + + private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?"; + + @Test + void createsCountQueryCorrectly() { + assertCountQuery(QUERY, COUNT_QUERY, true); + } + + @Test + void createsCountQueriesCorrectlyForCapitalLetterJPQL() { + + assertCountQuery("FROM User u WHERE u.foo.bar = ?", "select count(u) FROM User u WHERE u.foo.bar = ?", false); + + assertCountQuery("SELECT u FROM User u where u.foo.bar = ?", "select count(u) FROM User u where u.foo.bar = ?", + true); + } + + @Test + void createsCountQueryForDistinctQueries() { + + assertCountQuery("select distinct u from User u where u.foo = ?", + "select count(distinct u) from User u where u.foo = ?", true); + } + + @Test + void createsCountQueryForConstructorQueries() { + + assertCountQuery("select distinct new User(u.name) from User u where u.foo = ?", + "select count(distinct u) from User u where u.foo = ?", false); + } + + @Test + void createsCountQueryForJoinsNoneNative() { + + assertCountQuery("select distinct new User(u.name) from User u left outer join u.roles r WHERE r = ?", + "select count(distinct u) from User u left outer join u.roles r WHERE r = ?", false); + } + + @Test + void createsCountQueryForJoinsNative() { + + assertCountQuery("select distinct u.name from User u left outer join u.roles r WHERE r = ?", + "select count(distinct u.name) from User u left outer join u.roles r WHERE r = ?", true); + } + + @Test + void createsCountQueryForQueriesWithSubSelects() { + + assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role)", + "select count(u) from User u left outer join u.roles r where r in (select r from Role)", true); + } + + @Test + void createsCountQueryForAliasesCorrectly() { + + assertCountQuery("select u from User as u", "select count(u) from User as u", true); + } + + @Test + void allowsShortJpaSyntax() { + + assertCountQuery(SIMPLE_QUERY, COUNT_QUERY, false); + } + + @ParameterizedTest + @MethodSource("detectsAliasWithUCorrectlySource") + void detectsAliasWithUCorrectly(DeclaredQuery query, String alias) { + assertThat(getEnhancer(query).detectAlias()).isEqualTo(alias); + } + + public static Stream detectsAliasWithUCorrectlySource() { + return Stream.of( // + Arguments.of(new StringQuery(QUERY, true), "u"), // + Arguments.of(new StringQuery(SIMPLE_QUERY, false), "u"), // + Arguments.of(new StringQuery(COUNT_QUERY, true), "u"), // + Arguments.of(new StringQuery(QUERY_WITH_AS, true), "u"), // + Arguments.of(new StringQuery("SELECT FROM USER U", false), "U"), // + Arguments.of(new StringQuery("select u from User u", true), "u"), // + Arguments.of(new StringQuery("select u from com.acme.User u", true), "u"), // + Arguments.of(new StringQuery("select u from T05User u", true), "u") // + ); + } + + @Test + void allowsFullyQualifiedEntityNamesInQuery() { + + StringQuery query = new StringQuery(FQ_QUERY, true); + assertThat(getEnhancer(query).detectAlias()).isEqualTo("u"); + assertCountQuery(FQ_QUERY, "select count(u) from org.acme.domain.User$Foo_Bar u", true); + } + + @Test // DATAJPA-252 + void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { + + StringQuery query = new StringQuery("select p from Person p left join p.address address", true); + endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("address.city")), "order by address.city asc"); + endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("address.city", "lastname"), "p"), + "order by address.city asc, p.lastname asc"); + } + + @Test // DATAJPA-252 + void extendsExistingOrderByClausesCorrectly() { + + StringQuery query = new StringQuery("select p from Person p order by p.lastname asc", true); + endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("firstname"), "p"), + "order by p.lastname asc, p.firstname asc"); + } + + @Test // DATAJPA-296 + void appliesIgnoreCaseOrderingCorrectly() { + + Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); + + StringQuery query = new StringQuery("select p from Person p", true); + endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by lower(p.firstname) asc"); + } + + @Test // DATAJPA-296 + void appendsIgnoreCaseOrderingCorrectly() { + + Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); + + StringQuery query = new StringQuery("select p from Person p order by p.lastname asc", true); + endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by p.lastname asc, lower(p.firstname) asc"); + } + + @Test // DATAJPA-342 + void usesReturnedVariableInCOuntProjectionIfSet() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", + "select count(distinct m.genre) from Media m where m.user = ?1", true); + } + + @Test // DATAJPA-343 + void projectsCountQueriesForQueriesWithSubSelects() { + + assertCountQuery("select o from Foo o where cb.id in (select b from Bar b)", + "select count(o) from Foo o where cb.id in (select b from Bar b)", true); + } + + @Test // DATAJPA-148 + void doesNotPrefixSortsIfFunction() { + StringQuery query = new StringQuery("select p from Person p", true); + Sort sort = Sort.by("sum(foo)"); + + QueryEnhancer enhancer = getEnhancer(query); + + assertThatThrownBy(() -> enhancer.applySorting(sort, "p")) // + .isInstanceOf(InvalidDataAccessApiUsageException.class); + } + + @Test // DATAJPA-377 + void removesOrderByInGeneratedCountQueryFromOriginalQueryIfPresent() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 OrDer By m.genre ASC", + "select count(distinct m.genre) from Media m where m.user = ?1", true); + } + + @Test // DATAJPA-375 + void findsExistingOrderByIndependentOfCase() { + + Sort sort = Sort.by("lastname"); + StringQuery originalQuery = new StringQuery("select p from Person p ORDER BY p.firstname", true); + String query = getEnhancer(originalQuery).applySorting(sort, "p"); + endsIgnoringCase(query, "ORDER BY p.firstname, p.lastname asc"); + } + + @Test // DATAJPA-409 + void createsCountQueryForNestedReferenceCorrectly() { + assertCountQuery("select a.b from A a", "select count(a.b) from A a", true); + } + + @Test // DATAJPA-420 + void createsCountQueryForScalarSelects() { + assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p", true); + } + + @Test // DATAJPA-456 + void createCountQueryFromTheGivenCountProjection() { + StringQuery query = new StringQuery("select p.lastname,p.firstname from Person p", true); + assertThat(getEnhancer(query).createCountQueryFor("p.lastname")) + .isEqualToIgnoringCase("select count(p.lastname) from Person p"); + } + + @Test // DATAJPA-726 + void detectsAliassesInPlainJoins() { + + StringQuery query = new StringQuery("select p from Customer c join c.productOrder p where p.delaye = true", true); + Sort sort = Sort.by("p.lineItems"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "c"), "order by p.lineItems asc"); + } + + @Test // DATAJPA-736 + void supportsNonAsciiCharactersInEntityNames() { + StringQuery query = new StringQuery("select u from Usèr u", true); + assertThat(getEnhancer(query).createCountQueryFor()).isEqualToIgnoringCase("select count(u) from Usèr u"); + } + + @Test // DATAJPA-798 + void detectsAliasInQueryContainingLineBreaks() { + StringQuery query = new StringQuery("select \n u \n from \n User \nu", true); + assertThat(getEnhancer(query).detectAlias()).isEqualTo("u"); + } + + @Test // DATAJPA-815 + void doesPrefixPropertyWithNonNative() { + + StringQuery query = new StringQuery("from Cat c join Dog d", false); + Sort sort = Sort.by("dPropertyStartingWithJoinAlias"); + + assertThat(getEnhancer(query).applySorting(sort, "c")).endsWith("order by c.dPropertyStartingWithJoinAlias asc"); + } + + @Test // DATAJPA-815 + void doesPrefixPropertyWithNative() { + StringQuery query = new StringQuery("Select * from Cat c join Dog d", true); + Sort sort = Sort.by("dPropertyStartingWithJoinAlias"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "c"), "order by c.dPropertyStartingWithJoinAlias asc"); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionInDistinctQuery() { + StringQuery query = new StringQuery("select distinct new Foo() from Bar b", false); + assertThat(getEnhancer(query).hasConstructorExpression()).isTrue(); + } + + @Test // DATAJPA-938 + void detectsComplexConstructorExpression() { + + StringQuery query = new StringQuery("select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) " // + + "from Bar lp join lp.investmentProduct ip " // + + "where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId " + // + + "group by ip.id, ip.name, lp.accountId " // + + "order by ip.name ASC", false); + + assertThat(getEnhancer(query).hasConstructorExpression()).isTrue(); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionWithLineBreaks() { + StringQuery query = new StringQuery("select new foo.bar.FooBar(\na.id) from DtoA a ", false); + assertThat(getEnhancer(query).hasConstructorExpression()).isTrue(); + } + + @Test // DATAJPA-960 + void doesNotQualifySortIfNoAliasDetectedNonNative() { + StringQuery query = new StringQuery("from mytable where ?1 is null", false); + assertThat(getEnhancer(query).applySorting(Sort.by("firstname"))).endsWith("order by firstname asc"); + } + + @Test // DATAJPA-960 + void doesNotQualifySortIfNoAliasDetectedNative() { + StringQuery query = new StringQuery("Select * from mytable where ?1 is null", true); + endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("firstname")), "order by firstname asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotAllowWhitespaceInSort() { + + StringQuery query = new StringQuery("select p from Person p", true); + + Sort sort = Sort.by("case when foo then bar"); + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> getEnhancer(query).applySorting(sort, "p")); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixUnsageJpaSortFunctionCalls() { + + JpaSort sort = JpaSort.unsafe("sum(foo)"); + StringQuery query = new StringQuery("select p from Person p", true); + endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by sum(foo) asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixMultipleAliasedFunctionCalls() { + + StringQuery query = new StringQuery("SELECT AVG(m.price) AS avgPrice, SUM(m.stocks) AS sumStocks FROM Magazine m", + true); + Sort sort = Sort.by("avgPrice", "sumStocks"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by avgPrice asc, sumStocks asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixSingleAliasedFunctionCalls() { + + StringQuery query = new StringQuery("SELECT AVG(m.price) AS avgPrice FROM Magazine m", true); + Sort sort = Sort.by("avgPrice"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { + + StringQuery query = new StringQuery("SELECT AVG(m.price) AS avgPrice FROM Magazine m", true); + Sort sort = Sort.by("someOtherProperty"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by m.someOtherProperty asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainesAliasedFunctionForDifferentProperty() { + + StringQuery query = new StringQuery("SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m", true); + Sort sort = Sort.by("name", "avgPrice"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by m.name asc, avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() { + + StringQuery query = new StringQuery("SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m", true); + Sort sort = Sort.by("trimmedName"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by trimmedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() { + + StringQuery query = new StringQuery("SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m", true); + Sort sort = Sort.by("extendedName"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by extendedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { + + StringQuery query = new StringQuery("SELECT AVG(m.price) AS avg_price FROM Magazine m", true); + Sort sort = Sort.by("avg_price"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by avg_price asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithDots() { + + StringQuery query = new StringQuery("SELECT AVG(m.price) AS m.avg FROM Magazine m", false); + Sort sort = Sort.by("m.avg"); + + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWith("order by m.avg asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithDotsNativeQuery() { + + // this is invalid since the '.' character is not allowed. Not in sql nor in JPQL. + assertThatThrownBy(() -> new StringQuery("SELECT AVG(m.price) AS m.avg FROM Magazine m", true)) // + .isInstanceOf(IllegalArgumentException.class); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpaces() { + + StringQuery query = new StringQuery("SELECT AVG( m.price ) AS avgPrice FROM Magazine m", true); + Sort sort = Sort.by("avgPrice"); + + endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by avgPrice asc"); + } + + @Test // DATAJPA-1000 + void discoversCorrectAliasForJoinFetch() { + + String queryString = "SELECT DISTINCT user FROM User user LEFT JOIN user.authorities AS authority"; + Set aliases = QueryUtils.getOuterJoinAliases(queryString); + + StringQuery nativeQuery = new StringQuery(queryString, true); + Set joinAliases = new JSqlParserQueryEnhancer(nativeQuery).getJoinAliases(); + + assertThat(aliases).containsExactly("authority"); + assertThat(joinAliases).containsExactly("authority"); + } + + @Test // DATAJPA-1171 + void doesNotContainStaticClauseInExistsQuery() { + endsIgnoringCase(QueryUtils.getExistsQueryString("entity", "x", Collections.singleton("id")), "WHERE x.id = :id"); + } + + @Test // DATAJPA-1363 + void discoversAliasWithComplexFunction() { + + assertThat( + QueryUtils.getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) // + .contains("myAlias"); + } + + @Test // DATAJPA-1506 + void detectsAliasWithGroupAndOrderBy() { + + StringQuery queryWithGroupNoAlias = new StringQuery("select * from User group by name", true); + StringQuery queryWithGroupAlias = new StringQuery("select * from User u group by name", true); + + StringQuery queryWithOrderNoAlias = new StringQuery("select * from User order by name", true); + StringQuery queryWithOrderAlias = new StringQuery("select * from User u order by name", true); + + assertThat(getEnhancer(queryWithGroupNoAlias).detectAlias()).isNull(); + assertThat(getEnhancer(queryWithOrderNoAlias).detectAlias()).isNull(); + assertThat(getEnhancer(queryWithGroupAlias).detectAlias()).isEqualTo("u"); + assertThat(getEnhancer(queryWithOrderAlias).detectAlias()).isEqualTo("u"); + } + + @Test // DATAJPA-1500 + void createCountQuerySupportsWhitespaceCharacters() { + + StringQuery query = new StringQuery("select * from User user\n" + // + " where user.age = 18\n" + // + " order by user.name\n ", true); + + assertThat(getEnhancer(query).createCountQueryFor()) + .isEqualToIgnoringCase("select count(user) from User user where user.age = 18"); + } + + @Test + void createCountQuerySupportsLineBreaksInSelectClause() { + StringQuery query = new StringQuery("select user.age,\n" + // + " user.name\n" + // + " from User user\n" + // + " where user.age = 18\n" + // + " order\nby\nuser.name\n ", true); + assertThat(getEnhancer(query).createCountQueryFor()) + .isEqualToIgnoringCase("select count(user) from User user where user.age = 18"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFieldAliases() { + + StringQuery query = new StringQuery( + "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a", + true); + Sort sort = Sort.by("authorName"); + + String fullQuery = getEnhancer(query).applySorting(sort); + + endsIgnoringCase(fullQuery, "order by authorName asc"); + } + + @Test // GH-2280 + void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { + + StringQuery query = new StringQuery("SELECT customer.id as id, customer.name as name FROM CustomerEntity customer", + true); + Sort sort = Sort.by(Sort.Order.by("name").ignoreCase()); + + String fullQuery = getEnhancer(query).applySorting(sort); + + assertThat(fullQuery).isEqualToIgnoringCase( + "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(name) asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFunctionAliases() { + + StringQuery query = new StringQuery( + "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a", + true); + Sort sort = Sort.by("title"); + + String fullQuery = getEnhancer(query).applySorting(sort); + + endsIgnoringCase(fullQuery, "order by title asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForSimpleField() { + + StringQuery query = new StringQuery( + "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a", + true); + Sort sort = Sort.by("price"); + + String fullQuery = getEnhancer(query).applySorting(sort); + + endsIgnoringCase(fullQuery, "order by m.price asc"); + } + + @Test + void createCountQuerySupportsLineBreakRightAfterDistinct() { + + StringQuery query1 = new StringQuery("select\ndistinct\nuser.age,\n" + // + "user.name\n" + // + "from\nUser\nuser", true); + + StringQuery query2 = new StringQuery("select\ndistinct user.age,\n" + // + "user.name\n" + // + "from\nUser\nuser", true); + + assertThat(getEnhancer(query1).createCountQueryFor()).isEqualTo(getEnhancer(query2).createCountQueryFor()); + } + + @Test + void detectsAliasWithGroupAndOrderByWithLineBreaks() { + + StringQuery queryWithGroupAndLineBreak = new StringQuery("select * from User group\nby name", true); + StringQuery queryWithGroupAndLineBreakAndAlias = new StringQuery("select * from User u group\nby name", true); + + assertThat(getEnhancer(queryWithGroupAndLineBreak).detectAlias()).isNull(); + assertThat(getEnhancer(queryWithGroupAndLineBreakAndAlias).detectAlias()).isEqualTo("u"); + + StringQuery queryWithOrderAndLineBreak = new StringQuery("select * from User order\nby name", true); + StringQuery queryWithOrderAndLineBreakAndAlias = new StringQuery("select * from User u order\nby name", true); + StringQuery queryWithOrderAndMultipleLineBreakAndAlias = new StringQuery("select * from User\nu\norder \n by name", + true); + + assertThat(getEnhancer(queryWithOrderAndLineBreak).detectAlias()).isNull(); + assertThat(getEnhancer(queryWithOrderAndLineBreakAndAlias).detectAlias()).isEqualTo("u"); + assertThat(getEnhancer(queryWithOrderAndMultipleLineBreakAndAlias).detectAlias()).isEqualTo("u"); + } + + @ParameterizedTest // DATAJPA-1679 + @MethodSource("findProjectionClauseWithDistinctSource") + void findProjectionClauseWithDistinct(DeclaredQuery query, String expected) { + + SoftAssertions.assertSoftly(sofly -> { + sofly.assertThat(getEnhancer(query).getProjection()).isEqualTo(expected); + }); + } + + public static Stream findProjectionClauseWithDistinctSource() { + return Stream.of( // + Arguments.of(new StringQuery("select * from x", true), "*"), // + Arguments.of(new StringQuery("select a, b, c from x", true), "a, b, c"), // + Arguments.of(new StringQuery("select distinct a, b, c from x", true), "a, b, c"), // + Arguments.of(new StringQuery("select DISTINCT a, b, c from x", true), "a, b, c") // + ); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithSubselect() { + + // This is not a required behavior, in fact the opposite is, + // but it documents a current limitation. + // to fix this without breaking findProjectionClauseWithIncludedFrom we need a more sophisticated parser. + assertThat(QueryUtils.getProjection("select * from (select x from y)")).isNotEqualTo("*"); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithSubselectNative() { + + // This is a required behavior the testcase in #findProjectionClauseWithSubselect tells why + String queryString = "select * from (select x from y)"; + StringQuery query = new StringQuery(queryString, true); + assertThat(getEnhancer(query).getProjection()).isEqualTo("*"); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithIncludedFrom() { + StringQuery query = new StringQuery("select x, frommage, y from t", true); + assertThat(getEnhancer(query).getProjection()).isEqualTo("x, frommage, y"); + } + + @Test + void countProjectionDistrinctQueryIncludesNewLineAfterFromAndBeforeJoin() { + StringQuery originalQuery = new StringQuery( + "SELECT DISTINCT entity1\nFROM Entity1 entity1\nLEFT JOIN Entity2 entity2 ON entity1.key = entity2.key", true); + + assertCountQuery(originalQuery, + "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test + void countProjectionDistinctQueryIncludesNewLineAfterEntity() { + StringQuery originalQuery = new StringQuery( + "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key", true); + assertCountQuery(originalQuery, + "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test + void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { + StringQuery originalQuery = new StringQuery( + "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799", + true); + assertCountQuery(originalQuery, + "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key where entity1.id = 1799"); + } + + @Test + void createsCountQueriesCorrectlyForCapitalLetter() { + assertCountQuery("SELECT u FROM User u where u.foo.bar = ?", "select count(u) FROM User u where u.foo.bar = ?", + true); + } + + @ParameterizedTest // DATAJPA-252 + @MethodSource("detectsJoinAliasesCorrectlySource") + void detectsJoinAliasesCorrectly(String queryString, List aliases) { + + StringQuery nativeQuery = new StringQuery(queryString, true); + StringQuery nonNativeQuery = new StringQuery(queryString, false); + + Set nativeJoinAliases = getEnhancer(nativeQuery).getJoinAliases(); + Set nonNativeJoinAliases = getEnhancer(nonNativeQuery).getJoinAliases(); + + assertThat(nonNativeJoinAliases).containsAll(nativeJoinAliases); + assertThat(nativeJoinAliases) // + .hasSize(aliases.size()) // + .containsAll(aliases); + + } + + @Test // GH-2441 + void correctFunctionAliasWithComplexNestedFunctions() { + String queryString = "\nSELECT \nCAST(('{' || string_agg(distinct array_to_string(c.institutes_ids, ','), ',') || '}') AS bigint[]) as institutesIds\nFROM\ncity c"; + StringQuery nativeQuery = new StringQuery(queryString, true); + + JSqlParserQueryEnhancer queryEnhancer = (JSqlParserQueryEnhancer) getEnhancer(nativeQuery); + + assertThat(queryEnhancer.getSelectionAliases()).contains("institutesIds"); + } + + @Test // GH-2441 + void correctApplySortOnComplexNestedFunctionQuery() { + String queryString = "SELECT dd.institutesIds FROM (\n" + " SELECT\n" + + " CAST(('{' || string_agg(distinct array_to_string(c.institutes_ids, ','), ',') || '}') AS bigint[]) as institutesIds\n" + + " FROM\n" + " city c\n" + + " ) dd"; + + StringQuery nativeQuery = new StringQuery(queryString, true); + + QueryEnhancer queryEnhancer = getEnhancer(nativeQuery); + + String result = queryEnhancer.applySorting(Sort.by(new Sort.Order(Sort.Direction.ASC, "institutesIds"))); + assertThat(result).containsIgnoringCase("order by dd.institutesIds"); + } + + public static Stream detectsJoinAliasesCorrectlySource() { + return Stream.of( // + Arguments.of("select p from Person p left outer join x.foo b2_$ar", Collections.singletonList("b2_$ar")), // + Arguments.of("select p from Person p left join x.foo b2_$ar", Collections.singletonList("b2_$ar")), // + Arguments.of("select p from Person p left outer join x.foo as b2_$ar, left join x.bar as foo", + Arrays.asList("b2_$ar", "foo")), // + Arguments.of("select p from Person p left join x.foo as b2_$ar, left outer join x.bar foo", + Arrays.asList("b2_$ar", "foo")) // + + ); + } + + private static void assertCountQuery(String originalQuery, String countQuery, boolean nativeQuery) { + assertCountQuery(new StringQuery(originalQuery, nativeQuery), countQuery); + } + + private static void assertCountQuery(StringQuery originalQuery, String countQuery) { + assertThat(getEnhancer(originalQuery).createCountQueryFor()).isEqualToIgnoringCase(countQuery); + } + + private static void endsIgnoringCase(String original, String endWithIgnoreCase) { + // https://github.com/assertj/assertj-core/pull/2451 + // can be removed when upgrading to version 3.23.0 assertJ + assertThat(original.toUpperCase()).endsWith(endWithIgnoreCase.toUpperCase()); + } + + private static QueryEnhancer getEnhancer(DeclaredQuery query) { + return QueryEnhancerFactory.forQuery(query); + } + +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java index 68e38e4925..a0081a413e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java @@ -53,14 +53,14 @@ void before() { @Test // DATAJPA-1058 void noExceptionWhenQueryDoesNotContainNamedParameters() { - setterFactory.create(binding, DeclaredQuery.of("QueryStringWithOutNamedParameter")); + setterFactory.create(binding, DeclaredQuery.of("QueryStringWithOutNamedParameter", false)); } @Test // DATAJPA-1058 void exceptionWhenQueryContainNamedParametersAndMethodParametersAreNotNamed() { assertThatExceptionOfType(IllegalStateException.class) // - .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith :NamedParameter"))) // + .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith :NamedParameter", false))) // .withMessageContaining("Java 8") // .withMessageContaining("@Param") // .withMessageContaining("-parameters"); @@ -77,7 +77,7 @@ void exceptionWhenCriteriaQueryContainsInsufficientAmountOfParameters() { when(binding.getRequiredPosition()).thenReturn(1); assertThatExceptionOfType(IllegalArgumentException.class) // - .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith :NamedParameter"))) // + .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith :NamedParameter", false))) // .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query."); } @@ -91,7 +91,7 @@ void exceptionWhenBasicQueryContainsInsufficientAmountOfParameters() { when(binding.getRequiredPosition()).thenReturn(1); assertThatExceptionOfType(IllegalArgumentException.class) // - .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith ?1"))) // + .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith ?1", false))) // .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query."); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index 8fde636585..d048d70bbd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -71,7 +71,8 @@ @ContextConfiguration("classpath:infrastructure.xml") public class QueryUtilsIntegrationTests { - @PersistenceContext EntityManager em; + @PersistenceContext + EntityManager em; @Test // DATAJPA-403 void reusesExistingJoinForExpression() { @@ -139,8 +140,8 @@ void createsLeftJoinForNonOptionalToOneWithNestedOptional() { CriteriaQuery query = builder.createQuery(InvoiceItem.class); Root root = query.from(InvoiceItem.class); - QueryUtils - .toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), false); + QueryUtils.toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), + false); assertThat(getInnerJoins(root)).hasSize(1); // join invoice Join rootInnerJoin = getInnerJoins(root).iterator().next(); @@ -162,8 +163,8 @@ void reusesLeftJoinForNonOptionalToOneWithNestedOptional() { root.join("invoice", JoinType.LEFT).join("order", JoinType.LEFT); // when navigating through a path with nested optionals - QueryUtils - .toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), false); + QueryUtils.toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), + false); // assert that existing joins are reused and no additional joins are created assertThat(getInnerJoins(root)).isEmpty(); // no inner join invoice @@ -185,8 +186,8 @@ void reusesInnerJoinForNonOptionalToOneWithNestedOptional() { // given an existing inner join an nested optional root.join("invoice").join("order"); - QueryUtils - .toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), false); + QueryUtils.toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), + false); // assert that no useless left joins are created assertThat(getInnerJoins(root)).hasSize(1); // join invoice @@ -347,32 +348,40 @@ int getNumberOfJoinsAfterCreatingAPath() { @SuppressWarnings("unused") static class Merchant { - @Id String id; - @OneToMany Set employees; + @Id + String id; + @OneToMany + Set employees; - @OneToOne Address address; + @OneToOne + Address address; } @Entity @SuppressWarnings("unused") static class Address { - @Id String id; - @OneToOne(mappedBy = "address") Merchant merchant; + @Id + String id; + @OneToOne(mappedBy = "address") + Merchant merchant; } @Entity @SuppressWarnings("unused") static class Employee { - @Id String id; - @OneToMany Set credentials; + @Id + String id; + @OneToMany + Set credentials; } @Entity @SuppressWarnings("unused") static class Credential { - @Id String id; + @Id + String id; String uid; } @@ -390,7 +399,8 @@ public List getPersistenceProviders() { } @Override - public void clearCachedProviders() {} + public void clearCachedProviders() { + } } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index b2640fea25..cf7cb86144 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -51,7 +51,7 @@ class QueryUtilsUnitTests { private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?"; @Test - void createsCountQueryCorrectly() { + void createsCountQueryCorrectly() throws Exception { assertCountQuery(QUERY, COUNT_QUERY); } @@ -64,47 +64,47 @@ void createsCountQueriesCorrectlyForCapitalLetterJPQL() { } @Test - void createsCountQueryForDistinctQueries() { + void createsCountQueryForDistinctQueries() throws Exception { assertCountQuery("select distinct u from User u where u.foo = ?", "select count(distinct u) from User u where u.foo = ?"); } @Test - void createsCountQueryForConstructorQueries() { + void createsCountQueryForConstructorQueries() throws Exception { assertCountQuery("select distinct new User(u.name) from User u where u.foo = ?", "select count(distinct u) from User u where u.foo = ?"); } @Test - void createsCountQueryForJoins() { + void createsCountQueryForJoins() throws Exception { assertCountQuery("select distinct new User(u.name) from User u left outer join u.roles r WHERE r = ?", "select count(distinct u) from User u left outer join u.roles r WHERE r = ?"); } @Test - void createsCountQueryForQueriesWithSubSelects() { + void createsCountQueryForQueriesWithSubSelects() throws Exception { assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role)", "select count(u) from User u left outer join u.roles r where r in (select r from Role)"); } @Test - void createsCountQueryForAliasesCorrectly() { + void createsCountQueryForAliasesCorrectly() throws Exception { assertCountQuery("select u from User as u", "select count(u) from User as u"); } @Test - void allowsShortJpaSyntax() { + void allowsShortJpaSyntax() throws Exception { assertCountQuery(SIMPLE_QUERY, COUNT_QUERY); } @Test - void detectsAliasCorrectly() { + void detectsAliasCorrectly() throws Exception { assertThat(detectAlias(QUERY)).isEqualTo("u"); assertThat(detectAlias(SIMPLE_QUERY)).isEqualTo("u"); @@ -400,8 +400,8 @@ void doesNotContainStaticClauseInExistsQuery() { @Test // DATAJPA-1363 void discoversAliasWithComplexFunction() { - assertThat( - QueryUtils.getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) // + assertThat(QueryUtils + .getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) // .contains("myAlias"); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index adec631c12..9260474770 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -37,6 +37,7 @@ * @author Jens Schauder * @author Nils Borrmann * @author Andriy Redko + * @author Diego Krupitza */ class StringQueryUnitTests { @@ -46,7 +47,7 @@ class StringQueryUnitTests { void doesNotConsiderPlainLikeABinding() { String source = "select from User u where u.firstname like :firstname"; - StringQuery query = new StringQuery(source); + StringQuery query = new StringQuery(source, false); assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()).isEqualTo(source); @@ -63,7 +64,8 @@ void doesNotConsiderPlainLikeABinding() { @Test // DATAJPA-292 void detectsPositionalLikeBindings() { - StringQuery query = new StringQuery("select u from User u where u.firstname like %?1% or u.lastname like %?2"); + StringQuery query = new StringQuery("select u from User u where u.firstname like %?1% or u.lastname like %?2", + true); assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()) @@ -86,7 +88,7 @@ void detectsPositionalLikeBindings() { @Test // DATAJPA-292 void detectsNamedLikeBindings() { - StringQuery query = new StringQuery("select u from User u where u.firstname like %:firstname"); + StringQuery query = new StringQuery("select u from User u where u.firstname like %:firstname", true); assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()).isEqualTo("select u from User u where u.firstname like :firstname"); @@ -104,7 +106,7 @@ void detectsNamedLikeBindings() { void detectsNamedInParameterBindings() { String queryString = "select u from User u where u.id in :ids"; - StringQuery query = new StringQuery(queryString); + StringQuery query = new StringQuery(queryString, true); assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()).isEqualTo(queryString); @@ -121,7 +123,7 @@ void detectsNamedInParameterBindings() { void detectsMultipleNamedInParameterBindings() { String queryString = "select u from User u where u.id in :ids and u.name in :names and foo = :bar"; - StringQuery query = new StringQuery(queryString); + StringQuery query = new StringQuery(queryString, true); assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()).isEqualTo(queryString); @@ -140,7 +142,7 @@ void detectsMultipleNamedInParameterBindings() { void detectsPositionalInParameterBindings() { String queryString = "select u from User u where u.id in ?1"; - StringQuery query = new StringQuery(queryString); + StringQuery query = new StringQuery(queryString, true); assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()).isEqualTo(queryString); @@ -157,7 +159,7 @@ void detectsPositionalInParameterBindings() { void detectsMultiplePositionalInParameterBindings() { String queryString = "select u from User u where u.id in ?1 and u.names in ?2 and foo = ?3"; - StringQuery query = new StringQuery(queryString); + StringQuery query = new StringQuery(queryString, true); assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()).isEqualTo(queryString); @@ -174,19 +176,19 @@ void detectsMultiplePositionalInParameterBindings() { @Test // DATAJPA-373 void handlesMultipleNamedLikeBindingsCorrectly() { - new StringQuery("select u from User u where u.firstname like %:firstname or foo like :bar"); + new StringQuery("select u from User u where u.firstname like %:firstname or foo like :bar", true); } @Test // DATAJPA-292, DATAJPA-362 void rejectsDifferentBindingsForRepeatedParameter() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new StringQuery("select u from User u where u.firstname like %?1 and u.lastname like ?1%")); + assertThatIllegalArgumentException().isThrownBy( + () -> new StringQuery("select u from User u where u.firstname like %?1 and u.lastname like ?1%", true)); } @Test // DATAJPA-461 void treatsGreaterThanBindingAsSimpleBinding() { - StringQuery query = new StringQuery("select u from User u where u.createdDate > ?1"); + StringQuery query = new StringQuery("select u from User u where u.createdDate > ?1", true); List bindings = query.getParameterBindings(); assertThat(bindings).hasSize(1); @@ -199,7 +201,7 @@ void treatsGreaterThanBindingAsSimpleBinding() { void removesLikeBindingsFromQueryIfQueryContainsSimpleBinding() { StringQuery query = new StringQuery("SELECT a FROM Article a WHERE a.overview LIKE %:escapedWord% ESCAPE '~'" - + " OR a.content LIKE %:escapedWord% ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC"); + + " OR a.content LIKE %:escapedWord% ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC", true); List bindings = query.getParameterBindings(); @@ -217,7 +219,7 @@ void removesLikeBindingsFromQueryIfQueryContainsSimpleBinding() { @Test // DATAJPA-483 void detectsInBindingWithParentheses() { - StringQuery query = new StringQuery("select count(we) from MyEntity we where we.status in (:statuses)"); + StringQuery query = new StringQuery("select count(we) from MyEntity we where we.status in (:statuses)", true); List bindings = query.getParameterBindings(); @@ -230,7 +232,7 @@ void detectsInBindingWithParentheses() { @Test // DATAJPA-545 void detectsInBindingWithSpecialFrenchCharactersInParentheses() { - StringQuery query = new StringQuery("select * from MyEntity where abonnés in (:abonnés)"); + StringQuery query = new StringQuery("select * from MyEntity where abonnés in (:abonnés)", true); List bindings = query.getParameterBindings(); @@ -243,7 +245,7 @@ void detectsInBindingWithSpecialFrenchCharactersInParentheses() { @Test // DATAJPA-545 void detectsInBindingWithSpecialCharactersInParentheses() { - StringQuery query = new StringQuery("select * from MyEntity where øre in (:øre)"); + StringQuery query = new StringQuery("select * from MyEntity where øre in (:øre)", true); List bindings = query.getParameterBindings(); @@ -256,7 +258,7 @@ void detectsInBindingWithSpecialCharactersInParentheses() { @Test // DATAJPA-545 void detectsInBindingWithSpecialAsianCharactersInParentheses() { - StringQuery query = new StringQuery("select * from MyEntity where 생일 in (:생일)"); + StringQuery query = new StringQuery("select * from MyEntity where 생일 in (:생일)", true); List bindings = query.getParameterBindings(); @@ -269,7 +271,7 @@ void detectsInBindingWithSpecialAsianCharactersInParentheses() { @Test // DATAJPA-545 void detectsInBindingWithSpecialCharactersAndWordCharactersMixedInParentheses() { - StringQuery query = new StringQuery("select * from MyEntity where foo in (:ab1babc생일233)"); + StringQuery query = new StringQuery("select * from MyEntity where foo in (:ab1babc생일233)", true); List bindings = query.getParameterBindings(); @@ -281,14 +283,14 @@ void detectsInBindingWithSpecialCharactersAndWordCharactersMixedInParentheses() @Test // DATAJPA-362 void rejectsDifferentBindingsForRepeatedParameter2() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new StringQuery("select u from User u where u.firstname like ?1 and u.lastname like %?1")); + assertThatIllegalArgumentException().isThrownBy( + () -> new StringQuery("select u from User u where u.firstname like ?1 and u.lastname like %?1", true)); } @Test // DATAJPA-712 void shouldReplaceAllNamedExpressionParametersWithInClause() { - StringQuery query = new StringQuery("select a from A a where a.b in :#{#bs} and a.c in :#{#cs}"); + StringQuery query = new StringQuery("select a from A a where a.b in :#{#bs} and a.c in :#{#cs}", true); String queryString = query.getQueryString(); assertThat(queryString).isEqualTo("select a from A a where a.b in :__$synthetic$__1 and a.c in :__$synthetic$__2"); @@ -297,7 +299,7 @@ void shouldReplaceAllNamedExpressionParametersWithInClause() { @Test // DATAJPA-712 void shouldReplaceAllPositionExpressionParametersWithInClause() { - StringQuery query = new StringQuery("select a from A a where a.b in ?#{#bs} and a.c in ?#{#cs}"); + StringQuery query = new StringQuery("select a from A a where a.b in ?#{#bs} and a.c in ?#{#cs}", true); String queryString = query.getQueryString(); softly.assertThat(queryString).isEqualTo("select a from A a where a.b in ?1 and a.c in ?2"); @@ -310,9 +312,11 @@ void shouldReplaceAllPositionExpressionParametersWithInClause() { @Test // DATAJPA-864 void detectsConstructorExpressions() { - softly.assertThat(new StringQuery("select new Dto(a.foo, a.bar) from A a").hasConstructorExpression()).isTrue(); - softly.assertThat(new StringQuery("select new Dto (a.foo, a.bar) from A a").hasConstructorExpression()).isTrue(); - softly.assertThat(new StringQuery("select a from A a").hasConstructorExpression()).isFalse(); + softly.assertThat(new StringQuery("select new Dto(a.foo, a.bar) from A a", false).hasConstructorExpression()) + .isTrue(); + softly.assertThat(new StringQuery("select new Dto (a.foo, a.bar) from A a", false).hasConstructorExpression()) + .isTrue(); + softly.assertThat(new StringQuery("select a from A a", true).hasConstructorExpression()).isFalse(); softly.assertAll(); } @@ -325,8 +329,8 @@ void detectsConstructorExpressions() { void detectsConstructorExpressionForDefaultConstructor() { // Parentheses required - softly.assertThat(new StringQuery("select new Dto() from A a").hasConstructorExpression()).isTrue(); - softly.assertThat(new StringQuery("select new Dto from A a").hasConstructorExpression()).isFalse(); + softly.assertThat(new StringQuery("select new Dto() from A a", false).hasConstructorExpression()).isTrue(); + softly.assertThat(new StringQuery("select new Dto from A a", false).hasConstructorExpression()).isFalse(); softly.assertAll(); } @@ -334,7 +338,7 @@ void detectsConstructorExpressionForDefaultConstructor() { @Test // DATAJPA-1179 void bindingsMatchQueryForIdenticalSpelExpressions() { - StringQuery query = new StringQuery("select a from A a where a.first = :#{#exp} or a.second = :#{#exp}"); + StringQuery query = new StringQuery("select a from A a where a.first = :#{#exp} or a.second = :#{#exp}", true); List bindings = query.getParameterBindings(); softly.assertThat(bindings).isNotEmpty(); @@ -351,18 +355,18 @@ void bindingsMatchQueryForIdenticalSpelExpressions() { @Test // DATAJPA-1235 void getProjection() { - checkProjection("SELECT something FROM", "something", "uppercase is supported"); - checkProjection("select something from", "something", "single expression"); - checkProjection("select x, y, z from", "x, y, z", "tuple"); - checkProjection("sect x, y, z from", "", "missing select"); - checkProjection("select x, y, z fron", "", "missing from"); + checkProjection("SELECT something FROM", "something", "uppercase is supported", false); + checkProjection("select something from", "something", "single expression", false); + checkProjection("select x, y, z from", "x, y, z", "tuple", false); + checkProjection("sect x, y, z from", "", "missing select", false); + checkProjection("select x, y, z fron", "", "missing from", false); softly.assertAll(); } - void checkProjection(String query, String expected, String description) { + void checkProjection(String query, String expected, String description, boolean nativeQuery) { - softly.assertThat(new StringQuery(query).getProjection()) // + softly.assertThat(new StringQuery(query, nativeQuery).getProjection()) // .as("%s (%s)", description, query) // .isEqualTo(expected); } @@ -370,25 +374,25 @@ void checkProjection(String query, String expected, String description) { @Test // DATAJPA-1235 void getAlias() { - checkAlias("from User u", "u", "simple query"); - checkAlias("select count(u) from User u", "u", "count query"); - checkAlias("select u from User as u where u.username = ?", "u", "with as"); - checkAlias("SELECT FROM USER U", "U", "uppercase"); - checkAlias("select u from User u", "u", "simple query"); - checkAlias("select u from com.acme.User u", "u", "fully qualified package name"); - checkAlias("select u from T05User u", "u", "interesting entity name"); - checkAlias("from User ", null, "trailing space"); - checkAlias("from User", null, "no trailing space"); - checkAlias("from User as bs", "bs", "ignored as"); - checkAlias("from User as AS", "AS", "ignored as using the second"); - checkAlias("from User asas", "asas", "asas is weird but legal"); + checkAlias("from User u", "u", "simple query", false); + checkAlias("select count(u) from User u", "u", "count query", true); + checkAlias("select u from User as u where u.username = ?", "u", "with as", true); + checkAlias("SELECT FROM USER U", "U", "uppercase", false); + checkAlias("select u from User u", "u", "simple query", true); + checkAlias("select u from com.acme.User u", "u", "fully qualified package name", true); + checkAlias("select u from T05User u", "u", "interesting entity name", true); + checkAlias("from User ", null, "trailing space", false); + checkAlias("from User", null, "no trailing space", false); + checkAlias("from User as bs", "bs", "ignored as", false); + checkAlias("from User as AS", "AS", "ignored as using the second", false); + checkAlias("from User asas", "asas", "asas is weird but legal", false); softly.assertAll(); } - private void checkAlias(String query, String expected, String description) { + private void checkAlias(String query, String expected, String description, boolean nativeQuery) { - softly.assertThat(new StringQuery(query).getAlias()) // + softly.assertThat(new StringQuery(query, nativeQuery).getAlias()) // .as("%s (%s)", description, query) // .isEqualTo(expected); } @@ -396,32 +400,32 @@ private void checkAlias(String query, String expected, String description) { @Test // DATAJPA-1200 void testHasNamedParameter() { - checkHasNamedParameter("select something from x where id = :id", true, "named parameter"); - checkHasNamedParameter("in the :id middle", true, "middle"); - checkHasNamedParameter(":id start", true, "beginning"); - checkHasNamedParameter(":id", true, "alone"); - checkHasNamedParameter("select something from x where id = :id", true, "named parameter"); - checkHasNamedParameter(":UPPERCASE", true, "uppercase"); - checkHasNamedParameter(":lowercase", true, "lowercase"); - checkHasNamedParameter(":2something", true, "beginning digit"); - checkHasNamedParameter(":2", true, "only digit"); - checkHasNamedParameter(":.something", true, "dot"); - checkHasNamedParameter(":_something", true, "underscore"); - checkHasNamedParameter(":$something", true, "dollar"); - checkHasNamedParameter(":\uFE0F", true, "non basic latin emoji"); // - checkHasNamedParameter(":\u4E01", true, "chinese japanese korean"); - - checkHasNamedParameter("no bind variable", false, "no bind variable"); - checkHasNamedParameter(":\u2004whitespace", false, "non basic latin whitespace"); - checkHasNamedParameter("select something from x where id = ?1", false, "indexed parameter"); - checkHasNamedParameter("::", false, "double colon"); - checkHasNamedParameter(":", false, "end of query"); - checkHasNamedParameter(":\u0003", false, "non-printable"); - checkHasNamedParameter(":*", false, "basic latin emoji"); - checkHasNamedParameter("\\:", false, "escaped colon"); - checkHasNamedParameter("::id", false, "double colon with identifier"); - checkHasNamedParameter("\\:id", false, "escaped colon with identifier"); - checkHasNamedParameter("select something from x where id = #something", false, "hash"); + checkHasNamedParameter("select something from x where id = :id", true, "named parameter", true); + checkHasNamedParameter("in the :id middle", true, "middle", false); + checkHasNamedParameter(":id start", true, "beginning", false); + checkHasNamedParameter(":id", true, "alone", false); + checkHasNamedParameter("select something from x where id = :id", true, "named parameter", true); + checkHasNamedParameter(":UPPERCASE", true, "uppercase", false); + checkHasNamedParameter(":lowercase", true, "lowercase", false); + checkHasNamedParameter(":2something", true, "beginning digit", false); + checkHasNamedParameter(":2", true, "only digit", false); + checkHasNamedParameter(":.something", true, "dot", false); + checkHasNamedParameter(":_something", true, "underscore", false); + checkHasNamedParameter(":$something", true, "dollar", false); + checkHasNamedParameter(":\uFE0F", true, "non basic latin emoji", false); // + checkHasNamedParameter(":\u4E01", true, "chinese japanese korean", false); + + checkHasNamedParameter("no bind variable", false, "no bind variable", false); + checkHasNamedParameter(":\u2004whitespace", false, "non basic latin whitespace", false); + checkHasNamedParameter("select something from x where id = ?1", false, "indexed parameter", true); + checkHasNamedParameter("::", false, "double colon", false); + checkHasNamedParameter(":", false, "end of query", false); + checkHasNamedParameter(":\u0003", false, "non-printable", false); + checkHasNamedParameter(":*", false, "basic latin emoji", false); + checkHasNamedParameter("\\:", false, "escaped colon", false); + checkHasNamedParameter("::id", false, "double colon with identifier", false); + checkHasNamedParameter("\\:id", false, "escaped colon with identifier", false); + checkHasNamedParameter("select something from x where id = #something", false, "hash", true); softly.assertAll(); } @@ -429,11 +433,12 @@ void testHasNamedParameter() { @Test // DATAJPA-1235 void ignoresQuotedNamedParameterLookAlike() { - checkNumberOfNamedParameters("select something from blah where x = '0:name'", 0, "single quoted"); - checkNumberOfNamedParameters("select something from blah where x = \"0:name\"", 0, "double quoted"); - checkNumberOfNamedParameters("select something from blah where x = '\"0':name", 1, "double quote in single quotes"); - checkNumberOfNamedParameters("select something from blah where x = \"'0\":name", 1, - "single quote in double quotes"); + checkNumberOfNamedParameters("select something from blah where x = '0:name'", 0, "single quoted", false); + checkNumberOfNamedParameters("select something from blah where x = \"0:name\"", 0, "double quoted", false); + checkNumberOfNamedParameters("select something from blah where x = '\"0':name", 1, "double quote in single quotes", + false); + checkNumberOfNamedParameters("select something from blah where x = \"'0\":name", 1, "single quote in double quotes", + false); softly.assertAll(); } @@ -442,7 +447,7 @@ void ignoresQuotedNamedParameterLookAlike() { void detectsMultiplePositionalParameterBindingsWithoutIndex() { String queryString = "select u from User u where u.id in ? and u.names in ? and foo = ?"; - StringQuery query = new StringQuery(queryString); + StringQuery query = new StringQuery(queryString, false); softly.assertThat(query.getQueryString()).isEqualTo(queryString); softly.assertThat(query.hasParameterBindings()).isTrue(); @@ -464,14 +469,14 @@ void failOnMixedBindingsWithoutIndex() { for (String testQuery : testQueries) { Assertions.assertThatExceptionOfType(IllegalArgumentException.class) // - .describedAs(testQuery).isThrownBy(() -> new StringQuery(testQuery)); + .describedAs(testQuery).isThrownBy(() -> new StringQuery(testQuery, false)); } } @Test // DATAJPA-1307 void makesUsageOfJdbcStyleParameterAvailable() { - softly.assertThat(new StringQuery("something = ?").usesJdbcStyleParameters()).isTrue(); + softly.assertThat(new StringQuery("something = ?", false).usesJdbcStyleParameters()).isTrue(); List testQueries = Arrays.asList( // "something = ?1", // @@ -481,7 +486,7 @@ void makesUsageOfJdbcStyleParameterAvailable() { for (String testQuery : testQueries) { - softly.assertThat(new StringQuery(testQuery) // + softly.assertThat(new StringQuery(testQuery, false) // .usesJdbcStyleParameters()) // .describedAs(testQuery) // .isFalse(); @@ -494,7 +499,7 @@ void makesUsageOfJdbcStyleParameterAvailable() { void questionMarkInStringLiteral() { String queryString = "select '? ' from dual"; - StringQuery query = new StringQuery(queryString); + StringQuery query = new StringQuery(queryString, false); softly.assertThat(query.getQueryString()).isEqualTo(queryString); softly.assertThat(query.hasParameterBindings()).isFalse(); @@ -515,7 +520,7 @@ void isNotDefaultProjection() { "select a, b from C"); for (String queryString : queriesWithoutDefaultProjection) { - softly.assertThat(new StringQuery(queryString).isDefaultProjection()) // + softly.assertThat(new StringQuery(queryString, true).isDefaultProjection()) // .describedAs(queryString) // .isFalse(); } @@ -532,7 +537,7 @@ void isNotDefaultProjection() { ); for (String queryString : queriesWithDefaultProjection) { - softly.assertThat(new StringQuery(queryString).isDefaultProjection()) // + softly.assertThat(new StringQuery(queryString, true).isDefaultProjection()) // .describedAs(queryString) // .isTrue(); } @@ -544,7 +549,7 @@ void isNotDefaultProjection() { void usingPipesWithNamedParameter() { String queryString = "SELECT u FROM User u WHERE u.lastname LIKE '%'||:name||'%'"; - StringQuery query = new StringQuery(queryString); + StringQuery query = new StringQuery(queryString, true); assertThat(query.getParameterBindings()) // .extracting(ParameterBinding::getName) // @@ -555,16 +560,16 @@ void usingPipesWithNamedParameter() { void usingGreaterThanWithNamedParameter() { String queryString = "SELECT u FROM User u WHERE :age>u.age"; - StringQuery query = new StringQuery(queryString); + StringQuery query = new StringQuery(queryString, true); assertThat(query.getParameterBindings()) // .extracting(ParameterBinding::getName) // .containsExactly("age"); } - void checkNumberOfNamedParameters(String query, int expectedSize, String label) { + void checkNumberOfNamedParameters(String query, int expectedSize, String label, boolean nativeQuery) { - DeclaredQuery declaredQuery = DeclaredQuery.of(query); + DeclaredQuery declaredQuery = DeclaredQuery.of(query, nativeQuery); softly.assertThat(declaredQuery.hasNamedParameter()) // .describedAs("hasNamed Parameter " + label) // @@ -574,9 +579,9 @@ void checkNumberOfNamedParameters(String query, int expectedSize, String label) .hasSize(expectedSize); } - private void checkHasNamedParameter(String query, boolean expected, String label) { + private void checkHasNamedParameter(String query, boolean expected, String label, boolean nativeQuery) { - softly.assertThat(new StringQuery(query).hasNamedParameter()) // + softly.assertThat(new StringQuery(query, nativeQuery).hasNamedParameter()) // .describedAs(String.format("<%s> (%s)", query, label)) // .isEqualTo(expected); } From 1f29463cf669c6fdd02d13a2280550a8c108f79b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 22 Feb 2022 14:22:30 -0600 Subject: [PATCH 153/821] Polishing. See #2409. --- pom.xml | 2 +- spring-data-jpa/pom.xml | 2 +- .../query/DefaultQueryEnhancer.java | 20 +- .../query/ExpressionBasedStringQuery.java | 9 +- .../query/JSqlParserQueryEnhancer.java | 82 +++----- .../jpa/repository/query/JSqlParserUtils.java | 62 +----- .../jpa/repository/query/JpaQueryCreator.java | 184 +++++++++--------- .../jpa/repository/query/QueryEnhancer.java | 22 +-- .../query/QueryEnhancerFactory.java | 8 +- .../query/QueryParameterSetter.java | 4 +- .../data/jpa/repository/query/QueryUtils.java | 5 +- .../jpa/repository/query/StringQuery.java | 46 ++--- .../jpa/repository/UserRepositoryTests.java | 92 ++++----- .../ExpressionBasedStringQueryUnitTests.java | 10 +- .../query/JpaQueryMethodUnitTests.java | 9 +- .../query/QueryEnhancerFactoryUnitTests.java | 12 +- .../query/QueryEnhancerUnitTests.java | 81 ++++++-- .../QueryParameterSetterFactoryUnitTests.java | 1 + .../query/QueryUtilsIntegrationTests.java | 38 ++-- .../repository/query/QueryUtilsUnitTests.java | 32 ++- .../query/StringQueryUnitTests.java | 1 - 21 files changed, 329 insertions(+), 393 deletions(-) diff --git a/pom.xml b/pom.xml index a230dab8b6..069cad460c 100644 --- a/pom.xml +++ b/pom.xml @@ -24,11 +24,11 @@ 3.0.2 5.6.0.Final + 4.3 8.0.23 42.2.19 3.0.0-SNAPSHOT 0.10.3 - 4.3 org.hibernate diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index e4f17e4aca..ba9220cc2a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -229,7 +229,7 @@ com.github.jsqlparser jsqlparser - ${jsqlparser.version} + ${jsqlparser} provided true diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java index 7504d81356..7dd4f9ce2a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java @@ -15,14 +15,16 @@ */ package org.springframework.data.jpa.repository.query; -import org.springframework.data.domain.Sort; - import java.util.Set; +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; + /** * The implementation of {@link QueryEnhancer} using {@link QueryUtils}. * * @author Diego Krupitza + * @since 2.7.0 */ public class DefaultQueryEnhancer implements QueryEnhancer { @@ -33,17 +35,7 @@ public DefaultQueryEnhancer(DeclaredQuery query) { } @Override - public String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable idAttributes) { - return QueryUtils.getExistsQueryString(entityName, countQueryPlaceHolder, idAttributes); - } - - @Override - public String getQueryString(String template, String entityName) { - return QueryUtils.getQueryString(template, entityName); - } - - @Override - public String applySorting(Sort sort, String alias) { + public String applySorting(Sort sort, @Nullable String alias) { return QueryUtils.applySorting(this.query.getQueryString(), sort, alias); } @@ -53,7 +45,7 @@ public String detectAlias() { } @Override - public String createCountQueryFor(String countProjection) { + public String createCountQueryFor(@Nullable String countProjection) { return QueryUtils.createCountQueryFor(this.query.getQueryString(), countProjection); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index 74fffaa869..ce399d3212 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -69,19 +69,18 @@ public ExpressionBasedStringQuery(String query, JpaEntityMetadata metadata, S * @param query the original query. Must not be {@literal null}. * @param metadata the {@link JpaEntityMetadata} for the given entity. Must not be {@literal null}. * @param parser Parser for resolving SpEL expressions. Must not be {@literal null}. - * @param nativeQuery + * @param nativeQuery is a given query native or not * @return A query supporting SpEL expressions. */ - static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata metadata, SpelExpressionParser parser, - boolean nativeQuery) { + static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata metadata, + SpelExpressionParser parser, boolean nativeQuery) { return new ExpressionBasedStringQuery(query.getQueryString(), metadata, parser, nativeQuery); } /** - * @param query, the query expression potentially containing a SpEL expression. Must not be {@literal null}.} + * @param query, the query expression potentially containing a SpEL expression. Must not be {@literal null}. * @param metadata the {@link JpaEntityMetadata} for the given entity. Must not be {@literal null}. * @param parser Must not be {@literal null}. - * @return */ private static String renderQueryIfExpressionOrReturnQuery(String query, JpaEntityMetadata metadata, SpelExpressionParser parser) { 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 220ddc868c..3c4d038c06 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,38 +15,44 @@ */ 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 net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Function; -import net.sf.jsqlparser.expression.operators.conditional.AndExpression; -import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.schema.Table; -import net.sf.jsqlparser.statement.select.*; -import net.sf.jsqlparser.util.SelectUtils; +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.SelectExpressionItem; +import net.sf.jsqlparser.statement.select.SelectItem; + +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.data.util.Streamable; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import java.util.*; -import java.util.stream.Collectors; - -import static org.springframework.data.jpa.repository.query.JSqlParserUtils.*; -import static org.springframework.data.jpa.repository.query.QueryUtils.checkSortExpression; - /** * The implementation of {@link QueryEnhancer} using JSqlParser. * * @author Diego Krupitza + * @author Greg Turnquist + * @since 2.7.0 */ public class JSqlParserQueryEnhancer implements QueryEnhancer { - private static final String DEFAULT_TABLE_ALIAS = "x"; - private final DeclaredQuery query; /** @@ -57,40 +63,8 @@ public JSqlParserQueryEnhancer(DeclaredQuery query) { } @Override - public String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable idAttributes) { - final Table tableNameWithAlias = getTableWithAlias(entityName, DEFAULT_TABLE_ALIAS); - Function jSqlCount = getJSqlCount(Collections.singletonList(countQueryPlaceHolder), false); - - Select select = SelectUtils.buildSelectFromTableAndSelectItems(tableNameWithAlias, - new SelectExpressionItem(jSqlCount)); - - PlainSelect selectBody = (PlainSelect) select.getSelectBody(); - - List equalityExpressions = Streamable.of(idAttributes).stream() // - .map(field -> { - Expression tableNameField = new Column().withTable(tableNameWithAlias).withColumnName(field); - Expression inputField = new Column(":".concat(field)); - return new EqualsTo(tableNameField, inputField); - }).collect(Collectors.toList()); - - if (equalityExpressions.size() > 1) { - AndExpression rootOfWhereClause = concatenateWithAndExpression(equalityExpressions); - selectBody.setWhere(rootOfWhereClause); - } else if (equalityExpressions.size() == 1) { - selectBody.setWhere(equalityExpressions.get(0)); - } + public String applySorting(Sort sort, @Nullable String alias) { - return selectBody.toString(); - } - - @Override - public String getQueryString(String template, String entityName) { - Assert.hasText(entityName, "Entity name must not be null or empty!"); - return String.format(template, entityName); - } - - @Override - public String applySorting(Sort sort, String alias) { String queryString = query.getQueryString(); Assert.hasText(queryString, "Query must not be null or empty!"); @@ -145,6 +119,7 @@ private Set getSelectionAliases(PlainSelect selectBody) { * @return a {@literal Set} containing all found aliases. Guaranteed to be not {@literal null}. */ Set getSelectionAliases() { + Select selectStatement = parseSelectStatement(this.query.getQueryString()); PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); return this.getSelectionAliases(selectBody); @@ -189,7 +164,7 @@ private Set getJoinAliases(PlainSelect selectBody) { * @return a {@link OrderByElement} containing an order clause. Guaranteed to be not {@literal null}. */ private OrderByElement getOrderClause(final Set joinAliases, final Set selectionAliases, - final String alias, final Sort.Order order) { + @Nullable final String alias, final Sort.Order order) { final OrderByElement orderByElement = new OrderByElement(); orderByElement.setAsc(order.getDirection().isAscending()); @@ -233,7 +208,9 @@ public String detectAlias() { * @param query must not be {@literal null}. * @return Might return {@literal null}. */ + @Nullable private String detectAlias(String query) { + Select selectStatement = parseSelectStatement(query); PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); return detectAlias(selectBody); @@ -246,13 +223,15 @@ private String detectAlias(String query) { * @param selectBody must not be {@literal null}. * @return Might return {@literal null}. */ + @Nullable private static String detectAlias(PlainSelect selectBody) { + Alias alias = selectBody.getFromItem().getAlias(); return alias == null ? null : alias.getName(); } @Override - public String createCountQueryFor(String countProjection) { + public String createCountQueryFor(@Nullable String countProjection) { Assert.hasText(this.query.getQueryString(), "OriginalQuery must not be null or empty!"); @@ -298,6 +277,7 @@ public String createCountQueryFor(String countProjection) { @Override public String getProjection() { + Assert.hasText(query.getQueryString(), "Query must not be null or empty!"); Select selectStatement = parseSelectStatement(query.getQueryString()); @@ -321,6 +301,7 @@ public Set getJoinAliases() { * @return the parsed query */ private static Select parseSelectStatement(String query) { + try { return (Select) CCJSqlParserUtil.parse(query); } catch (JSQLParserException e) { @@ -329,12 +310,13 @@ private static Select parseSelectStatement(String query) { } /** - * Checks whether a given projection only contains a single column definition (aka without functions, etc) + * Checks whether a given projection only contains a single column definition (aka without functions, etc.) * * @param projection the projection to analyse * @return true when the projection only contains a single column definition otherwise false */ private boolean onlyASingleColumnProjection(List projection) { + // this is unfortunately the only way to check without any hacky & hard string regex magic return projection.size() == 1 && projection.get(0) instanceof SelectExpressionItem && (((SelectExpressionItem) projection.get(0)).getExpression()) instanceof Column; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java index 1cc4eb1d6a..4fbf0bf9ca 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java @@ -15,15 +15,10 @@ */ package org.springframework.data.jpa.repository.query; -import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Function; -import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.schema.Table; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; import java.util.Collections; import java.util.List; @@ -33,58 +28,12 @@ * A utility class for JSqlParser. * * @author Diego Krupitza + * @author Greg Turnquist + * @since 2.7.0 */ public final class JSqlParserUtils { - private JSqlParserUtils() { - } - - /** - * Generates a JSqlParser table from an entity name and an optional alias name - * - * @param entityName the name of the table - * @param alias the optional alias. Might be {@literal null} or empty - * @return the newly generated table - */ - public static Table getTableWithAlias(String entityName, String alias) { - Table table = new Table(entityName); - return StringUtils.hasText(alias) ? table.withAlias(new Alias(alias)) : table; - } - - /** - * Concatenates a list of expression with AND. - * - * @param expressions the list of expressions to concatenate. Has to be non empty and with size >= 2 - * @return the root of the concatenated expression - */ - public static AndExpression concatenateWithAndExpression(List expressions) { - - if (CollectionUtils.isEmpty(expressions) || expressions.size() == 1) { - throw new IllegalArgumentException( - "The list of expression has to be at least of length 2! Otherwise it is not possible to concatinate with an"); - } - - AndExpression rootAndExpression = new AndExpression(); - AndExpression currentLocation = rootAndExpression; - - // traverse the list with looking 1 element ahead - for (int i = 0; i < expressions.size(); i++) { - Expression currentExpression = expressions.get(i); - if (currentLocation.getLeftExpression() == null) { - currentLocation.setLeftExpression(currentExpression); - } else if (currentLocation.getRightExpression() == null && i == expressions.size() - 1) { - currentLocation.setRightExpression(currentExpression); - } else { - AndExpression nextAndExpression = new AndExpression(); - nextAndExpression.setLeftExpression(currentExpression); - - currentLocation.setRightExpression(nextAndExpression); - currentLocation = (AndExpression) currentLocation.getRightExpression(); - } - } - - return rootAndExpression; - } + private JSqlParserUtils() {} /** * Generates a count function call, based on the {@code countFields}. @@ -94,12 +43,14 @@ public static AndExpression concatenateWithAndExpression(List expres * @return the generated count function call */ public static Function getJSqlCount(final List countFields, final boolean distinct) { + List countColumns = countFields // .stream() // .map(Column::new) // .collect(Collectors.toList()); ExpressionList countExpression = new ExpressionList(countColumns); + return new Function() // .withName("count") // .withParameters(countExpression) // @@ -113,11 +64,12 @@ public static Function getJSqlCount(final List countFields, final boolea * @return the generated lower function call */ public static Function getJSqlLower(String column) { + List expressions = Collections.singletonList(new Column(column)); ExpressionList lowerParamExpression = new ExpressionList(expressions); + return new Function() // .withName("lower") // .withParameters(lowerParamExpression); } - } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 4afd0a79ab..c23eef9002 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -55,6 +55,7 @@ * @author Reda.Housni-Alaoui * @author Moritz Becker * @author Andrey Kovalev + * @author Greg Turnquist */ public class JpaQueryCreator extends AbstractQueryCreator, Predicate> { @@ -166,8 +167,10 @@ protected CriteriaQuery complete(@Nullable Predicate predicate Class typeToRead = returnedType.getReturnedType(); - query = typeToRead.isInterface() ? query.multiselect(selections) - : query.select((Selection) builder.construct(typeToRead, selections.toArray(new Selection[0]))); + query = typeToRead.isInterface() // + ? query.multiselect(selections) // + : query.select((Selection) builder.construct(typeToRead, // + selections.toArray(new Selection[0]))); } else if (tree.isExistsProjection()) { @@ -239,83 +242,84 @@ public Predicate build() { Type type = part.getType(); switch (type) { - case BETWEEN: - ParameterMetadata first = provider.next(part); - ParameterMetadata second = provider.next(part); - return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression()); - case AFTER: - case GREATER_THAN: - return builder.greaterThan(getComparablePath(root, part), - provider.next(part, Comparable.class).getExpression()); - case GREATER_THAN_EQUAL: - return builder.greaterThanOrEqualTo(getComparablePath(root, part), - provider.next(part, Comparable.class).getExpression()); - case BEFORE: - case LESS_THAN: - return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression()); - case LESS_THAN_EQUAL: - return builder.lessThanOrEqualTo(getComparablePath(root, part), - provider.next(part, Comparable.class).getExpression()); - case IS_NULL: - return getTypedPath(root, part).isNull(); - case IS_NOT_NULL: - return getTypedPath(root, part).isNotNull(); - case NOT_IN: - // cast required for eclipselink workaround, see DATAJPA-433 - return upperIfIgnoreCase(getTypedPath(root, part)) - .in((Expression>) provider.next(part, Collection.class).getExpression()).not(); - case IN: - // cast required for eclipselink workaround, see DATAJPA-433 - return upperIfIgnoreCase(getTypedPath(root, part)) - .in((Expression>) provider.next(part, Collection.class).getExpression()); - case STARTING_WITH: - case ENDING_WITH: - case CONTAINING: - case NOT_CONTAINING: - - if (property.getLeafProperty().isCollection()) { - - Expression> propertyExpression = traversePath(root, property); - ParameterExpression parameterExpression = provider.next(part).getExpression(); - - // Can't just call .not() in case of negation as EclipseLink chokes on that. - return type.equals(NOT_CONTAINING) ? isNotMember(builder, parameterExpression, propertyExpression) - : isMember(builder, parameterExpression, propertyExpression); - } - - case LIKE: - case NOT_LIKE: - Expression stringPath = getTypedPath(root, part); - Expression propertyExpression = upperIfIgnoreCase(stringPath); - Expression parameterExpression = upperIfIgnoreCase(provider.next(part, String.class).getExpression()); - Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter()); - return type.equals(NOT_LIKE) || type.equals(NOT_CONTAINING) ? like.not() : like; - case TRUE: - Expression truePath = getTypedPath(root, part); - return builder.isTrue(truePath); - case FALSE: - Expression falsePath = getTypedPath(root, part); - return builder.isFalse(falsePath); - case SIMPLE_PROPERTY: - ParameterMetadata expression = provider.next(part); - Expression path = getTypedPath(root, part); - return expression.isIsNullParameter() ? path.isNull() - : builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression())); - case NEGATING_SIMPLE_PROPERTY: - return builder.notEqual(upperIfIgnoreCase(getTypedPath(root, part)), - upperIfIgnoreCase(provider.next(part).getExpression())); - case IS_EMPTY: - case IS_NOT_EMPTY: - - if (!property.getLeafProperty().isCollection()) { - throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties!"); - } - - Expression> collectionPath = traversePath(root, property); - return type.equals(IS_NOT_EMPTY) ? builder.isNotEmpty(collectionPath) : builder.isEmpty(collectionPath); - - default: - throw new IllegalArgumentException("Unsupported keyword " + type); + case BETWEEN: + ParameterMetadata first = provider.next(part); + ParameterMetadata second = provider.next(part); + return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression()); + case AFTER: + case GREATER_THAN: + return builder.greaterThan(getComparablePath(root, part), + provider.next(part, Comparable.class).getExpression()); + case GREATER_THAN_EQUAL: + return builder.greaterThanOrEqualTo(getComparablePath(root, part), + provider.next(part, Comparable.class).getExpression()); + case BEFORE: + case LESS_THAN: + return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression()); + case LESS_THAN_EQUAL: + return builder.lessThanOrEqualTo(getComparablePath(root, part), + provider.next(part, Comparable.class).getExpression()); + case IS_NULL: + return getTypedPath(root, part).isNull(); + case IS_NOT_NULL: + return getTypedPath(root, part).isNotNull(); + case NOT_IN: + // cast required for eclipselink workaround, see DATAJPA-433 + return upperIfIgnoreCase(getTypedPath(root, part)) + .in((Expression>) provider.next(part, Collection.class).getExpression()).not(); + case IN: + // cast required for eclipselink workaround, see DATAJPA-433 + return upperIfIgnoreCase(getTypedPath(root, part)) + .in((Expression>) provider.next(part, Collection.class).getExpression()); + case STARTING_WITH: + case ENDING_WITH: + case CONTAINING: + case NOT_CONTAINING: + + if (property.getLeafProperty().isCollection()) { + + Expression> propertyExpression = traversePath(root, property); + ParameterExpression parameterExpression = provider.next(part).getExpression(); + + // Can't just call .not() in case of negation as EclipseLink chokes on that. + return type.equals(NOT_CONTAINING) // + ? isNotMember(builder, parameterExpression, propertyExpression) // + : isMember(builder, parameterExpression, propertyExpression); + } + + case LIKE: + case NOT_LIKE: + Expression stringPath = getTypedPath(root, part); + Expression propertyExpression = upperIfIgnoreCase(stringPath); + Expression parameterExpression = upperIfIgnoreCase(provider.next(part, String.class).getExpression()); + Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter()); + return type.equals(NOT_LIKE) || type.equals(NOT_CONTAINING) ? like.not() : like; + case TRUE: + Expression truePath = getTypedPath(root, part); + return builder.isTrue(truePath); + case FALSE: + Expression falsePath = getTypedPath(root, part); + return builder.isFalse(falsePath); + case SIMPLE_PROPERTY: + ParameterMetadata expression = provider.next(part); + Expression path = getTypedPath(root, part); + return expression.isIsNullParameter() ? path.isNull() + : builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression())); + case NEGATING_SIMPLE_PROPERTY: + return builder.notEqual(upperIfIgnoreCase(getTypedPath(root, part)), + upperIfIgnoreCase(provider.next(part).getExpression())); + case IS_EMPTY: + case IS_NOT_EMPTY: + + if (!property.getLeafProperty().isCollection()) { + throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties!"); + } + + Expression> collectionPath = traversePath(root, property); + return type.equals(IS_NOT_EMPTY) ? builder.isNotEmpty(collectionPath) : builder.isEmpty(collectionPath); + + default: + throw new IllegalArgumentException("Unsupported keyword " + type); } } @@ -340,22 +344,22 @@ private Expression upperIfIgnoreCase(Expression expression) switch (part.shouldIgnoreCase()) { - case ALWAYS: + case ALWAYS: - Assert.state(canUpperCase(expression), "Unable to ignore case of " + expression.getJavaType().getName() - + " types, the property '" + part.getProperty().getSegment() + "' must reference a String"); - return (Expression) builder.upper((Expression) expression); + Assert.state(canUpperCase(expression), "Unable to ignore case of " + expression.getJavaType().getName() + + " types, the property '" + part.getProperty().getSegment() + "' must reference a String"); + return (Expression) builder.upper((Expression) expression); - case WHEN_POSSIBLE: + case WHEN_POSSIBLE: - if (canUpperCase(expression)) { - return (Expression) builder.upper((Expression) expression); - } + if (canUpperCase(expression)) { + return (Expression) builder.upper((Expression) expression); + } - case NEVER: - default: + case NEVER: + default: - return (Expression) expression; + return (Expression) expression; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java index b8fba6e998..1c470ae671 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java @@ -24,27 +24,11 @@ * This interface describes the API for enhancing a given Query. * * @author Diego Krupitza + * @author Greg Turnquist + * @since 2.7.0 */ public interface QueryEnhancer { - /** - * Returns the query string to execute an exists query for the given id attributes. - * - * @param entityName the name of the entity to create the query for, must not be {@literal null}. - * @param countQueryPlaceHolder the placeholder for the count clause, must not be {@literal null}. - * @param idAttributes the id attributes for the entity, must not be {@literal null}. - */ - String getExistsQueryString(String entityName, String countQueryPlaceHolder, Iterable idAttributes); - - /** - * Returns the query string for the given class name. - * - * @param template must not be {@literal null}. - * @param entityName must not be {@literal null}. - * @return the template with placeholders replaced by the {@literal entityName}. Guaranteed to be not {@literal null}. - */ - String getQueryString(String template, String entityName); - /** * Adds {@literal order by} clause to the JPQL query. Uses the first alias to bind the sorting property to. * @@ -110,7 +94,7 @@ default boolean hasConstructorExpression() { /** * Gets the query we want to use for enhancements. * - * @return non null {@link DeclaredQuery} that wraps the query + * @return non-null {@link DeclaredQuery} that wraps the query */ DeclaredQuery getQuery(); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java index d6123ddee5..156a49aeec 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java @@ -22,6 +22,8 @@ * Encapsulates different strategies for the creation of a {@link QueryEnhancer} from a {@link DeclaredQuery}. * * @author Diego Krupitza + * @author Greg Turnquist + * @since 2.7.0 */ public final class QueryEnhancerFactory { @@ -29,8 +31,7 @@ public final class QueryEnhancerFactory { private static final boolean JSQLPARSER_IN_CLASSPATH = isJSqlParserInClassPath(); - private QueryEnhancerFactory() { - } + private QueryEnhancerFactory() {} /** * Creates a new {@link QueryEnhancer} for the given {@link DeclaredQuery}. @@ -39,6 +40,7 @@ private QueryEnhancerFactory() { * @return an implementation of {@link QueryEnhancer} that suits the query the most */ public static QueryEnhancer forQuery(DeclaredQuery query) { + if (qualifiesForJSqlParserUsage(query)) { return new JSqlParserQueryEnhancer(query); } else { @@ -63,6 +65,7 @@ private static boolean qualifiesForJSqlParserUsage(DeclaredQuery query) { * @return true when in classpath otherwise false */ private static boolean isJSqlParserInClassPath() { + try { Class.forName("net.sf.jsqlparser.parser.JSqlParser", false, QueryEnhancerFactory.class.getClassLoader()); LOG.info("JSqlParser is in classpath. If applicable JSqlParser will be used."); @@ -71,5 +74,4 @@ private static boolean isJSqlParserInClassPath() { return false; } } - } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index d14fea67e8..8bc189e310 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -32,7 +32,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -49,8 +48,7 @@ interface QueryParameterSetter { void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling); /** Noop implementation */ - QueryParameterSetter NOOP = (query, values, errorHandling) -> { - }; + QueryParameterSetter NOOP = (query, values, errorHandling) -> {}; /** * {@link QueryParameterSetter} for named or indexed parameters that might have a {@link TemporalType} specified. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index dc20aad839..d68068082a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -666,8 +666,8 @@ static Expression toExpressionRecursively(From from, PropertyPath p } /** - * Checks if this attribute requires an outer join. This is the case eg. if it hadn't already been fetched with an - * inner join and if it's an a optional association, and if previous paths has already required outer joins. It also + * Checks if this attribute requires an outer join. This is the case e.g. if it hadn't already been fetched with an + * inner join and if it's an optional association, and if previous paths has already required outer joins. It also * ensures outer joins are used even when Hibernate defaults to inner joins (HHH-12712 and HHH-12999). * * @param from the {@link From} to check for fetches. @@ -741,6 +741,7 @@ private static boolean requiresOuterJoin(From from, PropertyPath property, return hasRequiredOuterJoin || getAnnotationProperty(attribute, "optional", true); } + @Nullable private static T getAnnotationProperty(Attribute attribute, String propertyName, T defaultValue) { Class associationAnnotation = ASSOCIATION_TYPES.get(attribute.getPersistentAttributeType()); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 37f3af731a..60ca72af33 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -101,10 +101,9 @@ public List getParameterBindings() { } @Override - @SuppressWarnings("deprecation") public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable String countQueryProjection) { - return DeclaredQuery.of( + return DeclaredQuery.of( // countQuery != null ? countQuery : this.queryEnhancer.createCountQueryFor(countQueryProjection), // this.isNative); } @@ -259,36 +258,37 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St switch (ParameterBindingType.of(typeSource)) { - case LIKE: + case LIKE: - Type likeType = LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); - replacement = matcher.group(3); + Type likeType = LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); + replacement = matcher.group(3); - if (parameterIndex != null) { - checkAndRegister(new LikeParameterBinding(parameterIndex, likeType, expression), bindings); - } else { - checkAndRegister(new LikeParameterBinding(parameterName, likeType, expression), bindings); + if (parameterIndex != null) { + checkAndRegister(new LikeParameterBinding(parameterIndex, likeType, expression), bindings); + } else { + checkAndRegister(new LikeParameterBinding(parameterName, likeType, expression), bindings); - replacement = ":" + parameterName; - } + replacement = ":" + parameterName; + } - break; + break; - case IN: + case IN: - if (parameterIndex != null) { - checkAndRegister(new InParameterBinding(parameterIndex, expression), bindings); - } else { - checkAndRegister(new InParameterBinding(parameterName, expression), bindings); - } + if (parameterIndex != null) { + checkAndRegister(new InParameterBinding(parameterIndex, expression), bindings); + } else { + checkAndRegister(new InParameterBinding(parameterName, expression), bindings); + } - break; + break; - case AS_IS: // fall-through we don't need a special parameter binding for the given parameter. - default: + case AS_IS: // fall-through we don't need a special parameter binding for the given parameter. + default: - bindings.add(parameterIndex != null ? new ParameterBinding(null, parameterIndex, expression) - : new ParameterBinding(parameterName, null, expression)); + bindings.add(parameterIndex != null // + ? new ParameterBinding(null, parameterIndex, expression) // + : new ParameterBinding(parameterName, null, expression)); } if (replacement != null) { 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 b9b7e1cf25..52dc7c7308 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 @@ -57,8 +57,6 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; -import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; @@ -103,12 +101,10 @@ @Transactional public class UserRepositoryTests { - @PersistenceContext - EntityManager em; + @PersistenceContext EntityManager em; // CUT - @Autowired - UserRepository repository; + @Autowired UserRepository repository; // Test fixture private User firstUser; @@ -147,7 +143,7 @@ void testCreation() { } @Test - void testRead() throws Exception { + void testRead() { flushTestUsers(); @@ -172,7 +168,7 @@ void testReadByIdReturnsNullForNotFoundEntities() { } @Test - void savesCollectionCorrectly() throws Exception { + void savesCollectionCorrectly() { assertThat(repository.saveAll(asList(firstUser, secondUser, thirdUser))) // .containsExactlyInAnyOrder(firstUser, secondUser, thirdUser); @@ -186,7 +182,7 @@ void savesAndFlushesCollectionCorrectly() { } @Test - void savingEmptyCollectionIsNoOp() throws Exception { + void savingEmptyCollectionIsNoOp() { assertThat(repository.saveAll(new ArrayList<>())).isEmpty(); } @@ -207,7 +203,7 @@ void testUpdate() { } @Test - void existReturnsWhetherAnEntityCanBeLoaded() throws Exception { + void existReturnsWhetherAnEntityCanBeLoaded() { flushTestUsers(); assertThat(repository.existsById(id)).isTrue(); @@ -237,7 +233,7 @@ void testDelete() { } @Test - void returnsAllSortedCorrectly() throws Exception { + void returnsAllSortedCorrectly() { flushTestUsers(); @@ -246,7 +242,7 @@ void returnsAllSortedCorrectly() throws Exception { } @Test // DATAJPA-296 - void returnsAllIgnoreCaseSortedCorrectly() throws Exception { + void returnsAllIgnoreCaseSortedCorrectly() { flushTestUsers(); @@ -306,7 +302,7 @@ void deleteEmptyCollectionDoesNotDeleteAnything() { } @Test - void executesManipulatingQuery() throws Exception { + void executesManipulatingQuery() { flushTestUsers(); repository.renameAllUsersTo("newLastname"); @@ -320,11 +316,11 @@ void testFinderInvocationWithNullParameter() { flushTestUsers(); - repository.findByLastname((String) null); + repository.findByLastname(null); } @Test - void testFindByLastname() throws Exception { + void testFindByLastname() { flushTestUsers(); @@ -335,7 +331,7 @@ void testFindByLastname() throws Exception { * Tests, that searching by the email address of the reference user returns exactly that instance. */ @Test - void testFindByEmailAddress() throws Exception { + void testFindByEmailAddress() { flushTestUsers(); @@ -358,7 +354,7 @@ void testReadAll() { * Tests that all users get deleted by triggering {@link UserRepository#deleteAll()}. */ @Test - void deleteAll() throws Exception { + void deleteAll() { flushTestUsers(); @@ -472,14 +468,14 @@ void executesSpecificationCorrectly() { } @Test - void executesSingleEntitySpecificationCorrectly() throws Exception { + void executesSingleEntitySpecificationCorrectly() { flushTestUsers(); assertThat(repository.findOne(userHasFirstname("Oliver"))).contains(firstUser); } @Test - void returnsNullIfNoEntityFoundForSingleEntitySpecification() throws Exception { + void returnsNullIfNoEntityFoundForSingleEntitySpecification() { flushTestUsers(); assertThat(repository.findOne(userHasLastname("Beauford"))).isNotPresent(); @@ -524,7 +520,7 @@ void executesCombinedSpecificationsWithPageableCorrectly() { } @Test - void executesMethodWithAnnotatedNamedParametersCorrectly() throws Exception { + void executesMethodWithAnnotatedNamedParametersCorrectly() { firstUser = repository.save(firstUser); secondUser = repository.save(secondUser); @@ -533,7 +529,7 @@ void executesMethodWithAnnotatedNamedParametersCorrectly() throws Exception { } @Test - void executesMethodWithNamedParametersCorrectlyOnMethodsWithQueryCreation() throws Exception { + void executesMethodWithNamedParametersCorrectlyOnMethodsWithQueryCreation() { firstUser = repository.save(firstUser); secondUser = repository.save(secondUser); @@ -542,7 +538,7 @@ void executesMethodWithNamedParametersCorrectlyOnMethodsWithQueryCreation() thro } @Test - void executesLikeAndOrderByCorrectly() throws Exception { + void executesLikeAndOrderByCorrectly() { flushTestUsers(); @@ -551,7 +547,7 @@ void executesLikeAndOrderByCorrectly() throws Exception { } @Test - void executesNotLikeCorrectly() throws Exception { + void executesNotLikeCorrectly() { flushTestUsers(); @@ -559,7 +555,7 @@ void executesNotLikeCorrectly() throws Exception { } @Test - void executesSimpleNotCorrectly() throws Exception { + void executesSimpleNotCorrectly() { flushTestUsers(); @@ -567,21 +563,21 @@ void executesSimpleNotCorrectly() throws Exception { } @Test - void returnsSameListIfNoSpecGiven() throws Exception { + void returnsSameListIfNoSpecGiven() { flushTestUsers(); assertSameElements(repository.findAll(), repository.findAll((Specification) null)); } @Test - void returnsSameListIfNoSortIsGiven() throws Exception { + void returnsSameListIfNoSortIsGiven() { flushTestUsers(); assertSameElements(repository.findAll(Sort.unsorted()), repository.findAll()); } @Test - void returnsSamePageIfNoSpecGiven() throws Exception { + void returnsSamePageIfNoSpecGiven() { Pageable pageable = PageRequest.of(0, 1); @@ -590,14 +586,14 @@ void returnsSamePageIfNoSpecGiven() throws Exception { } @Test - void returnsAllAsPageIfNoPageableIsGiven() throws Exception { + void returnsAllAsPageIfNoPageableIsGiven() { flushTestUsers(); assertThat(repository.findAll(Pageable.unpaged())).isEqualTo(new PageImpl<>(repository.findAll())); } @Test - void removeDetachedObject() throws Exception { + void removeDetachedObject() { flushTestUsers(); @@ -608,14 +604,14 @@ void removeDetachedObject() throws Exception { } @Test - void executesPagedSpecificationsCorrectly() throws Exception { + void executesPagedSpecificationsCorrectly() { Page result = executeSpecWithSort(Sort.unsorted()); assertThat(result.getContent()).isSubsetOf(firstUser, thirdUser); } @Test - void executesPagedSpecificationsWithSortCorrectly() throws Exception { + void executesPagedSpecificationsWithSortCorrectly() { Page result = executeSpecWithSort(Sort.by(Direction.ASC, "lastname")); @@ -623,7 +619,7 @@ void executesPagedSpecificationsWithSortCorrectly() throws Exception { } @Test - void executesPagedSpecificationWithSortCorrectly2() throws Exception { + void executesPagedSpecificationWithSortCorrectly2() { Page result = executeSpecWithSort(Sort.by(Direction.DESC, "lastname")); @@ -631,7 +627,7 @@ void executesPagedSpecificationWithSortCorrectly2() throws Exception { } @Test - void executesQueryMethodWithDeepTraversalCorrectly() throws Exception { + void executesQueryMethodWithDeepTraversalCorrectly() { flushTestUsers(); @@ -644,7 +640,7 @@ void executesQueryMethodWithDeepTraversalCorrectly() throws Exception { } @Test - void executesFindByColleaguesLastnameCorrectly() throws Exception { + void executesFindByColleaguesLastnameCorrectly() { flushTestUsers(); @@ -658,7 +654,7 @@ void executesFindByColleaguesLastnameCorrectly() throws Exception { } @Test - void executesFindByNotNullLastnameCorrectly() throws Exception { + void executesFindByNotNullLastnameCorrectly() { flushTestUsers(); @@ -666,7 +662,7 @@ void executesFindByNotNullLastnameCorrectly() throws Exception { } @Test - void executesFindByNullLastnameCorrectly() throws Exception { + void executesFindByNullLastnameCorrectly() { flushTestUsers(); User forthUser = repository.save(new User("Foo", null, "email@address.com")); @@ -675,7 +671,7 @@ void executesFindByNullLastnameCorrectly() throws Exception { } @Test - void findsSortedByLastname() throws Exception { + void findsSortedByLastname() { flushTestUsers(); @@ -1676,7 +1672,7 @@ void shouldfindUsersBySpELExpressionParametersWithSpelTemplateExpression() { } @Test // DATAJPA-606 - void findByEmptyCollectionOfStrings() throws Exception { + void findByEmptyCollectionOfStrings() { flushTestUsers(); @@ -1685,7 +1681,7 @@ void findByEmptyCollectionOfStrings() throws Exception { } @Test // DATAJPA-606 - void findByEmptyCollectionOfIntegers() throws Exception { + void findByEmptyCollectionOfIntegers() { flushTestUsers(); @@ -1694,7 +1690,7 @@ void findByEmptyCollectionOfIntegers() throws Exception { } @Test // DATAJPA-606 - void findByEmptyArrayOfIntegers() throws Exception { + void findByEmptyArrayOfIntegers() { flushTestUsers(); @@ -1736,7 +1732,7 @@ void supportsJava8StreamForPageableMethod() { flushTestUsers(); - try (Stream stream = repository.streamAllPaged(PageRequest.of(0, 2));) { + try (Stream stream = repository.streamAllPaged(PageRequest.of(0, 2))) { assertThat(stream).hasSize(2); } } @@ -2088,13 +2084,11 @@ void findByFluentExampleOneValue() { User prototype = new User(); prototype.setFirstname("v"); - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> { - repository.findBy( - of(prototype, - matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", - GenericPropertyMatcher::contains)), // - q -> q.sortBy(Sort.by("firstname")).oneValue()); - }); + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy(() -> repository.findBy( + of(prototype, + matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname", + GenericPropertyMatcher::contains)), // + q -> q.sortBy(Sort.by("firstname")).oneValue())); } @Test // GH-2294 @@ -2552,7 +2546,7 @@ void returnsNullValueInMap() { } @Test // DATAJPA-1307 - void testFindByEmailAddressJdbcStyleParameter() throws Exception { + void testFindByEmailAddressJdbcStyleParameter() { flushTestUsers(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index ac81488503..677cbc4f25 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -24,7 +24,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.expression.spel.standard.SpelExpressionParser; /** @@ -42,8 +41,7 @@ class ExpressionBasedStringQueryUnitTests { private static final SpelExpressionParser SPEL_PARSER = new SpelExpressionParser(); - @Mock - JpaEntityMetadata metadata; + @Mock JpaEntityMetadata metadata; @Test // DATAJPA-170 void shouldReturnQueryWithDomainTypeExpressionReplacedWithSimpleDomainTypeName() { @@ -93,6 +91,7 @@ void shouldDetectBindParameterCountCorrectlyWithJDBCStyleParameters() { @Test void shouldDetectComplexNativeQueriesWithSpelAsNonNative() { + StringQuery query = new ExpressionBasedStringQuery( "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )\"\n" + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)\"\n" @@ -105,16 +104,17 @@ void shouldDetectComplexNativeQueriesWithSpelAsNonNative() { @Test void shouldDetectSimpleNativeQueriesWithSpelAsNonNative() { + StringQuery query = new ExpressionBasedStringQuery("select n from #{#entityName} n", metadata, SPEL_PARSER, true); assertThat(query.isNativeQuery()).isFalse(); } @Test - void shouldDetectSimpleNativeQueriesWithoutSpelAsNonNative() { + void shouldDetectSimpleNativeQueriesWithoutSpelAsNative() { + StringQuery query = new ExpressionBasedStringQuery("select u from User u", metadata, SPEL_PARSER, true); assertThat(query.isNativeQuery()).isTrue(); } - } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 3488212569..f6964d9b94 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -35,7 +35,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.core.annotation.AliasFor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -74,10 +73,8 @@ public class JpaQueryMethodUnitTests { private static final String METHOD_NAME = "findByFirstname"; - @Mock - QueryExtractor extractor; - @Mock - RepositoryMetadata metadata; + @Mock QueryExtractor extractor; + @Mock RepositoryMetadata metadata; private ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); private Method invalidReturnType; @@ -520,7 +517,7 @@ interface InvalidRepository extends Repository { interface ValidRepository extends Repository { - @Query(value = "Select u from User u where u.lastname = ?1", nativeQuery = true) + @Query(value = "select u from User u where u.lastname = ?1", nativeQuery = true) List findByLastname(String lastname); @Query(name = "HateoasAwareSpringDataWebConfiguration.bar") diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java index 1eb04239e7..a6248e5a6c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java @@ -15,9 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; /** * Unit tests for {@link QueryEnhancerFactory}. @@ -28,18 +28,22 @@ class QueryEnhancerFactoryUnitTests { @Test void createsDefaultImplementationForNonNativeQuery() { - StringQuery query = new StringQuery("Select new User(u.firstname) from User u", false); + + StringQuery query = new StringQuery("select new User(u.firstname) from User u", false); QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query); + assertThat(queryEnhancer) // .isInstanceOf(DefaultQueryEnhancer.class); } @Test void createsJSqlImplementationForNativeQuery() { - StringQuery query = new StringQuery("Select * from User", true); + + StringQuery query = new StringQuery("select * from User", true); QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query); + assertThat(queryEnhancer) // .isInstanceOf(JSqlParserQueryEnhancer.class); } 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 7ba11d74b7..e40535c1ee 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 @@ -15,6 +15,14 @@ */ package org.springframework.data.jpa.repository.query; +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -24,16 +32,8 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.JpaSort; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.*; - /** - * Unit test for {@link QueryEnhancer}. + * Unit tests for {@link QueryEnhancer}. * * @author Diego Krupitza */ @@ -114,6 +114,7 @@ void detectsAliasWithUCorrectly(DeclaredQuery query, String alias) { } public static Stream detectsAliasWithUCorrectlySource() { + return Stream.of( // Arguments.of(new StringQuery(QUERY, true), "u"), // Arguments.of(new StringQuery(SIMPLE_QUERY, false), "u"), // @@ -130,6 +131,7 @@ public static Stream detectsAliasWithUCorrectlySource() { void allowsFullyQualifiedEntityNamesInQuery() { StringQuery query = new StringQuery(FQ_QUERY, true); + assertThat(getEnhancer(query).detectAlias()).isEqualTo("u"); assertCountQuery(FQ_QUERY, "select count(u) from org.acme.domain.User$Foo_Bar u", true); } @@ -138,6 +140,7 @@ void allowsFullyQualifiedEntityNamesInQuery() { void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { StringQuery query = new StringQuery("select p from Person p left join p.address address", true); + endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("address.city")), "order by address.city asc"); endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("address.city", "lastname"), "p"), "order by address.city asc, p.lastname asc"); @@ -147,6 +150,7 @@ void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { void extendsExistingOrderByClausesCorrectly() { StringQuery query = new StringQuery("select p from Person p order by p.lastname asc", true); + endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("firstname"), "p"), "order by p.lastname asc, p.firstname asc"); } @@ -157,6 +161,7 @@ void appliesIgnoreCaseOrderingCorrectly() { Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); StringQuery query = new StringQuery("select p from Person p", true); + endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by lower(p.firstname) asc"); } @@ -166,11 +171,12 @@ void appendsIgnoreCaseOrderingCorrectly() { Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); StringQuery query = new StringQuery("select p from Person p order by p.lastname asc", true); + endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by p.lastname asc, lower(p.firstname) asc"); } @Test // DATAJPA-342 - void usesReturnedVariableInCOuntProjectionIfSet() { + void usesReturnedVariableInCountProjectionIfSet() { assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", "select count(distinct m.genre) from Media m where m.user = ?1", true); @@ -185,6 +191,7 @@ void projectsCountQueriesForQueriesWithSubSelects() { @Test // DATAJPA-148 void doesNotPrefixSortsIfFunction() { + StringQuery query = new StringQuery("select p from Person p", true); Sort sort = Sort.by("sum(foo)"); @@ -207,6 +214,7 @@ void findsExistingOrderByIndependentOfCase() { Sort sort = Sort.by("lastname"); StringQuery originalQuery = new StringQuery("select p from Person p ORDER BY p.firstname", true); String query = getEnhancer(originalQuery).applySorting(sort, "p"); + endsIgnoringCase(query, "ORDER BY p.firstname, p.lastname asc"); } @@ -222,15 +230,17 @@ void createsCountQueryForScalarSelects() { @Test // DATAJPA-456 void createCountQueryFromTheGivenCountProjection() { + StringQuery query = new StringQuery("select p.lastname,p.firstname from Person p", true); + assertThat(getEnhancer(query).createCountQueryFor("p.lastname")) .isEqualToIgnoringCase("select count(p.lastname) from Person p"); } @Test // DATAJPA-726 - void detectsAliassesInPlainJoins() { + void detectsAliasesInPlainJoins() { - StringQuery query = new StringQuery("select p from Customer c join c.productOrder p where p.delaye = true", true); + StringQuery query = new StringQuery("select p from Customer c join c.productOrder p where p.delay = true", true); Sort sort = Sort.by("p.lineItems"); endsIgnoringCase(getEnhancer(query).applySorting(sort, "c"), "order by p.lineItems asc"); @@ -238,13 +248,17 @@ void detectsAliassesInPlainJoins() { @Test // DATAJPA-736 void supportsNonAsciiCharactersInEntityNames() { + StringQuery query = new StringQuery("select u from Usèr u", true); + assertThat(getEnhancer(query).createCountQueryFor()).isEqualToIgnoringCase("select count(u) from Usèr u"); } @Test // DATAJPA-798 void detectsAliasInQueryContainingLineBreaks() { + StringQuery query = new StringQuery("select \n u \n from \n User \nu", true); + assertThat(getEnhancer(query).detectAlias()).isEqualTo("u"); } @@ -259,6 +273,7 @@ void doesPrefixPropertyWithNonNative() { @Test // DATAJPA-815 void doesPrefixPropertyWithNative() { + StringQuery query = new StringQuery("Select * from Cat c join Dog d", true); Sort sort = Sort.by("dPropertyStartingWithJoinAlias"); @@ -267,7 +282,9 @@ void doesPrefixPropertyWithNative() { @Test // DATAJPA-938 void detectsConstructorExpressionInDistinctQuery() { + StringQuery query = new StringQuery("select distinct new Foo() from Bar b", false); + assertThat(getEnhancer(query).hasConstructorExpression()).isTrue(); } @@ -286,19 +303,25 @@ void detectsComplexConstructorExpression() { @Test // DATAJPA-938 void detectsConstructorExpressionWithLineBreaks() { + StringQuery query = new StringQuery("select new foo.bar.FooBar(\na.id) from DtoA a ", false); + assertThat(getEnhancer(query).hasConstructorExpression()).isTrue(); } @Test // DATAJPA-960 void doesNotQualifySortIfNoAliasDetectedNonNative() { + StringQuery query = new StringQuery("from mytable where ?1 is null", false); + assertThat(getEnhancer(query).applySorting(Sort.by("firstname"))).endsWith("order by firstname asc"); } @Test // DATAJPA-960 void doesNotQualifySortIfNoAliasDetectedNative() { + StringQuery query = new StringQuery("Select * from mytable where ?1 is null", true); + endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("firstname")), "order by firstname asc"); } @@ -308,15 +331,17 @@ void doesNotAllowWhitespaceInSort() { StringQuery query = new StringQuery("select p from Person p", true); Sort sort = Sort.by("case when foo then bar"); + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) .isThrownBy(() -> getEnhancer(query).applySorting(sort, "p")); } @Test // DATAJPA-965, DATAJPA-970 - void doesNotPrefixUnsageJpaSortFunctionCalls() { + void doesNotPrefixUnsafeJpaSortFunctionCalls() { JpaSort sort = JpaSort.unsafe("sum(foo)"); StringQuery query = new StringQuery("select p from Person p", true); + endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by sum(foo) asc"); } @@ -349,7 +374,7 @@ void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { } @Test // DATAJPA-965, DATAJPA-970 - void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainesAliasedFunctionForDifferentProperty() { + void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainsAliasedFunctionForDifferentProperty() { StringQuery query = new StringQuery("SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m", true); Sort sort = Sort.by("name", "avgPrice"); @@ -464,11 +489,13 @@ void createCountQuerySupportsWhitespaceCharacters() { @Test void createCountQuerySupportsLineBreaksInSelectClause() { + StringQuery query = new StringQuery("select user.age,\n" + // " user.name\n" + // " from User user\n" + // " where user.age = 18\n" + // " order\nby\nuser.name\n ", true); + assertThat(getEnhancer(query).createCountQueryFor()) .isEqualToIgnoringCase("select count(user) from User user where user.age = 18"); } @@ -562,12 +589,11 @@ void detectsAliasWithGroupAndOrderByWithLineBreaks() { @MethodSource("findProjectionClauseWithDistinctSource") void findProjectionClauseWithDistinct(DeclaredQuery query, String expected) { - SoftAssertions.assertSoftly(sofly -> { - sofly.assertThat(getEnhancer(query).getProjection()).isEqualTo(expected); - }); + SoftAssertions.assertSoftly(sofly -> sofly.assertThat(getEnhancer(query).getProjection()).isEqualTo(expected)); } public static Stream findProjectionClauseWithDistinctSource() { + return Stream.of( // Arguments.of(new StringQuery("select * from x", true), "*"), // Arguments.of(new StringQuery("select a, b, c from x", true), "a, b, c"), // @@ -591,17 +617,21 @@ void findProjectionClauseWithSubselectNative() { // This is a required behavior the testcase in #findProjectionClauseWithSubselect tells why String queryString = "select * from (select x from y)"; StringQuery query = new StringQuery(queryString, true); + assertThat(getEnhancer(query).getProjection()).isEqualTo("*"); } @Test // DATAJPA-1696 void findProjectionClauseWithIncludedFrom() { + StringQuery query = new StringQuery("select x, frommage, y from t", true); + assertThat(getEnhancer(query).getProjection()).isEqualTo("x, frommage, y"); } @Test - void countProjectionDistrinctQueryIncludesNewLineAfterFromAndBeforeJoin() { + void countProjectionDistinctQueryIncludesNewLineAfterFromAndBeforeJoin() { + StringQuery originalQuery = new StringQuery( "SELECT DISTINCT entity1\nFROM Entity1 entity1\nLEFT JOIN Entity2 entity2 ON entity1.key = entity2.key", true); @@ -611,17 +641,21 @@ void countProjectionDistrinctQueryIncludesNewLineAfterFromAndBeforeJoin() { @Test void countProjectionDistinctQueryIncludesNewLineAfterEntity() { + StringQuery originalQuery = new StringQuery( "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key", true); + assertCountQuery(originalQuery, "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"); } @Test void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { + StringQuery originalQuery = new StringQuery( "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799", true); + assertCountQuery(originalQuery, "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key where entity1.id = 1799"); } @@ -651,6 +685,7 @@ void detectsJoinAliasesCorrectly(String queryString, List aliases) { @Test // GH-2441 void correctFunctionAliasWithComplexNestedFunctions() { + String queryString = "\nSELECT \nCAST(('{' || string_agg(distinct array_to_string(c.institutes_ids, ','), ',') || '}') AS bigint[]) as institutesIds\nFROM\ncity c"; StringQuery nativeQuery = new StringQuery(queryString, true); @@ -661,9 +696,11 @@ void correctFunctionAliasWithComplexNestedFunctions() { @Test // GH-2441 void correctApplySortOnComplexNestedFunctionQuery() { - String queryString = "SELECT dd.institutesIds FROM (\n" + " SELECT\n" + String queryString = "SELECT dd.institutesIds FROM (\n" // + + " SELECT\n" // + " CAST(('{' || string_agg(distinct array_to_string(c.institutes_ids, ','), ',') || '}') AS bigint[]) as institutesIds\n" - + " FROM\n" + " city c\n" + + " FROM\n" // + + " city c\n" // + " ) dd"; StringQuery nativeQuery = new StringQuery(queryString, true); @@ -671,10 +708,12 @@ void correctApplySortOnComplexNestedFunctionQuery() { QueryEnhancer queryEnhancer = getEnhancer(nativeQuery); String result = queryEnhancer.applySorting(Sort.by(new Sort.Order(Sort.Direction.ASC, "institutesIds"))); + assertThat(result).containsIgnoringCase("order by dd.institutesIds"); } public static Stream detectsJoinAliasesCorrectlySource() { + return Stream.of( // Arguments.of("select p from Person p left outer join x.foo b2_$ar", Collections.singletonList("b2_$ar")), // Arguments.of("select p from Person p left join x.foo b2_$ar", Collections.singletonList("b2_$ar")), // @@ -695,6 +734,7 @@ private static void assertCountQuery(StringQuery originalQuery, String countQuer } private static void endsIgnoringCase(String original, String endWithIgnoreCase) { + // https://github.com/assertj/assertj-core/pull/2451 // can be removed when upgrading to version 3.23.0 assertJ assertThat(original.toUpperCase()).endsWith(endWithIgnoreCase.toUpperCase()); @@ -703,5 +743,4 @@ private static void endsIgnoringCase(String original, String endWithIgnoreCase) private static QueryEnhancer getEnhancer(DeclaredQuery query) { return QueryEnhancerFactory.forQuery(query); } - } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java index a0081a413e..ee126b0396 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java @@ -34,6 +34,7 @@ * * @author Jens Schauder * @author Mark Paluch + * @author Diego Krupitza */ class QueryParameterSetterFactoryUnitTests { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index d048d70bbd..11776da9fd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -46,7 +46,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; - import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.jpa.domain.sample.Category; @@ -66,13 +65,13 @@ * @author Sébastien Péralta * @author Jens Schauder * @author Patrice Blanchardie + * @author Diego Krupitza */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") public class QueryUtilsIntegrationTests { - @PersistenceContext - EntityManager em; + @PersistenceContext EntityManager em; @Test // DATAJPA-403 void reusesExistingJoinForExpression() { @@ -183,7 +182,7 @@ void reusesInnerJoinForNonOptionalToOneWithNestedOptional() { CriteriaQuery query = builder.createQuery(InvoiceItem.class); Root root = query.from(InvoiceItem.class); - // given an existing inner join an nested optional + // given an existing inner join a nested optional root.join("invoice").join("order"); QueryUtils.toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), @@ -237,7 +236,7 @@ void doesNotCreateAJoinForNonOptionalAssociation() { } @Test // DATAJPA-454 - void createsJoingToTraverseCollectionPath() { + void createsJoinToTraverseCollectionPath() { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(User.class); @@ -319,7 +318,7 @@ void toOrdersCanSortByJoinColumn() { * https://github.com/javaee/jpa-spec/issues/169 Compare to: {@link EclipseLinkQueryUtilsIntegrationTests} */ @Test // DATAJPA-1238 - void demonstrateDifferentBehavorOfGetJoin() { + void demonstrateDifferentBehaviorOfGetJoin() { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(User.class); @@ -348,40 +347,32 @@ int getNumberOfJoinsAfterCreatingAPath() { @SuppressWarnings("unused") static class Merchant { - @Id - String id; - @OneToMany - Set employees; + @Id String id; + @OneToMany Set employees; - @OneToOne - Address address; + @OneToOne Address address; } @Entity @SuppressWarnings("unused") static class Address { - @Id - String id; - @OneToOne(mappedBy = "address") - Merchant merchant; + @Id String id; + @OneToOne(mappedBy = "address") Merchant merchant; } @Entity @SuppressWarnings("unused") static class Employee { - @Id - String id; - @OneToMany - Set credentials; + @Id String id; + @OneToMany Set credentials; } @Entity @SuppressWarnings("unused") static class Credential { - @Id - String id; + @Id String id; String uid; } @@ -399,8 +390,7 @@ public List getPersistenceProviders() { } @Override - public void clearCachedProviders() { - } + public void clearCachedProviders() {} } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index cf7cb86144..d7aaad1686 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -51,7 +51,7 @@ class QueryUtilsUnitTests { private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?"; @Test - void createsCountQueryCorrectly() throws Exception { + void createsCountQueryCorrectly() { assertCountQuery(QUERY, COUNT_QUERY); } @@ -64,47 +64,45 @@ void createsCountQueriesCorrectlyForCapitalLetterJPQL() { } @Test - void createsCountQueryForDistinctQueries() throws Exception { + void createsCountQueryForDistinctQueries() { assertCountQuery("select distinct u from User u where u.foo = ?", "select count(distinct u) from User u where u.foo = ?"); } @Test - void createsCountQueryForConstructorQueries() throws Exception { + void createsCountQueryForConstructorQueries() { assertCountQuery("select distinct new User(u.name) from User u where u.foo = ?", "select count(distinct u) from User u where u.foo = ?"); } @Test - void createsCountQueryForJoins() throws Exception { + void createsCountQueryForJoins() { assertCountQuery("select distinct new User(u.name) from User u left outer join u.roles r WHERE r = ?", "select count(distinct u) from User u left outer join u.roles r WHERE r = ?"); } @Test - void createsCountQueryForQueriesWithSubSelects() throws Exception { + void createsCountQueryForQueriesWithSubSelects() { assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role)", "select count(u) from User u left outer join u.roles r where r in (select r from Role)"); } @Test - void createsCountQueryForAliasesCorrectly() throws Exception { - + void createsCountQueryForAliasesCorrectly() { assertCountQuery("select u from User as u", "select count(u) from User as u"); } @Test - void allowsShortJpaSyntax() throws Exception { - + void allowsShortJpaSyntax() { assertCountQuery(SIMPLE_QUERY, COUNT_QUERY); } @Test - void detectsAliasCorrectly() throws Exception { + void detectsAliasCorrectly() { assertThat(detectAlias(QUERY)).isEqualTo("u"); assertThat(detectAlias(SIMPLE_QUERY)).isEqualTo("u"); @@ -180,14 +178,14 @@ void appendsIgnoreCaseOrderingCorrectly() { } @Test // DATAJPA-342 - void usesReturnedVariableInCOuntProjectionIfSet() { + void usesReturnedVariableInCountProjectionIfSet() { assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", "select count(distinct m.genre) from Media m where m.user = ?1"); } @Test // DATAJPA-343 - void projectsCOuntQueriesForQueriesWithSubselects() { + void projectsCountQueriesForQueriesWithSubselects() { assertCountQuery("select o from Foo o where cb.id in (select b from Bar b)", "select count(o) from Foo o where cb.id in (select b from Bar b)"); @@ -233,7 +231,7 @@ void createCountQueryFromTheGivenCountProjection() { } @Test // DATAJPA-726 - void detectsAliassesInPlainJoins() { + void detectsAliasesInPlainJoins() { String query = "select p from Customer c join c.productOrder p where p.delayed = true"; Sort sort = Sort.by("p.lineItems"); @@ -294,7 +292,7 @@ void doesNotAllowWhitespaceInSort() { } @Test // DATAJPA-965, DATAJPA-970 - void doesNotPrefixUnsageJpaSortFunctionCalls() { + void doesNotPrefixUnsafeJpaSortFunctionCalls() { JpaSort sort = JpaSort.unsafe("sum(foo)"); assertThat(applySorting("select p from Person p", sort, "p")).endsWith("order by sum(foo) asc"); @@ -328,7 +326,7 @@ void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { } @Test // DATAJPA-965, DATAJPA-970 - void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainesAliasedFunctionForDifferentProperty() { + void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainsAliasedFunctionForDifferentProperty() { String query = "SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("name", "avgPrice"); @@ -400,8 +398,8 @@ void doesNotContainStaticClauseInExistsQuery() { @Test // DATAJPA-1363 void discoversAliasWithComplexFunction() { - assertThat(QueryUtils - .getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) // + assertThat( + QueryUtils.getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) // .contains("myAlias"); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 9260474770..4746f27106 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -23,7 +23,6 @@ import org.assertj.core.api.Assertions; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; - import org.springframework.data.jpa.repository.query.StringQuery.InParameterBinding; import org.springframework.data.jpa.repository.query.StringQuery.LikeParameterBinding; import org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding; From f7c5310dc9620d357f416093552de27a213fd38f Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Mon, 28 Feb 2022 12:37:35 +0100 Subject: [PATCH 154/821] Explicit type replaced with diamond operator. In modern java you do not need to add the explicit type if it can be inferred. To make the code more readable, we removed the explicit type and replaced it with the diamond operator (<>). Original pull request #2459 --- .../data/jpa/domain/JpaSort.java | 8 ++++---- .../jpa/mapping/JpaPersistentPropertyImpl.java | 8 ++++---- .../data/jpa/repository/query/Jpa21Utils.java | 2 +- .../repository/support/DefaultJpaContext.java | 2 +- .../support/JpaEntityInformationSupport.java | 2 +- .../data/jpa/repository/support/Querydsl.java | 6 +++--- .../support/QuerydslJpaRepository.java | 2 +- .../data/jpa/util/BeanDefinitionUtils.java | 4 ++-- .../data/jpa/domain/sample/Child.java | 2 +- .../data/jpa/domain/sample/Parent.java | 2 +- .../data/jpa/domain/sample/User.java | 6 +++--- .../JavaConfigUserRepositoryTests.java | 2 +- .../jpa/repository/UserRepositoryTests.java | 2 +- .../CustomGenericJpaRepositoryFactory.java | 2 +- .../DefaultJpaContextIntegrationTests.java | 2 +- .../DefaultJpaEntityMetadataUnitTest.java | 17 ++++++++++------- .../JpaMetamodelEntityInformationUnitTests.java | 6 ++---- ...paPersistableEntityInformationUnitTests.java | 4 +--- .../support/QuerydslJpaRepositoryTests.java | 8 ++------ .../support/SimpleJpaRepositoryUnitTests.java | 2 +- 20 files changed, 42 insertions(+), 47 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index dd98f192e7..e43754cb08 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -161,7 +161,7 @@ public JpaSort and(@Nullable Direction direction, Path... paths) { Assert.notNull(paths, "Paths must not be null!"); - List existing = new ArrayList(); + List existing = new ArrayList<>(); for (Order order : this) { existing.add(order); @@ -181,7 +181,7 @@ public JpaSort andUnsafe(@Nullable Direction direction, String... properties) { Assert.notEmpty(properties, "Properties must not be empty!"); - List orders = new ArrayList(); + List orders = new ArrayList<>(); for (Order order : this) { orders.add(order); @@ -216,7 +216,7 @@ public JpaSort andUnsafe(@Nullable Direction direction, String... properties) { private static List combine(List orders, @Nullable Direction direction, List> paths) { - List result = new ArrayList(orders); + List result = new ArrayList<>(orders); for (Path path : paths) { result.add(new Order(direction, path.toString())); @@ -315,7 +315,7 @@ private Path(List> attributes) { * @return */ public , U> Path dot(A attribute) { - return new Path(add(attribute)); + return new Path<>(add(attribute)); } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index def06c3e78..46d72642ed 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -57,7 +57,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty> annotations = new HashSet>(); + Set> annotations = new HashSet<>(); annotations.add(OneToMany.class); annotations.add(OneToOne.class); annotations.add(ManyToMany.class); @@ -65,13 +65,13 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty>(); + annotations = new HashSet<>(); annotations.add(Id.class); annotations.add(EmbeddedId.class); ID_ANNOTATIONS = Collections.unmodifiableSet(annotations); - annotations = new HashSet>(); + annotations = new HashSet<>(); annotations.add(Column.class); annotations.add(OrderColumn.class); @@ -150,7 +150,7 @@ public boolean isTransient() { @Override protected Association createAssociation() { - return new Association(this, null); + return new Association<>(this, null); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java index cff97cfa9e..b8e7a561b3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java @@ -144,7 +144,7 @@ private static EntityGraph createDynamicEntityGraph(EntityManager em, JpaEnti */ static void configureFetchGraphFrom(JpaEntityGraph jpaEntityGraph, EntityGraph entityGraph) { - List attributePaths = new ArrayList(jpaEntityGraph.getAttributePaths()); + List attributePaths = new ArrayList<>(jpaEntityGraph.getAttributePaths()); // Sort to ensure that the intermediate entity subgraphs are created accordingly. Collections.sort(attributePaths); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java index 64914eafc6..1672f0ee41 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java @@ -47,7 +47,7 @@ public DefaultJpaContext(Set entityManagers) { Assert.notNull(entityManagers, "EntityManagers must not be null!"); Assert.notEmpty(entityManagers, "EntityManagers must not be empty!"); - this.entityManagers = new LinkedMultiValueMap, EntityManager>(); + this.entityManagers = new LinkedMultiValueMap<>(); for (EntityManager em : entityManagers) { for (ManagedType managedType : em.getMetamodel().getManagedTypes()) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java index afec2277ee..29a6948815 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -42,7 +42,7 @@ public abstract class JpaEntityInformationSupport extends AbstractEntityI */ public JpaEntityInformationSupport(Class domainClass) { super(domainClass); - this.metadata = new DefaultJpaEntityMetadata(domainClass); + this.metadata = new DefaultJpaEntityMetadata<>(domainClass); } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 479337f34c..772b8ec5c3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -80,12 +80,12 @@ public AbstractJPAQuery> createQuery() { switch (provider) { case ECLIPSELINK: - return new JPAQuery(em, EclipseLinkTemplates.DEFAULT); + return new JPAQuery<>(em, EclipseLinkTemplates.DEFAULT); case HIBERNATE: - return new JPAQuery(em, HQLTemplates.DEFAULT); + return new JPAQuery<>(em, HQLTemplates.DEFAULT); case GENERIC_JPA: default: - return new JPAQuery(em); + return new JPAQuery<>(em); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index 51b6fafd47..9f093211dc 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -92,7 +92,7 @@ public QuerydslJpaRepository(JpaEntityInformation entityInformation, Enti super(entityInformation, entityManager); this.path = resolver.createPath(entityInformation.getJavaType()); - this.builder = new PathBuilder(path.getType(), path.getMetadata()); + this.builder = new PathBuilder<>(path.getType(), path.getMetadata()); this.querydsl = new Querydsl(entityManager, builder); this.entityManager = entityManager; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java index aef4bc5ac4..dfa6ca5258 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java @@ -53,7 +53,7 @@ private BeanDefinitionUtils() {} static { - List> types = new ArrayList>(); + List> types = new ArrayList<>(); types.add(EntityManagerFactory.class); types.add(AbstractEntityManagerFactoryBean.class); @@ -96,7 +96,7 @@ public static Iterable getEntityManagerFactoryBeanNames(ListableBeanFact public static Collection getEntityManagerFactoryBeanDefinitions( ConfigurableListableBeanFactory beanFactory) { - Set definitions = new HashSet(); + Set definitions = new HashSet<>(); for (Class type : EMF_TYPES) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java index 94ce575d8d..8e3b002dd1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java @@ -31,7 +31,7 @@ public class Child { Long id; @ManyToMany(mappedBy = "children") - Set parents = new HashSet(); + Set parents = new HashSet<>(); /** * @param parent diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java index 00be476528..baa2e9784d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java @@ -34,7 +34,7 @@ public class Parent { static final long serialVersionUID = -89717120680485957L; @ManyToMany(cascade = CascadeType.ALL) - Set children = new HashSet(); + Set children = new HashSet<>(); public Parent add(Child child) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java index 13202e433c..350724bf8f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -135,9 +135,9 @@ public User(String firstname, String lastname, String emailAddress, Role... role this.lastname = lastname; this.emailAddress = emailAddress; this.active = true; - this.roles = new HashSet(Arrays.asList(roles)); - this.colleagues = new HashSet(); - this.attributes = new HashSet(); + this.roles = new HashSet<>(Arrays.asList(roles)); + this.colleagues = new HashSet<>(); + this.attributes = new HashSet<>(); this.createdAt = new Date(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java index 0d3999d483..0a5018adcd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java @@ -75,7 +75,7 @@ public UserRepository userRepository() throws Exception { QueryMethodEvaluationContextProvider evaluationContextProvider = new ExtensionAwareQueryMethodEvaluationContextProvider( applicationContext); - JpaRepositoryFactoryBean factory = new JpaRepositoryFactoryBean( + JpaRepositoryFactoryBean factory = new JpaRepositoryFactoryBean<>( UserRepository.class); factory.setEntityManager(entityManager); factory.setBeanFactory(applicationContext); 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 52dc7c7308..f257136502 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 @@ -298,7 +298,7 @@ void deleteCollectionOfEntitiesById() { @Test void deleteEmptyCollectionDoesNotDeleteAnything() { - assertDeleteCallDoesNotDeleteAnything(new ArrayList()); + assertDeleteCallDoesNotDeleteAnything(new ArrayList<>()); } @Test diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java index 0ac035f661..0d9671113e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java @@ -48,7 +48,7 @@ public CustomGenericJpaRepositoryFactory(EntityManager entityManager) { JpaEntityInformation entityMetadata = mock(JpaEntityInformation.class); when(entityMetadata.getJavaType()).thenReturn((Class) information.getDomainType()); - return new CustomGenericJpaRepository(entityMetadata, em); + return new CustomGenericJpaRepository<>(entityMetadata, em); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java index d93dea1475..ebe084f093 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java @@ -91,7 +91,7 @@ void createEntityManagers() { this.firstEm = firstEmf.createEntityManager(); this.secondEm = secondEmf.createEntityManager(); - this.jpaContext = new DefaultJpaContext(new HashSet(Arrays.asList(firstEm, secondEm))); + this.jpaContext = new DefaultJpaContext(new HashSet<>(Arrays.asList(firstEm, secondEm))); } @Test // DATAJPA-669 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java index ead477af5f..28b8b9b789 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java @@ -44,28 +44,28 @@ void rejectsNullDomainType() { @Test void returnsConfiguredType() { - DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata(Foo.class); + DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata<>(Foo.class); assertThat(metadata.getJavaType()).isEqualTo(Foo.class); } @Test void returnsSimpleClassNameAsEntityNameByDefault() { - DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata(Foo.class); + DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata<>(Foo.class); assertThat(metadata.getEntityName()).isEqualTo(Foo.class.getSimpleName()); } @Test void returnsCustomizedEntityNameIfConfigured() { - DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata(Bar.class); + DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata<>(Bar.class); assertThat(metadata.getEntityName()).isEqualTo("Entity"); } @Test // DATAJPA-871 void returnsCustomizedEntityNameIfConfiguredViaComposedAnnotation() { - DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata( + DefaultJpaEntityMetadata metadata = new DefaultJpaEntityMetadata<>( BarWithComposedAnnotation.class); assertThat(metadata.getEntityName()).isEqualTo("Entity"); } @@ -78,11 +78,14 @@ void returnsCustomizedEntityNameIfConfiguredViaComposedAnnotation() { String entityName(); } - private static class Foo {} + private static class Foo { + } @Entity(name = "Entity") - static class Bar {} + static class Bar { + } @CustomEntityAnnotationUsingAliasFor(entityName = "Entity") - private static class BarWithComposedAnnotation {} + private static class BarWithComposedAnnotation { + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java index e3820d10b0..9b509bf5a8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java @@ -35,7 +35,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.data.jpa.domain.sample.PersistableWithIdClass; import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK; @@ -62,8 +61,7 @@ void setUp() { when(first.getName()).thenReturn("first"); when(second.getName()).thenReturn("second"); - Set> attributes = new HashSet>( - asList(first, second)); + Set> attributes = new HashSet<>(asList(first, second)); when(type.getIdClassAttributes()).thenReturn(attributes); @@ -77,7 +75,7 @@ void setUp() { @Test // DATAJPA-50 void doesNotCreateIdIfAllPartialAttributesAreNull() { - JpaMetamodelEntityInformation information = new JpaMetamodelEntityInformation( + JpaMetamodelEntityInformation information = new JpaMetamodelEntityInformation<>( PersistableWithIdClass.class, metamodel); PersistableWithIdClass entity = new PersistableWithIdClass(null, null); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java index cfcde4816f..13d7e78abc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java @@ -29,7 +29,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.data.domain.Persistable; import org.springframework.data.repository.core.EntityInformation; @@ -61,8 +60,7 @@ void setUp() { @Test void usesPersistableMethodsForIsNewAndGetId() { - EntityInformation entityInformation = new JpaPersistableEntityInformation(Foo.class, - metamodel); + EntityInformation entityInformation = new JpaPersistableEntityInformation<>(Foo.class, metamodel); Foo foo = new Foo(); assertThat(entityInformation.isNew(foo)).isFalse(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java index 97fcd9cd4f..9d21f3e38f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java @@ -16,10 +16,6 @@ package org.springframework.data.jpa.repository.support; import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.domain.Example.*; -import static org.springframework.data.domain.ExampleMatcher.*; - -import lombok.Data; import java.sql.Date; import java.time.LocalDate; @@ -80,10 +76,10 @@ class QuerydslJpaRepositoryTests { @BeforeEach void setUp() { - JpaEntityInformation information = new JpaMetamodelEntityInformation(User.class, + JpaEntityInformation information = new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel()); - repository = new QuerydslJpaRepository(information, em); + repository = new QuerydslJpaRepository<>(information, em); dave = repository.save(new User("Dave", "Matthews", "dave@matthews.com")); carter = repository.save(new User("Carter", "Beauford", "carter@beauford.com")); oliver = repository.save(new User("Oliver", "matthews", "oliver@matthews.com")); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index ab9b5b67b6..cc69535d33 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -84,7 +84,7 @@ void setUp() { when(metadata.getQueryHints()).thenReturn(hints); when(metadata.getQueryHintsForCount()).thenReturn(hints); - repo = new SimpleJpaRepository(information, em); + repo = new SimpleJpaRepository<>(information, em); repo.setRepositoryMethodMetadata(metadata); } From 60ae66d4e773359ffceaf021e52a481534b80554 Mon Sep 17 00:00:00 2001 From: heowc Date: Tue, 8 Mar 2022 18:32:23 +0900 Subject: [PATCH 155/821] Null arguments now get wrapped in a `TypedParameterValue` for Hibernate. This avoids problems where a JDBC driver fails to determine the correct type for such values. Closes #2370 Original pull request #2461 --- .../repository/query/AbstractJpaQuery.java | 11 ++- .../JpaParametersParameterAccessorTests.java | 86 +++++++++++++++++++ ...bernateJpaParametersParameterAccessor.java | 68 +++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java create mode 100644 src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 7f950b018a..23d2b14068 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -60,6 +60,7 @@ * @author Nicolas Cirigliano * @author Jens Schauder * @author Сергей Цыпанов + * @author Wonchul Heo */ public abstract class AbstractJpaQuery implements RepositoryQuery { @@ -143,13 +144,21 @@ public Object execute(Object[] parameters) { @Nullable private Object doExecute(JpaQueryExecution execution, Object[] values) { - JpaParametersParameterAccessor accessor = new JpaParametersParameterAccessor(method.getParameters(), values); + JpaParametersParameterAccessor accessor = obtainParameterAccessor(values); Object result = execution.execute(this, accessor); ResultProcessor withDynamicProjection = method.getResultProcessor().withDynamicProjection(accessor); return withDynamicProjection.processResult(result, new TupleConverter(withDynamicProjection.getReturnedType())); } + private JpaParametersParameterAccessor obtainParameterAccessor(Object[] values) { + if (provider == PersistenceProvider.HIBERNATE) { + return new HibernateJpaParametersParameterAccessor(method.getParameters(), values, em); + } else { + return new JpaParametersParameterAccessor(method.getParameters(), values); + } + } + protected JpaQueryExecution getExecution() { JpaQueryExecution execution = this.execution.getNullable(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java new file mode 100644 index 0000000000..5f196ca2cc --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java @@ -0,0 +1,86 @@ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.lang.reflect.Method; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; + +import org.hibernate.jpa.TypedParameterValue; +import org.hibernate.type.StandardBasicTypes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.springframework.data.jpa.domain.sample.User; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Unit test for {@link JpaParametersParameterAccessor}. + * + * @author Wonchul Heo + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration("classpath:infrastructure.xml") +class JpaParametersParameterAccessorTests { + + @PersistenceContext + private EntityManager em; + private Query query; + + @BeforeEach + void setUp() { + query = mock(Query.class); + } + + @Test // GH-2370 + void createsJpaParametersParameterAccessor() throws Exception { + + Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); + Object[] values = { null }; + JpaParameters parameters = new JpaParameters(withNativeQuery); + JpaParametersParameterAccessor accessor = new JpaParametersParameterAccessor(parameters, values); + + bind(parameters, accessor); + + verify(query).setParameter(eq(1), isNull()); + } + + @Test // GH-2370 + void createsHibernateParametersParameterAccessor() throws Exception { + + Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); + Object[] values = { null }; + JpaParameters parameters = new JpaParameters(withNativeQuery); + JpaParametersParameterAccessor accessor = + new HibernateJpaParametersParameterAccessor(parameters, values, em); + + bind(parameters, accessor); + + ArgumentCaptor captor = ArgumentCaptor.forClass(TypedParameterValue.class); + verify(query).setParameter(eq(1), captor.capture()); + TypedParameterValue captorValue = captor.getValue(); + assertThat(captorValue.getType()).isEqualTo(StandardBasicTypes.INTEGER); + assertThat(captorValue.getValue()).isNull(); + } + + private void bind(JpaParameters parameters, JpaParametersParameterAccessor accessor) { + ParameterBinderFactory.createBinder(parameters).bind(QueryParameterSetter.BindableQuery.from(query), + accessor, + QueryParameterSetter.ErrorHandling.LENIENT); + } + + interface SampleRepository { + @org.springframework.data.jpa.repository.Query( + value = "select 1 from user where age = :age", + nativeQuery = true) + User withNativeQuery(Integer age); + } +} diff --git a/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java b/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java new file mode 100644 index 0000000000..3054f27fe1 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java @@ -0,0 +1,68 @@ +/* + * Copyright 2017-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import javax.persistence.EntityManager; + +import org.hibernate.Session; +import org.hibernate.TypeHelper; +import org.hibernate.jpa.TypedParameterValue; +import org.hibernate.type.Type; +import org.springframework.data.repository.query.Parameter; +import org.springframework.data.repository.query.Parameters; +import org.springframework.data.repository.query.ParametersParameterAccessor; + +/** + * {@link org.springframework.data.repository.query.ParameterAccessor} based on an {@link Parameters} instance. + * In addition to the {@link JpaParametersParameterAccessor} functions, the bindable value is provided by + * fetching the method type when there is null. + * + * @author Wonchul Heo + */ +public class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAccessor { + + private final TypeHelper typeHelper; + + /** + * Creates a new {@link ParametersParameterAccessor}. + * + * @param parameters must not be {@literal null}. + * @param values must not be {@literal null}. + * @param em must not be {@literal null}. + */ + HibernateJpaParametersParameterAccessor(Parameters parameters, Object[] values, EntityManager em) { + super(parameters, values); + Session session = em.unwrap(Session.class); + this.typeHelper = session.getSessionFactory().getTypeHelper(); + } + + public Object getValue(Parameter parameter) { + Object value = super.getValue(parameter.getIndex()); + if (value == null) { + Type type = typeHelper.basic(parameter.getType()); + if (type == null) { + return null; + } + return new TypedParameterValue(type, null); + } + return value; + } + + @Override + public Object[] getValues() { + return super.getValues(); + } +} From 065d77ac1257586cff6375ae8856a467c48f09aa Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Thu, 10 Mar 2022 12:12:02 +0100 Subject: [PATCH 156/821] Polishing. Replaced type check by method providing the correct JpaParameterParameterAccessor. Formatting. See #2370 Original pull request #2461 --- ...bernateJpaParametersParameterAccessor.java | 40 +++--- .../jpa/provider/PersistenceProvider.java | 11 ++ .../repository/query/AbstractJpaQuery.java | 7 +- .../query/JpaParametersParameterAccessor.java | 2 +- .../JpaParametersParameterAccessorTests.java | 115 +++++++++--------- 5 files changed, 93 insertions(+), 82 deletions(-) rename {src/main/java/org/springframework/data/jpa/repository/query => spring-data-jpa/src/main/java/org/springframework/data/jpa/provider}/HibernateJpaParametersParameterAccessor.java (68%) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java similarity index 68% rename from src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index 3054f27fe1..5948c519d7 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,26 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.query; - -import javax.persistence.EntityManager; +package org.springframework.data.jpa.provider; +import jakarta.persistence.EntityManager; import org.hibernate.Session; import org.hibernate.TypeHelper; import org.hibernate.jpa.TypedParameterValue; import org.hibernate.type.Type; +import org.springframework.data.jpa.repository.query.JpaParametersParameterAccessor; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; /** - * {@link org.springframework.data.repository.query.ParameterAccessor} based on an {@link Parameters} instance. - * In addition to the {@link JpaParametersParameterAccessor} functions, the bindable value is provided by - * fetching the method type when there is null. + * {@link org.springframework.data.repository.query.ParameterAccessor} based on an {@link Parameters} instance. In + * addition to the {@link JpaParametersParameterAccessor} functions, the bindable value is provided by fetching the + * method type when there is null. * * @author Wonchul Heo + * @author Jens Schauder + * @since 2.7 */ -public class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAccessor { +class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAccessor { private final TypeHelper typeHelper; @@ -44,25 +46,25 @@ public class HibernateJpaParametersParameterAccessor extends JpaParametersParame * @param em must not be {@literal null}. */ HibernateJpaParametersParameterAccessor(Parameters parameters, Object[] values, EntityManager em) { + super(parameters, values); + Session session = em.unwrap(Session.class); this.typeHelper = session.getSessionFactory().getTypeHelper(); } + @Override public Object getValue(Parameter parameter) { + Object value = super.getValue(parameter.getIndex()); - if (value == null) { - Type type = typeHelper.basic(parameter.getType()); - if (type == null) { - return null; - } - return new TypedParameterValue(type, null); + if (value != null) { + return value; } - return value; - } - @Override - public Object[] getValues() { - return super.getValues(); + Type type = typeHelper.basic(parameter.getType()); + if (type == null) { + return null; + } + return new TypedParameterValue(type, null); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index a877d49c15..fa4f87f948 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -34,6 +34,8 @@ import org.hibernate.ScrollableResults; import org.hibernate.proxy.HibernateProxy; +import org.springframework.data.jpa.repository.query.JpaParameters; +import org.springframework.data.jpa.repository.query.JpaParametersParameterAccessor; import org.springframework.data.util.CloseableIterator; import org.springframework.lang.Nullable; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -98,6 +100,11 @@ public Object getIdentifierFrom(Object entity) { public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { return new HibernateScrollableResultsIterator(jpaQuery); } + + @Override + public JpaParametersParameterAccessor getParameterAccessor(JpaParameters parameters, Object[] values, EntityManager em) { + return new HibernateJpaParametersParameterAccessor(parameters, values, em); + } }, /** @@ -242,6 +249,10 @@ public static PersistenceProvider fromMetamodel(Metamodel metamodel) { return cacheAndReturn(metamodelType, GENERIC_JPA); } + public JpaParametersParameterAccessor getParameterAccessor(JpaParameters parameters, Object[] values, EntityManager em) { + return new JpaParametersParameterAccessor(parameters, values); + } + /** * Returns the placeholder to be used for simple count queries. Default implementation returns {@code x}. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 23d2b14068..52e9c3aa96 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -152,11 +152,8 @@ private Object doExecute(JpaQueryExecution execution, Object[] values) { } private JpaParametersParameterAccessor obtainParameterAccessor(Object[] values) { - if (provider == PersistenceProvider.HIBERNATE) { - return new HibernateJpaParametersParameterAccessor(method.getParameters(), values, em); - } else { - return new JpaParametersParameterAccessor(method.getParameters(), values); - } + + return provider.getParameterAccessor(method.getParameters(), values, em); } protected JpaQueryExecution getExecution() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java index f01dbdc69e..7870023405 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java @@ -36,7 +36,7 @@ public class JpaParametersParameterAccessor extends ParametersParameterAccessor * @param parameters must not be {@literal null}. * @param values must not be {@literal null}. */ - JpaParametersParameterAccessor(Parameters parameters, Object[] values) { + public JpaParametersParameterAccessor(Parameters parameters, Object[] values) { super(parameters, values); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java index 5f196ca2cc..059fc78ecc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java @@ -1,10 +1,8 @@ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import java.lang.reflect.Method; @@ -19,6 +17,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -31,56 +30,58 @@ @ContextConfiguration("classpath:infrastructure.xml") class JpaParametersParameterAccessorTests { - @PersistenceContext - private EntityManager em; - private Query query; - - @BeforeEach - void setUp() { - query = mock(Query.class); - } - - @Test // GH-2370 - void createsJpaParametersParameterAccessor() throws Exception { - - Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); - Object[] values = { null }; - JpaParameters parameters = new JpaParameters(withNativeQuery); - JpaParametersParameterAccessor accessor = new JpaParametersParameterAccessor(parameters, values); - - bind(parameters, accessor); - - verify(query).setParameter(eq(1), isNull()); - } - - @Test // GH-2370 - void createsHibernateParametersParameterAccessor() throws Exception { - - Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); - Object[] values = { null }; - JpaParameters parameters = new JpaParameters(withNativeQuery); - JpaParametersParameterAccessor accessor = - new HibernateJpaParametersParameterAccessor(parameters, values, em); - - bind(parameters, accessor); - - ArgumentCaptor captor = ArgumentCaptor.forClass(TypedParameterValue.class); - verify(query).setParameter(eq(1), captor.capture()); - TypedParameterValue captorValue = captor.getValue(); - assertThat(captorValue.getType()).isEqualTo(StandardBasicTypes.INTEGER); - assertThat(captorValue.getValue()).isNull(); - } - - private void bind(JpaParameters parameters, JpaParametersParameterAccessor accessor) { - ParameterBinderFactory.createBinder(parameters).bind(QueryParameterSetter.BindableQuery.from(query), - accessor, - QueryParameterSetter.ErrorHandling.LENIENT); - } - - interface SampleRepository { - @org.springframework.data.jpa.repository.Query( - value = "select 1 from user where age = :age", - nativeQuery = true) - User withNativeQuery(Integer age); - } + @PersistenceContext private EntityManager em; + private Query query; + + @BeforeEach + void setUp() { + query = mock(Query.class); + } + + @Test // GH-2370 + void createsJpaParametersParameterAccessor() throws Exception { + + Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); + Object[] values = { null }; + JpaParameters parameters = new JpaParameters(withNativeQuery); + JpaParametersParameterAccessor accessor = PersistenceProvider.GENERIC_JPA.getParameterAccessor(parameters, values, em); + + bind(parameters, accessor); + + verify(query).setParameter(eq(1), isNull()); + } + + @Test // GH-2370 + void createsHibernateParametersParameterAccessor() throws Exception { + + Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); + Object[] values = { null }; + JpaParameters parameters = new JpaParameters(withNativeQuery); + JpaParametersParameterAccessor accessor = PersistenceProvider.HIBERNATE.getParameterAccessor(parameters, values, + em); + + bind(parameters, accessor); + + ArgumentCaptor captor = ArgumentCaptor.forClass(TypedParameterValue.class); + verify(query).setParameter(eq(1), captor.capture()); + TypedParameterValue captorValue = captor.getValue(); + assertThat(captorValue.getType()).isEqualTo(StandardBasicTypes.INTEGER); + assertThat(captorValue.getValue()).isNull(); + } + + private void bind(JpaParameters parameters, JpaParametersParameterAccessor accessor) { + + ParameterBinderFactory.createBinder(parameters) + .bind( // + QueryParameterSetter.BindableQuery.from(query), // + accessor, // + QueryParameterSetter.ErrorHandling.LENIENT // + ); + } + + interface SampleRepository { + + @org.springframework.data.jpa.repository.Query(value = "select 1 from user where age = :age", nativeQuery = true) + User withNativeQuery(Integer age); + } } From a105a1e7e9c1e936a299fc5874baaaf5900c6eea Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 3 Nov 2021 15:12:30 +0100 Subject: [PATCH 157/821] Fix lazy loading problem with custom revision entities. We now unproxy the revision data before trying to access its data. Closes spring-projects/spring-data-envers/issues/313 --- .../support/EnversRevisionRepositoryImpl.java | 5 +- .../support/RepositoryIntegrationTests.java | 5 +- .../envers/sample/CustomRevisionEntity.java | 66 +++++++++++++++++++ .../envers/sample/CustomRevisionListener.java | 25 +++++++ 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionEntity.java create mode 100644 spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionListener.java diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index abfad6287c..838d647219 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -23,6 +23,7 @@ import jakarta.persistence.EntityManager; +import org.hibernate.Hibernate; import org.hibernate.envers.AuditReader; import org.hibernate.envers.AuditReaderFactory; import org.hibernate.envers.DefaultRevisionEntity; @@ -32,7 +33,6 @@ import org.hibernate.envers.query.AuditEntity; import org.hibernate.envers.query.AuditQuery; import org.hibernate.envers.query.order.AuditOrder; - import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -200,7 +200,8 @@ RevisionMetadata createRevisionMetadata() { return metadata instanceof DefaultRevisionEntity // ? new DefaultRevisionMetadata((DefaultRevisionEntity) metadata, revisionType) // - : new AnnotationRevisionMetadata<>(metadata, RevisionNumber.class, RevisionTimestamp.class, revisionType); + : new AnnotationRevisionMetadata<>(Hibernate.unproxy(metadata), RevisionNumber.class, RevisionTimestamp.class, + revisionType); } private static RevisionMetadata.RevisionType convertRevisionType(RevisionType datum) { diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java index d75066dbd2..49d15bed9b 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java @@ -106,7 +106,10 @@ void testLifeCycle() { Page> page = licenseRepository.findRevisions(license.id, PageRequest.of(0, 10)); Revisions revisions = Revisions.of(page.getContent()); - assertThat(revisions.getLatestRevision()).isEqualTo(it); + Revision latestRevision = revisions.getLatestRevision(); + + assertThat(latestRevision.getRequiredRevisionNumber()).isEqualTo(it.getRequiredRevisionNumber()); + assertThat(latestRevision.getEntity()).isEqualTo(it.getEntity()); }); } diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionEntity.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionEntity.java new file mode 100644 index 0000000000..a475f1cba9 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionEntity.java @@ -0,0 +1,66 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Transient; + +import java.util.Date; + +import org.hibernate.envers.RevisionEntity; +import org.hibernate.envers.RevisionNumber; +import org.hibernate.envers.RevisionTimestamp; + +@Entity +@RevisionEntity(CustomRevisionListener.class) +public class CustomRevisionEntity { + + @Id @GeneratedValue @RevisionNumber private int id; + @RevisionTimestamp private long timestamp; + private String additionalData; + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + @Transient + public Date getRevisionDate() { + return new Date(this.timestamp); + } + + public long getTimestamp() { + return this.timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public String getAdditionalData() { + return additionalData; + } + + public void setAdditionalData(String additionalData) { + this.additionalData = additionalData; + } + +} diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionListener.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionListener.java new file mode 100644 index 0000000000..897b439e39 --- /dev/null +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionListener.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.envers.sample; + +import org.hibernate.envers.RevisionListener; + +public class CustomRevisionListener implements RevisionListener { + @Override + public void newRevision(Object o) { + ((CustomRevisionEntity) o).setAdditionalData("some data"); + } +} From 82b9f4a10a48c356ab927ad65b9085e0b39bf933 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 21 Mar 2022 16:34:35 +0100 Subject: [PATCH 158/821] Prepare 3.0 M2 (2022.0.0). See #2407 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 069cad460c..58f5cc3898 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-SNAPSHOT + 3.0.0-M2 @@ -27,7 +27,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-SNAPSHOT + 3.0.0-M2 0.10.3 org.hibernate @@ -210,8 +210,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From 826de597b883befa324ed56751dd59262bb22fd2 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 21 Mar 2022 16:35:08 +0100 Subject: [PATCH 159/821] Release version 3.0 M2 (2022.0.0). See #2407 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 58f5cc3898..ab9ca12410 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M2 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 5e82ca77cc..6c61aa0899 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-SNAPSHOT + 3.0.0-M2 org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M2 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index bba8571672..9c353650ce 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M2 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index ba9220cc2a..3f2e6b0992 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-SNAPSHOT + 3.0.0-M2 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M2 ../pom.xml From bcb996f743c9825e5148ffa8ef12d6e01a7b9305 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 21 Mar 2022 16:44:39 +0100 Subject: [PATCH 160/821] Prepare next development iteration. See #2407 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index ab9ca12410..58f5cc3898 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M2 + 3.0.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 6c61aa0899..5e82ca77cc 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-M2 + 3.0.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.0.0-M2 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 9c353650ce..bba8571672 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M2 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 3f2e6b0992..ba9220cc2a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-M2 + 3.0.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M2 + 3.0.0-SNAPSHOT ../pom.xml From 168e1add7e17607b18f31ef7491254889bf49607 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 21 Mar 2022 16:44:41 +0100 Subject: [PATCH 161/821] After release cleanups. See #2407 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 58f5cc3898..069cad460c 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-M2 + 3.0.0-SNAPSHOT @@ -27,7 +27,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-M2 + 3.0.0-SNAPSHOT 0.10.3 org.hibernate @@ -210,8 +210,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 83ce3ab4d76a9f755dd79ca87d2314ffd66eb92b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Mar 2022 14:00:01 +0100 Subject: [PATCH 162/821] Prepare 3.0 M3 (2022.0.0). See #2470 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 069cad460c..10dca6768e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-SNAPSHOT + 3.0.0-M3 @@ -27,7 +27,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-SNAPSHOT + 3.0.0-M3 0.10.3 org.hibernate @@ -210,8 +210,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From 06f4165dbebcad64df4467912058709b14a86161 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Mar 2022 14:00:23 +0100 Subject: [PATCH 163/821] Release version 3.0 M3 (2022.0.0). See #2470 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 10dca6768e..1a65d1a3a6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M3 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 5e82ca77cc..d0518bbbb1 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-SNAPSHOT + 3.0.0-M3 org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M3 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index bba8571672..a54d97b618 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M3 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index ba9220cc2a..bc9b331445 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-SNAPSHOT + 3.0.0-M3 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M3 ../pom.xml From 8d04597c6955fa5977908dd5411e3ec98d35fbe0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Mar 2022 14:07:36 +0100 Subject: [PATCH 164/821] Prepare next development iteration. See #2470 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 1a65d1a3a6..10dca6768e 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M3 + 3.0.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index d0518bbbb1..5e82ca77cc 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-M3 + 3.0.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.0.0-M3 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a54d97b618..bba8571672 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M3 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index bc9b331445..ba9220cc2a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-M3 + 3.0.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M3 + 3.0.0-SNAPSHOT ../pom.xml From 6b27459b5b226db7e4f68ef37e3e061c3c14381f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Mar 2022 14:07:38 +0100 Subject: [PATCH 165/821] After release cleanups. See #2470 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 10dca6768e..069cad460c 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-M3 + 3.0.0-SNAPSHOT @@ -27,7 +27,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-M3 + 3.0.0-SNAPSHOT 0.10.3 org.hibernate @@ -210,8 +210,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 15e8e3bfe8396e7e151e7e540ed0b040593f1cfa Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 18 Mar 2022 16:28:23 -0500 Subject: [PATCH 166/821] Use 'lower' for all ignoreCase operations. To avoid having to maintain multiples indices, use 'lower' for all ignoreCase operations (JSqlParser, QueryByExample, Querydsl). See #2420. --- .../springframework/data/jpa/repository/query/QueryUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index d68068082a..1d68ab43a9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -610,8 +610,8 @@ private static jakarta.persistence.criteria.Order toJpaOrder(Order order, From expression = toExpressionRecursively(from, property); if (order.isIgnoreCase() && String.class.equals(expression.getJavaType())) { - Expression lower = cb.lower((Expression) expression); - return order.isAscending() ? cb.asc(lower) : cb.desc(lower); + Expression upper = cb.lower((Expression) expression); + return order.isAscending() ? cb.asc(upper) : cb.desc(upper); } else { return order.isAscending() ? cb.asc(expression) : cb.desc(expression); } From ef13a0d251eb68ccae1e1e73fa88d6c16e979961 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 18 Mar 2022 16:24:15 -0500 Subject: [PATCH 167/821] Add tests to verify AuditingEntityListener works with embeddables. See #2365. --- .../domain/sample/AuditableEmbeddable.java | 54 +++++++++++ .../jpa/domain/sample/AuditableEntity.java | 78 ++++++++++++++++ ...tingEntityWithEmbeddableListenerTests.java | 91 +++++++++++++++++++ .../sample/AuditableEntityRepository.java | 24 +++++ .../test/resources/META-INF/persistence.xml | 2 + .../test/resources/META-INF/persistence2.xml | 2 + ...diting-entity-with-embeddable-listener.xml | 16 ++++ 7 files changed, 267 insertions(+) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEmbeddable.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEntity.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableEntityRepository.java create mode 100644 spring-data-jpa/src/test/resources/auditing/auditing-entity-with-embeddable-listener.xml diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEmbeddable.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEmbeddable.java new file mode 100644 index 0000000000..c66b7222c5 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEmbeddable.java @@ -0,0 +1,54 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import jakarta.persistence.Embeddable; + +import java.time.Instant; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + +/** + * JPA {@link Embeddable} to test out {@link org.springframework.data.jpa.domain.support.AuditingEntityListener}. + * + * @author Greg Turnquist + */ +@Embeddable +public class AuditableEmbeddable { + + @CreatedDate // + private Instant dateCreated; + + @LastModifiedDate // + private Instant dateUpdated; + + public Instant getDateCreated() { + return dateCreated; + } + + public void setDateCreated(Instant dateCreated) { + this.dateCreated = dateCreated; + } + + public Instant getDateUpdated() { + return dateUpdated; + } + + public void setDateUpdated(Instant dateUpdated) { + this.dateUpdated = dateUpdated; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEntity.java new file mode 100644 index 0000000000..fa294462d8 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEntity.java @@ -0,0 +1,78 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +/** + * JPA entity with an {@link Embedded} set of auditable data. + * + * @author Greg Turnquist + */ +@Entity +@EntityListeners(AuditingEntityListener.class) +public class AuditableEntity { + + @Id + @GeneratedValue // + private Long id; + + private String data; + + @Embedded // + private AuditableEmbeddable auditDetails; + + public AuditableEntity() { + this(null, null, null); + } + + public AuditableEntity(Long id, String data, AuditableEmbeddable auditDetails) { + + this.id = id; + this.data = data; + this.auditDetails = auditDetails; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public AuditableEmbeddable getAuditDetails() { + return auditDetails; + } + + public void setAuditDetails(AuditableEmbeddable auditDetails) { + this.auditDetails = auditDetails; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java new file mode 100644 index 0000000000..8f94078e5f --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.support; + +import static org.assertj.core.api.Assertions.*; + +import java.time.Instant; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.sample.AuditableEmbeddable; +import org.springframework.data.jpa.domain.sample.AuditableEntity; +import org.springframework.data.jpa.repository.sample.AuditableEntityRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Integration test for {@link AuditingEntityListener}. + * + * @author Greg Turnquist + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration("classpath:auditing/auditing-entity-with-embeddable-listener.xml") +public class AuditingEntityWithEmbeddableListenerTests { + + @Autowired AuditableEntityRepository repository; + + private AuditableEntity entity; + private AuditableEmbeddable auditDetails; + + @BeforeEach + void setUp() { + + entity = new AuditableEntity(); + entity.setId(1L); + entity.setData("original value"); + + auditDetails = new AuditableEmbeddable(); + entity.setAuditDetails(auditDetails); + } + + @Test + void auditsEmbeddedCorrectly() { + + // when + repository.saveAndFlush(entity); + Optional optionalEntity = repository.findById(1L); + + // then + assertThat(optionalEntity).isNotEmpty(); + + AuditableEntity auditableEntity = optionalEntity.get(); + assertThat(auditableEntity.getData()).isEqualTo("original value"); + + assertThat(auditableEntity.getAuditDetails().getDateCreated()).isNotNull(); + assertThat(auditableEntity.getAuditDetails().getDateUpdated()).isNotNull(); + + Instant originalCreationDate = auditableEntity.getAuditDetails().getDateCreated(); + Instant originalDateUpdated = auditableEntity.getAuditDetails().getDateUpdated(); + + auditableEntity.setData("updated value"); + + repository.saveAndFlush(auditableEntity); + + Optional optionalRevisedEntity = repository.findById(1L); + + assertThat(optionalRevisedEntity).isNotEmpty(); + + AuditableEntity revisedEntity = optionalRevisedEntity.get(); + assertThat(revisedEntity.getData()).isEqualTo("updated value"); + + assertThat(revisedEntity.getAuditDetails().getDateCreated()).isEqualTo(originalCreationDate); + assertThat(revisedEntity.getAuditDetails().getDateUpdated()).isAfter(originalDateUpdated); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableEntityRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableEntityRepository.java new file mode 100644 index 0000000000..574e1abc9c --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableEntityRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.sample; + +import org.springframework.data.jpa.domain.sample.AuditableEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * {@link JpaRepository} to test {@link org.springframework.data.jpa.domain.support.AuditingEntityListener}. + */ +public interface AuditableEntityRepository extends JpaRepository {} diff --git a/spring-data-jpa/src/test/resources/META-INF/persistence.xml b/spring-data-jpa/src/test/resources/META-INF/persistence.xml index 79adc56fa9..9239b61d7d 100644 --- a/spring-data-jpa/src/test/resources/META-INF/persistence.xml +++ b/spring-data-jpa/src/test/resources/META-INF/persistence.xml @@ -10,6 +10,8 @@ org.springframework.data.jpa.domain.sample.AnnotatedAuditableUser org.springframework.data.jpa.domain.sample.AuditableRole org.springframework.data.jpa.domain.sample.AuditableUser + org.springframework.data.jpa.domain.sample.AuditableEntity + org.springframework.data.jpa.domain.sample.AuditableEmbeddable org.springframework.data.jpa.domain.sample.Category org.springframework.data.jpa.domain.sample.Child org.springframework.data.jpa.domain.sample.ConcreteType1 diff --git a/spring-data-jpa/src/test/resources/META-INF/persistence2.xml b/spring-data-jpa/src/test/resources/META-INF/persistence2.xml index 3e82937d79..683457aac8 100644 --- a/spring-data-jpa/src/test/resources/META-INF/persistence2.xml +++ b/spring-data-jpa/src/test/resources/META-INF/persistence2.xml @@ -6,6 +6,8 @@ org.springframework.data.jpa.domain.sample.AnnotatedAuditableUser org.springframework.data.jpa.domain.sample.AuditableRole org.springframework.data.jpa.domain.sample.AuditableUser + org.springframework.data.jpa.domain.sample.AuditableEntity + org.springframework.data.jpa.domain.sample.AuditableEmbeddable org.springframework.data.jpa.domain.sample.Category org.springframework.data.jpa.domain.sample.CustomAbstractPersistable org.springframework.data.jpa.domain.sample.EntityWithAssignedId diff --git a/spring-data-jpa/src/test/resources/auditing/auditing-entity-with-embeddable-listener.xml b/spring-data-jpa/src/test/resources/auditing/auditing-entity-with-embeddable-listener.xml new file mode 100644 index 0000000000..d874d03e86 --- /dev/null +++ b/spring-data-jpa/src/test/resources/auditing/auditing-entity-with-embeddable-listener.xml @@ -0,0 +1,16 @@ + + + + + + + + + + From 36f559e0413ca6348fd08ee3bb2c233db54e91be Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 23 Mar 2022 08:38:50 -0500 Subject: [PATCH 168/821] Polishing. --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 02988c7680..faec27db1f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,7 +9,7 @@ pipeline { triggers { pollSCM 'H/10 * * * *' - upstream(upstreamProjects: "spring-data-commons/3.0.x", threshold: hudson.model.Result.SUCCESS) + upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS) } options { From 2545e6d74a550d748c4b59a7bf0972ffc7d547bd Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Sat, 18 Dec 2021 00:48:58 +0100 Subject: [PATCH 169/821] Fix missing `DISTINCT` in count queries. The `COUNT_MATCH` does not consider line breaks after the `from` clause or the `where` clause. This leads to a no match scenario in the construction of the count query. With the fix we now consider line breaks/whitespaces after the `from` and `where` clause. See #2341 Related tickets: #2177 --- .../data/jpa/repository/query/QueryUtils.java | 5 ++-- .../repository/query/QueryUtilsUnitTests.java | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 1d68ab43a9..1bded19616 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -148,7 +148,7 @@ public abstract class QueryUtils { builder.append(IDENTIFIER_GROUP); builder.append("(.*)"); - COUNT_MATCH = compile(builder.toString(), CASE_INSENSITIVE); + COUNT_MATCH = compile(builder.toString(), CASE_INSENSITIVE | DOTALL); Map> persistentAttributeTypes = new HashMap<>(); persistentAttributeTypes.put(ONE_TO_ONE, OneToOne.class); @@ -490,7 +490,8 @@ public static String createCountQueryFor(String originalQuery, @Nullable String boolean useVariable = StringUtils.hasText(variable) // && !variable.startsWith(" new") // && !variable.startsWith("count(") // - && !variable.contains(","); // + && !variable.contains(",") // + && !variable.contains("*"); String complexCountValue = matcher.matches() && StringUtils.hasText(matcher.group(COMPLEX_COUNT_FIRST_INDEX)) ? COMPLEX_COUNT_VALUE diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index d7aaad1686..6082e4163c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -421,6 +421,11 @@ void createCountQuerySupportsWhitespaceCharacters() { " where user.age = 18\n "); } + @Test // GH-2341 + void createCountQueryStarCharacterConverted() { + assertThat(createCountQueryFor("select * from User user")).isEqualTo("select count(user) from User user"); + } + @Test void createCountQuerySupportsLineBreaksInSelectClause() { @@ -522,6 +527,28 @@ void findProjectionClauseWithIncludedFrom() { assertThat(QueryUtils.getProjection("select x, frommage, y from t")).isEqualTo("x, frommage, y"); } + @Test // GH-2341 + void countProjectionDistrinctQueryIncludesNewLineAfterFromAndBeforeJoin() { + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1\nLEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"; + + assertCountQuery(originalQuery, + "select count(DISTINCT entity1) FROM Entity1 entity1\nLEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test // GH-2341 + void countProjectionDistinctQueryIncludesNewLineAfterEntity() { + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"; + assertCountQuery(originalQuery, + "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test // GH-2341 + void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799"; + assertCountQuery(originalQuery, + "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799"); + } + private static void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 1336809b302eb1c4b4e8963e5465e53694f7497d Mon Sep 17 00:00:00 2001 From: Jedrzej Biedrzycki Date: Mon, 27 Sep 2021 01:39:55 +0200 Subject: [PATCH 170/821] Properly handle nested ORDER BY clauses. In several ways, it's possible to have various ORDER BY clauses, including SQL OVER (windowing) clauses. Need to improve handling of ORDER BY. See #2260. --- .../data/jpa/repository/query/QueryUtils.java | 43 +++++++++-- .../repository/query/QueryUtilsUnitTests.java | 76 +++++++++++++++++++ 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 1bded19616..eba4466bb7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -75,6 +75,7 @@ * @author Peter Großmann * @author Greg Turnquist * @author Diego Krupitza + * @author Jędrzej Biedrzycki */ public abstract class QueryUtils { @@ -108,7 +109,8 @@ public abstract class QueryUtils { private static final Pattern JOIN_PATTERN = Pattern.compile(JOIN, Pattern.CASE_INSENSITIVE); private static final String EQUALS_CONDITION_STRING = "%s.%s = :%s"; - private static final Pattern ORDER_BY = Pattern.compile(".*order\\s+by\\s+.*", CASE_INSENSITIVE); + private static final Pattern ORDER_BY = Pattern.compile("(order\\s+by\\s+)", CASE_INSENSITIVE); + private static final Pattern ORDER_BY_IN_WINDOW_OR_SUBSELECT = Pattern.compile("(\\(\\s*[a-z0-9 ,.*]*order\\s+by\\s+[a-z0-9 ,.]*\\s*\\))", CASE_INSENSITIVE); private static final Pattern NAMED_PARAMETER = Pattern.compile(COLON_NO_DOUBLE_COLON + IDENTIFIER + "|#" + IDENTIFIER, CASE_INSENSITIVE); @@ -257,10 +259,10 @@ public static String applySorting(String query, Sort sort, @Nullable String alia StringBuilder builder = new StringBuilder(query); - if (!ORDER_BY.matcher(query).matches()) { - builder.append(" order by "); - } else { + if (hasOrderByClause(query)) { builder.append(", "); + } else { + builder.append(" order by "); } Set joinAliases = getOuterJoinAliases(query); @@ -276,6 +278,31 @@ public static String applySorting(String query, Sort sort, @Nullable String alia return builder.toString(); } + /** + * Returns {@code true} if the query has {@code order by} clause. + * The query has {@code order by} clause if there is an {@code order by} which is not part of window clause. + * + * @param query the analysed query string + * @return {@code true} if the query has {@code order by} clause, {@code false} otherwise + */ + private static boolean hasOrderByClause(String query) { + return countOccurences(ORDER_BY, query) > countOccurences(ORDER_BY_IN_WINDOW_OR_SUBSELECT, query); + } + + /** + * Counts the number of occurrences of the pattern in the string + * + * @param pattern regex with a group to match + * @param string analysed string + * @return the number of occurences of the pattern in the string + */ + private static int countOccurences(Pattern pattern, String string) { + Matcher matcher = pattern.matcher(string); + int occurences = 0; + while (matcher.find()) occurences++; + return occurences; + } + /** * Returns the order clause for the given {@link Order}. Will prefix the clause with the given alias if the referenced * property refers to a join alias, i.e. starts with {@code $alias.}. @@ -397,10 +424,12 @@ private static String toJpaDirection(Order order) { @Nullable @Deprecated public static String detectAlias(String query) { - + String alias = null; Matcher matcher = ALIAS_MATCH.matcher(query); - - return matcher.find() ? matcher.group(2) : null; + while (matcher.find()) { + alias = matcher.group(2); + } + return alias; } /** diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 6082e4163c..f9b48335be 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -40,6 +40,7 @@ * @author Grégoire Druant * @author Mohammad Hewedy * @author Greg Turnquist + * @author Jędrzej Biedrzycki */ class QueryUtilsUnitTests { @@ -552,4 +553,79 @@ void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { private static void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } + + @Test // GH-2260 + void applySortingAccountsForNativeWindowFunction() { + + Sort sort = Sort.by(Order.desc("age")); + + // order by absent + assertThat(QueryUtils.applySorting("select * from user u", sort)) + .isEqualTo("select * from user u order by u.age desc"); + + // order by present + assertThat(QueryUtils.applySorting("select * from user u order by u.lastname", sort)) + .isEqualTo("select * from user u order by u.lastname, u.age desc"); + + // partition by + assertThat(QueryUtils.applySorting("select dense_rank() over (partition by age) from user u", sort)) + .isEqualTo("select dense_rank() over (partition by age) from user u order by u.age desc"); + + // order by in over clause + assertThat(QueryUtils.applySorting("select dense_rank() over (order by lastname) from user u", sort)) + .isEqualTo("select dense_rank() over (order by lastname) from user u order by u.age desc"); + + // order by in over clause (additional spaces) + assertThat(QueryUtils.applySorting("select dense_rank() over ( order by lastname ) from user u", sort)) + .isEqualTo("select dense_rank() over ( order by lastname ) from user u order by u.age desc"); + + // order by in over clause + at the end + assertThat( + QueryUtils.applySorting("select dense_rank() over (order by lastname) from user u order by u.lastname", sort)) + .isEqualTo("select dense_rank() over (order by lastname) from user u order by u.lastname, u.age desc"); + + // partition by + order by in over clause + assertThat(QueryUtils.applySorting( + "select dense_rank() over (partition by active, age order by lastname) from user u", sort)).isEqualTo( + "select dense_rank() over (partition by active, age order by lastname) from user u order by u.age desc"); + + // partition by + order by in over clause + order by at the end + assertThat(QueryUtils.applySorting( + "select dense_rank() over (partition by active, age order by lastname) from user u order by active", sort)) + .isEqualTo( + "select dense_rank() over (partition by active, age order by lastname) from user u order by active, u.age desc"); + + // partition by + order by in over clause + frame clause + assertThat(QueryUtils.applySorting( + "select dense_rank() over ( partition by active, age order by username rows between current row and unbounded following ) from user u", + sort)).isEqualTo( + "select dense_rank() over ( partition by active, age order by username rows between current row and unbounded following ) from user u order by u.age desc"); + + // partition by + order by in over clause + frame clause + order by at the end + assertThat(QueryUtils.applySorting( + "select dense_rank() over ( partition by active, age order by username rows between current row and unbounded following ) from user u order by active", + sort)).isEqualTo( + "select dense_rank() over ( partition by active, age order by username rows between current row and unbounded following ) from user u order by active, u.age desc"); + + // order by in subselect (select expression) + assertThat( + QueryUtils.applySorting("select lastname, (select i.id from item i order by i.id limit 1) from user u", sort)) + .isEqualTo( + "select lastname, (select i.id from item i order by i.id limit 1) from user u order by u.age desc"); + + // order by in subselect (select expression) + at the end + assertThat(QueryUtils.applySorting( + "select lastname, (select i.id from item i order by 1 limit 1) from user u order by active", sort)).isEqualTo( + "select lastname, (select i.id from item i order by 1 limit 1) from user u order by active, u.age desc"); + + // order by in subselect (from expression) + assertThat(QueryUtils.applySorting("select * from (select * from user order by age desc limit 10) u", sort)) + .isEqualTo("select * from (select * from user order by age desc limit 10) u order by age desc"); + + // order by in subselect (from expression) + at the end + assertThat(QueryUtils.applySorting( + "select * from (select * from user order by 1, 2, 3 desc limit 10) u order by u.active asc", sort)).isEqualTo( + "select * from (select * from user order by 1, 2, 3 desc limit 10) u order by u.active asc, age desc"); + } + } From 500bdb86b7b310e729104695325c50f43223cb5b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 28 Mar 2022 11:41:18 -0500 Subject: [PATCH 171/821] Add fluent findBy API to JpaSpecificationExecutor. Extend fluent findBy support through the usage of Specifications. See #2274. --- .../data/jpa/domain/Specification.java | 4 +- .../repository/JpaSpecificationExecutor.java | 14 ++ .../FetchableFluentQueryBySpecification.java | 196 ++++++++++++++++++ .../support/SimpleJpaRepository.java | 34 ++- .../jpa/repository/UserRepositoryTests.java | 184 +++++++++++++++- 5 files changed, 413 insertions(+), 19 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java index 8fa8f766e9..75708b0ec0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java @@ -15,13 +15,13 @@ */ package org.springframework.data.jpa.domain; -import java.io.Serializable; - import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import java.io.Serializable; + import org.springframework.lang.Nullable; /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index e310f8709f..b90b88d8fd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -17,11 +17,13 @@ import java.util.List; import java.util.Optional; +import java.util.function.Function; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.repository.query.FluentQuery; import org.springframework.lang.Nullable; /** @@ -84,4 +86,16 @@ public interface JpaSpecificationExecutor { * false. */ boolean exists(Specification spec); + + /** + * Returns entities matching the given {@link Specification} applying the {@code queryFunction} that defines the query + * and its result type. + * + * @param spec – must not be null. + * @param queryFunction – the query function defining projection, sorting, and the result type + * @return all entities matching the given Example. + * @since 3.0 + */ + R findBy(Specification spec, Function, R> queryFunction); + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java new file mode 100644 index 0000000000..b88111855f --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java @@ -0,0 +1,196 @@ +/* + * Copyright 2021-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.repository.query.FluentQuery; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.util.Assert; + +/** + * Immutable implementation of {@link FetchableFluentQuery} based on a {@link Specification}. All methods that return a + * {@link FetchableFluentQuery} will return a new instance, not the original. + * + * @param Domain type + * @param Result type + * @author Greg Turnquist + * @since 3.0 + */ +class FetchableFluentQueryBySpecification extends FluentQuerySupport + implements FluentQuery.FetchableFluentQuery { + + private final Specification spec; + private final Function> finder; + private final Function, Long> countOperation; + private final Function, Boolean> existsOperation; + private final EntityManager entityManager; + + public FetchableFluentQueryBySpecification(Specification spec, Class entityType, Sort sort, + Collection properties, Function> finder, + Function, Long> countOperation, Function, Boolean> existsOperation, + EntityManager entityManager) { + this(spec, entityType, (Class) entityType, Sort.unsorted(), Collections.emptySet(), finder, countOperation, + existsOperation, entityManager); + } + + private FetchableFluentQueryBySpecification(Specification spec, Class entityType, Class resultType, + Sort sort, Collection properties, Function> finder, + Function, Long> countOperation, Function, Boolean> existsOperation, + EntityManager entityManager) { + + super(resultType, sort, properties, entityType); + this.spec = spec; + this.finder = finder; + this.countOperation = countOperation; + this.existsOperation = existsOperation; + this.entityManager = entityManager; + } + + @Override + public FetchableFluentQuery sortBy(Sort sort) { + + Assert.notNull(sort, "Sort must not be null!"); + + return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, this.sort.and(sort), properties, + finder, countOperation, existsOperation, entityManager); + } + + @Override + public FetchableFluentQuery as(Class resultType) { + + Assert.notNull(resultType, "Projection target type must not be null!"); + if (!resultType.isInterface()) { + throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); + } + + return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, properties, finder, + countOperation, existsOperation, entityManager); + } + + @Override + public FetchableFluentQuery project(Collection properties) { + + return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, properties, finder, + countOperation, existsOperation, entityManager); + } + + @Override + public R oneValue() { + + List results = createSortedAndProjectedQuery() // + .setMaxResults(2) // Never need more than 2 values + .getResultList(); + + if (results.size() > 1) { + throw new IncorrectResultSizeDataAccessException(1); + } + + return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); + } + + @Override + public R firstValue() { + + List results = createSortedAndProjectedQuery() // + .setMaxResults(1) // Never need more than 1 value + .getResultList(); + + return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); + } + + @Override + public List all() { + return convert(createSortedAndProjectedQuery().getResultList()); + } + + @Override + public Page page(Pageable pageable) { + return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); + } + + @Override + public Stream stream() { + + return createSortedAndProjectedQuery() // + .getResultStream() // + .map(getConversionFunction()); + } + + @Override + public long count() { + return countOperation.apply(spec); + } + + @Override + public boolean exists() { + return existsOperation.apply(spec); + } + + private TypedQuery createSortedAndProjectedQuery() { + + TypedQuery query = finder.apply(sort); + + if (!properties.isEmpty()) { + query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties)); + } + + return query; + } + + private Page readPage(Pageable pageable) { + + TypedQuery pagedQuery = createSortedAndProjectedQuery(); + + if (pageable.isPaged()) { + pagedQuery.setFirstResult((int) pageable.getOffset()); + pagedQuery.setMaxResults(pageable.getPageSize()); + } + + List paginatedResults = convert(pagedQuery.getResultList()); + + return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(spec)); + } + + private List convert(List resultList) { + + Function conversionFunction = getConversionFunction(); + List mapped = new ArrayList<>(resultList.size()); + + for (S s : resultList) { + mapped.add(conversionFunction.apply(s)); + } + return mapped; + } + + private Function getConversionFunction() { + return getConversionFunction(entityType, resultType); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 6ef501c066..f98555f8c6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -17,15 +17,6 @@ import static org.springframework.data.jpa.repository.query.QueryUtils.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - import jakarta.persistence.EntityManager; import jakarta.persistence.LockModeType; import jakarta.persistence.NoResultException; @@ -39,6 +30,15 @@ import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; @@ -515,6 +515,22 @@ public R findBy(Example example, Function R findBy(Specification spec, Function, R> queryFunction) { + + Assert.notNull(spec, "Specification must not be null!"); + Assert.notNull(queryFunction, "Query function must not be null!"); + + Function> finder = sort -> { + return getQuery(spec, getDomainClass(), sort); + }; + + FetchableFluentQuery fluentQuery = new FetchableFluentQueryBySpecification(spec, getDomainClass(), + Sort.unsorted(), null, finder, this::count, this::exists, this.em); + + return queryFunction.apply((FetchableFluentQuery) fluentQuery); + } + @Override public long count() { return em.createQuery(getCountQueryString(), Long.class).getSingleResult(); 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 f257136502..fcf384fac7 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 @@ -24,6 +24,13 @@ import static org.springframework.data.jpa.domain.Specification.not; import static org.springframework.data.jpa.domain.sample.UserSpecifications.*; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import lombok.Data; import java.util.ArrayList; @@ -36,14 +43,6 @@ import java.util.Set; import java.util.stream.Stream; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Query; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Predicate; -import jakarta.persistence.criteria.Root; - import org.assertj.core.api.SoftAssertions; import org.hibernate.LazyInitializationException; import org.junit.jupiter.api.BeforeEach; @@ -2298,6 +2297,175 @@ void existsByFluentExample() { assertThat(exists).isTrue(); } + @Test // GH-2274 + void findByFluentSpecificationWithSorting() { + + flushTestUsers(); + + List users = repository.findBy(userHasFirstnameLike("v"), q -> q.sortBy(Sort.by("firstname")).all()); + + assertThat(users).containsExactly(thirdUser, firstUser, fourthUser); + } + + @Test // GH-2274 + void findByFluentSpecificationFirstValue() { + + flushTestUsers(); + + User firstUser = repository.findBy(userHasFirstnameLike("v"), q -> q.sortBy(Sort.by("firstname")).firstValue()); + + assertThat(firstUser).isEqualTo(thirdUser); + } + + @Test // GH-2274 + void findByFluentSpecificationOneValue() { + + flushTestUsers(); + + assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) + .isThrownBy(() -> repository.findBy(userHasFirstnameLike("v"), q -> q.sortBy(Sort.by("firstname")).oneValue())); + } + + @Test // GH-2274 + void findByFluentSpecificationStream() { + + flushTestUsers(); + + Stream userStream = repository.findBy(userHasFirstnameLike("v"), + q -> q.sortBy(Sort.by("firstname")).stream()); + + assertThat(userStream).containsExactly(thirdUser, firstUser, fourthUser); + } + + @Test // GH-2274 + void findByFluentSpecificationPage() { + + flushTestUsers(); + + Page page0 = repository.findBy(userHasFirstnameLike("v"), + q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(0, 2))); + + Page page1 = repository.findBy(userHasFirstnameLike("v"), + q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(1, 2))); + + assertThat(page0.getContent()).containsExactly(thirdUser, firstUser); + assertThat(page1.getContent()).containsExactly(fourthUser); + } + + @Test // GH-2274 + void findByFluentSpecificationWithInterfaceBasedProjection() { + + flushTestUsers(); + + List users = repository.findBy(userHasFirstnameLike("v"), + q -> q.as(UserProjectionInterfaceBased.class).all()); + + assertThat(users).extracting(UserProjectionInterfaceBased::getFirstname) + .containsExactlyInAnyOrder(firstUser.getFirstname(), thirdUser.getFirstname(), fourthUser.getFirstname()); + } + + @Test // GH-2274 + void findByFluentSpecificationWithSimplePropertyPathsDoesntLoadUnrequestedPaths() { + + flushTestUsers(); + // make sure we don't get preinitialized entities back: + em.clear(); + + List users = repository.findBy(userHasFirstnameLike("v"), q -> q.project("firstname").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + + assertThatExceptionOfType(LazyInitializationException.class) // + .isThrownBy( // + () -> users.forEach(u -> u.getRoles().size()) // forces loading of roles + ); + } + + @Test // GH-2274 + void findByFluentSpecificationWithCollectionPropertyPathsDoesntLoadUnrequestedPaths() { + + flushTestUsers(); + // make sure we don't get preinitialized entities back: + em.clear(); + + List users = repository.findBy(userHasFirstnameLike("v"), q -> q.project("firstname", "roles").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + + assertThat(users).allMatch(u -> u.getRoles().isEmpty()); + } + + @Test // GH-2274 + void findByFluentSpecificationWithComplexPropertyPathsDoesntLoadUnrequestedPaths() { + + flushTestUsers(); + // make sure we don't get preinitialized entities back: + em.clear(); + + List users = repository.findBy(userHasFirstnameLike("v"), q -> q.project("roles.name").all()); + + // remove the entities, so lazy loading throws an exception + em.clear(); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + + assertThat(users).allMatch(u -> u.getRoles().isEmpty()); + } + + @Test // GH-2274 + void findByFluentSpecificationWithSortedInterfaceBasedProjection() { + + flushTestUsers(); + + List users = repository.findBy(userHasFirstnameLike("v"), + q -> q.as(UserProjectionInterfaceBased.class).sortBy(Sort.by("firstname")).all()); + + assertThat(users).extracting(UserProjectionInterfaceBased::getFirstname) + .containsExactlyInAnyOrder(thirdUser.getFirstname(), firstUser.getFirstname(), fourthUser.getFirstname()); + } + + @Test // GH-2274 + void fluentSpecificationWithClassBasedDtosNotYetSupported() { + + @Data + class UserDto { + String firstname; + } + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> { + repository.findBy(userHasFirstnameLike("v"), q -> q.as(UserDto.class).sortBy(Sort.by("firstname")).all()); + }); + } + + @Test // GH-2274 + void countByFluentSpecification() { + + flushTestUsers(); + + long numOfUsers = repository.findBy(userHasFirstnameLike("v"), q -> q.sortBy(Sort.by("firstname")).count()); + + assertThat(numOfUsers).isEqualTo(3); + } + + @Test // GH-2274 + void existsByFluentSpecification() { + + flushTestUsers(); + + boolean exists = repository.findBy(userHasFirstnameLike("v"), q -> q.sortBy(Sort.by("firstname")).exists()); + + assertThat(exists).isTrue(); + } + @Test // DATAJPA-218 void countByExampleWithExcludedAttributes() { From c053f08d6565692172a073247c97f41b0f2d5b03 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 13 Apr 2022 11:01:19 -0500 Subject: [PATCH 172/821] Convert Iterable to Collection for deleteAllByIdInBatch. JpaRepository accepts Iterable for bulk deletes. But some JPA providers require Collection instead. To avoid breaking any APIs, convert the incoming argument if it's not already a Collection. See #2242. --- .../support/SimpleJpaRepository.java | 13 +++++- .../EclipseLinkJpaRepositoryTests.java | 13 ++++-- .../support/JpaRepositoryTests.java | 40 +++++++++++++++++-- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index f98555f8c6..ebdec761b3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -38,6 +38,8 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Example; @@ -223,7 +225,16 @@ public void deleteAllByIdInBatch(Iterable ids) { entityInformation.getIdAttribute().getName()); Query query = em.createQuery(queryString); - query.setParameter("ids", ids); + /** + * Some JPA providers require {@code ids} to be a {@link Collection} so we must convert if it's not already. + */ + if (Collection.class.isInstance(ids)) { + query.setParameter("ids", ids); + } else { + Collection idsCollection = StreamSupport.stream(ids.spliterator(), false) + .collect(Collectors.toCollection(ArrayList::new)); + query.setParameter("ids", idsCollection); + } query.executeUpdate(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java index 2574770e7e..bb16ec76c5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java @@ -22,15 +22,20 @@ * Integration tests to execute {@link JpaRepositoryTests} against EclipseLink. * * @author Oliver Gierke + * @author Greg Turnquist */ @ContextConfiguration("classpath:eclipselink.xml") class EclipseLinkJpaRepositoryTests extends JpaRepositoryTests { @Override - /** - * Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 is resolved. - */ + @Disabled("https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477") void deleteAllByIdInBatch() { - super.deleteAllByIdInBatch(); + // disabled + } + + @Override + @Disabled("https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477") + void deleteAllByIdInBatchShouldConvertAnIterableToACollection() { + // disabled } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java index ea5e179173..9b5860e1d2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java @@ -17,16 +17,18 @@ import static org.assertj.core.api.Assertions.*; -import java.util.Arrays; -import java.util.Optional; - import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.data.jpa.domain.sample.PersistableWithIdClass; import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK; import org.springframework.data.jpa.domain.sample.SampleEntity; @@ -43,6 +45,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Jens Schauder + * @author Greg Turnquist */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @@ -128,6 +131,35 @@ void deleteAllByIdInBatch() { assertThat(repository.findAll()).containsExactly(two); } + @Test // GH-2242 + void deleteAllByIdInBatchShouldConvertAnIterableToACollection() { + + SampleEntity one = new SampleEntity("one", "eins"); + SampleEntity two = new SampleEntity("two", "zwei"); + SampleEntity three = new SampleEntity("three", "drei"); + repository.saveAll(Arrays.asList(one, two, three)); + repository.flush(); + + /** + * Wrap a {@link List} inside an {@link Iterable} to verify that {@link SimpleJpaRepository} can properly convert a + * pure {@link Iterable} to a {@link Collection}. + **/ + Iterable ids = new Iterable() { + + private List ids = Arrays.asList(new SampleEntityPK("one", "eins"), + new SampleEntityPK("three", "drei")); + + @NotNull + @Override + public Iterator iterator() { + return ids.iterator(); + } + }; + + repository.deleteAllByIdInBatch(ids); + assertThat(repository.findAll()).containsExactly(two); + } + private interface SampleEntityRepository extends JpaRepository { } From 94e84cc8c498aa25bce249087fadb99cfc5d0dbd Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 14 Apr 2022 09:03:12 -0500 Subject: [PATCH 173/821] Allow count queries to start with whitespace. See #2393. --- .../data/jpa/repository/query/QueryUtils.java | 14 ++++++++++---- .../jpa/repository/query/QueryUtilsUnitTests.java | 10 ++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index eba4466bb7..da041122da 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -110,7 +110,8 @@ public abstract class QueryUtils { private static final String EQUALS_CONDITION_STRING = "%s.%s = :%s"; private static final Pattern ORDER_BY = Pattern.compile("(order\\s+by\\s+)", CASE_INSENSITIVE); - private static final Pattern ORDER_BY_IN_WINDOW_OR_SUBSELECT = Pattern.compile("(\\(\\s*[a-z0-9 ,.*]*order\\s+by\\s+[a-z0-9 ,.]*\\s*\\))", CASE_INSENSITIVE); + private static final Pattern ORDER_BY_IN_WINDOW_OR_SUBSELECT = Pattern + .compile("(\\(\\s*[a-z0-9 ,.*]*order\\s+by\\s+[a-z0-9 ,.]*\\s*\\))", CASE_INSENSITIVE); private static final Pattern NAMED_PARAMETER = Pattern.compile(COLON_NO_DOUBLE_COLON + IDENTIFIER + "|#" + IDENTIFIER, CASE_INSENSITIVE); @@ -144,6 +145,7 @@ public abstract class QueryUtils { ALIAS_MATCH = compile(builder.toString(), CASE_INSENSITIVE); builder = new StringBuilder(); + builder.append("\\s*"); builder.append("(select\\s+((distinct)?((?s).+?)?)\\s+)?(from\\s+"); builder.append(IDENTIFIER); builder.append("(?:\\s+as)?\\s+)"); @@ -279,8 +281,8 @@ public static String applySorting(String query, Sort sort, @Nullable String alia } /** - * Returns {@code true} if the query has {@code order by} clause. - * The query has {@code order by} clause if there is an {@code order by} which is not part of window clause. + * Returns {@code true} if the query has {@code order by} clause. The query has {@code order by} clause if there is an + * {@code order by} which is not part of window clause. * * @param query the analysed query string * @return {@code true} if the query has {@code order by} clause, {@code false} otherwise @@ -297,9 +299,13 @@ private static boolean hasOrderByClause(String query) { * @return the number of occurences of the pattern in the string */ private static int countOccurences(Pattern pattern, String string) { + Matcher matcher = pattern.matcher(string); + int occurences = 0; - while (matcher.find()) occurences++; + while (matcher.find()) { + occurences++; + } return occurences; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index f9b48335be..9a3e86965e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -550,6 +550,16 @@ void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799"); } + @Test // GH-2393 + void createCountQueryStartsWithWhitespace() { + + assertThat(createCountQueryFor(" \nselect * from User u where u.age > :age")) + .isEqualTo("select count(u) from User u where u.age > :age"); + + assertThat(createCountQueryFor(" \nselect u from User u where u.age > :age")) + .isEqualTo("select count(u) from User u where u.age > :age"); + } + private static void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From a70fff4515d04be2e0f6f18edbcd7c1c0bff704e Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 14 Apr 2022 09:17:51 -0500 Subject: [PATCH 174/821] Fix grammar mistake in reference documentation. See #2487. --- README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index d1a568deb9..ec7736e3bb 100644 --- a/README.adoc +++ b/README.adoc @@ -3,8 +3,8 @@ image:https://spring.io/badges/spring-data-jpa/snapshot.svg[Spring Data JPA,link = Spring Data JPA image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] -Spring Data JPA, part of the larger https://projects.spring.io/spring-data[Spring Data] family, makes it easy to easily implement JPA based repositories. -This module deals with enhanced support for JPA based data access layers. +Spring Data JPA, part of the larger https://projects.spring.io/spring-data[Spring Data] family, makes it easy to implement JPA-based repositories. +This module deals with enhanced support for JPA-based data access layers. It makes it easier to build Spring-powered applications that use data access technologies. Implementing a data access layer of an application has been cumbersome for quite a while. From 264472ba0f43331bb79ac2c083551739ba940789 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 22 Mar 2022 09:56:05 -0500 Subject: [PATCH 175/821] Introduce QueryRewriter. Allow a QueryRewriter to be applied to any query crafted using @Query via an additional @QueryRewriter annotation. Also supported directly inside @Query. See #2162. --- .../data/jpa/repository/Query.java | 17 +- .../data/jpa/repository/QueryRewriter.java | 64 +++++ .../jpa/repository/cdi/JpaRepositoryBean.java | 21 +- .../cdi/QueryRewriterBeanManagerProvider.java | 55 ++++ .../query/AbstractStringBasedJpaQuery.java | 48 +++- .../jpa/repository/query/JpaQueryFactory.java | 10 +- .../query/JpaQueryLookupStrategy.java | 47 ++-- .../jpa/repository/query/JpaQueryMethod.java | 18 +- .../jpa/repository/query/NativeJpaQuery.java | 13 +- .../QueryRewriterBeanFactoryProvider.java | 50 ++++ .../query/QueryRewriterNoopProvider.java | 45 ++++ .../query/QueryRewriterProvider.java | 71 ++++++ .../jpa/repository/query/SimpleJpaQuery.java | 12 +- .../support/JpaRepositoryFactory.java | 52 +++- .../cdi/CdiExtensionIntegrationTests.java | 8 +- ...aQueryRewriterWithCdiIntegrationTests.java | 234 ++++++++++++++++++ ...ctStringBasedJpaQueryIntegrationTests.java | 24 +- .../JpaQueryLookupStrategyUnitTests.java | 20 +- .../JpaQueryRewriteIntegrationTests.java | 218 ++++++++++++++++ .../query/SimpleJpaQueryUnitTests.java | 28 +-- src/main/asciidoc/jpa.adoc | 2 +- 21 files changed, 964 insertions(+), 93 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java index f12e7c39c4..614bee69cb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java @@ -29,7 +29,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl - * + * @author Greg Turnquist * @see Modifying */ @Retention(RetentionPolicy.RUNTIME) @@ -45,7 +45,8 @@ /** * Defines a special count query that shall be used for pagination queries to lookup the total number of elements for - * a page. If none is configured we will derive the count query from the original query or {@link #countProjection()} query if any. + * a page. If none is configured we will derive the count query from the original query or {@link #countProjection()} + * query if any. */ String countQuery() default ""; @@ -70,11 +71,19 @@ String name() default ""; /** - * Returns the name of the {@link jakarta.persistence.NamedQuery} to be used to execute count queries when pagination is - * used. Will default to the named query name configured suffixed by {@code .count}. + * Returns the name of the {@link jakarta.persistence.NamedQuery} to be used to execute count queries when pagination + * is used. Will default to the named query name configured suffixed by {@code .count}. * * @see #name() * @return */ String countName() default ""; + + /** + * Define the {@link QueryRewriter} bean that should be applied to this query after the query is full assembled. + * + * @return + * @since 3.0 + */ + Class queryRewriter() default QueryRewriter.NoopQueryRewriter.class; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java new file mode 100644 index 0000000000..d80a998c23 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java @@ -0,0 +1,64 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +/** + * Callback to rewrite a query right before it's handed to the EntityManager. + * + * @author Greg Turnquist + * @since 3.0 + */ +@FunctionalInterface +public interface QueryRewriter { + + /** + * The assembled query and current {@link Sort} settings are offered. This is the query right before it's handed to + * the EntityManager, so everything that Spring Data and tools intends to do has been done. The user is able to make + * any last minute changes.
+ *
+ * WARNING: No checks are performed before the transformed query is passed to the EntityManager. + * + * @param query - the assembled generated query, right before it's handed over to the EntityManager. + * @param sort - current {@link Sort} settings provided by the method, or {@link Sort#unsorted()}} if there are none. + * @return alter the query however you like. + */ + String rewrite(String query, Sort sort); + + /** + * This alternative is used to handle {@link Pageable}-based methods. + * + * @param query - the assembled generated query, right before it's handed over to the EntityManager. + * @param pageRequest + * @return + */ + default String rewrite(String query, Pageable pageRequest) { + return rewrite(query, pageRequest.getSort()); + } + + /** + * A {@link QueryRewriter} that doesn't change the query. + */ + public class NoopQueryRewriter implements QueryRewriter { + + @Override + public String rewrite(String query, Sort sort) { + return query; + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index 84dec88f98..3f9b522d24 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -15,18 +15,21 @@ */ package org.springframework.data.jpa.repository.cdi; -import java.lang.annotation.Annotation; -import java.util.Optional; -import java.util.Set; - import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.persistence.EntityManager; +import java.lang.annotation.Annotation; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; + +import org.springframework.data.jpa.repository.query.QueryRewriterProvider; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.repository.cdi.CdiRepositoryBean; import org.springframework.data.repository.config.CustomRepositoryImplementationDetector; +import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.util.Assert; /** @@ -41,6 +44,7 @@ class JpaRepositoryBean extends CdiRepositoryBean { private final Bean entityManagerBean; + private final QueryRewriterProvider queryRewriterProvider; /** * Constructs a {@link JpaRepositoryBean}. @@ -58,6 +62,7 @@ class JpaRepositoryBean extends CdiRepositoryBean { Assert.notNull(entityManagerBean, "EntityManager bean must not be null!"); this.entityManagerBean = entityManagerBean; + this.queryRewriterProvider = new QueryRewriterBeanManagerProvider(beanManager); } @Override @@ -65,6 +70,12 @@ protected T create(CreationalContext creationalContext, Class repositoryTy EntityManager entityManager = getDependencyInstance(entityManagerBean, EntityManager.class); - return create(() -> new JpaRepositoryFactory(entityManager), repositoryType); + Supplier repositoryFactorySupportSupplier = () -> { + JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager); + jpaRepositoryFactory.setQueryRewriterProvider(queryRewriterProvider); + return jpaRepositoryFactory; + }; + + return create(repositoryFactorySupportSupplier, repositoryType); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java new file mode 100644 index 0000000000..8a4369da82 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.cdi; + +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.BeanManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.data.jpa.repository.query.QueryRewriterProvider; + +/** + * A {@link BeanManager}-based {@link QueryRewriterProvider}. + * + * @author Greg Turnquist + * @since 3.0 + */ +public class QueryRewriterBeanManagerProvider extends QueryRewriterProvider { + + private static final Log LOGGER = LogFactory.getLog(QueryRewriterBeanManagerProvider.class); + + private final BeanManager beanManager; + + public QueryRewriterBeanManagerProvider(BeanManager beanManager) { + this.beanManager = beanManager; + } + + @Override + protected QueryRewriter extractQueryRewriterBean(Class queryRewriter) { + + try { + Bean bean = (Bean) beanManager.getBeans(queryRewriter).iterator().next(); + CreationalContext context = beanManager.createCreationalContext(bean); + return (QueryRewriter) beanManager.getReference(bean, queryRewriter, context); + } catch (Exception e) { + LOGGER.error(e.toString()); + return null; + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 2294ee18f4..6f4eb5bdb2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -18,6 +18,13 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; +import java.util.function.Supplier; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; @@ -35,14 +42,18 @@ * @author David Madden * @author Mark Paluch * @author Diego Krupitza + * @author Greg Turnquist */ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { + private static final Log LOGGER = LogFactory.getLog(AbstractStringBasedJpaQuery.class); + private final DeclaredQuery query; private final DeclaredQuery countQuery; private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final SpelExpressionParser parser; private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache(); + private final Supplier queryRewriterSupplier; /** * Creates a new {@link AbstractStringBasedJpaQuery} from the given {@link JpaQueryMethod}, {@link EntityManager} and @@ -57,7 +68,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { */ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, - SpelExpressionParser parser) { + SpelExpressionParser parser, QueryRewriterProvider queryRewriterProvider) { super(method, em); @@ -74,6 +85,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri method.isNativeQuery()); this.parser = parser; + this.queryRewriterSupplier = queryRewriterProvider.of(method); Assert.isTrue(method.isNativeQuery() || !query.usesJdbcStyleParameters(), "JDBC style parameters (?) are not supported for JPA queries."); @@ -86,7 +98,8 @@ public Query doCreateQuery(JpaParametersParameterAccessor accessor) { .applySorting(accessor.getSort(), query.getAlias()); ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor); - Query query = createJpaQuery(sortedQueryString, processor.getReturnedType()); + Query query = createJpaQuery(sortedQueryString, accessor.getSort(), accessor.getPageable(), + processor.getReturnedType()); QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata(sortedQueryString, query); @@ -137,18 +150,41 @@ public DeclaredQuery getCountQuery() { * Creates an appropriate JPA query from an {@link EntityManager} according to the current {@link AbstractJpaQuery} * type. */ - protected Query createJpaQuery(String queryString, ReturnedType returnedType) { + protected Query createJpaQuery(String queryString, Sort sort, @Nullable Pageable pageable, + ReturnedType returnedType) { EntityManager em = getEntityManager(); if (this.query.hasConstructorExpression() || this.query.isDefaultProjection()) { - return em.createQuery(queryString); + return em.createQuery(potentiallyRewriteQuery(queryString, sort, pageable)); } Class typeToRead = getTypeToRead(returnedType); return typeToRead == null // - ? em.createQuery(queryString) // - : em.createQuery(queryString, typeToRead); + ? em.createQuery(potentiallyRewriteQuery(queryString, sort, pageable)) // + : em.createQuery(potentiallyRewriteQuery(queryString, sort, pageable), typeToRead); + } + + /** + * Use the {@link QueryRewriter}, potentially rewrite the query, using relevant {@link Sort} and {@link Pageable} + * information. + * + * @param originalQuery + * @param sort + * @param pageable + * @return + */ + protected String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nullable Pageable pageable) { + + QueryRewriter queryRewriter = this.queryRewriterSupplier.get(); + + if (queryRewriter == null) { + return originalQuery; + } + + return pageable != null && pageable.isPaged() // + ? queryRewriter.rewrite(originalQuery, pageable) // + : queryRewriter.rewrite(originalQuery, sort); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 6eada129d2..83df010e50 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -45,12 +45,14 @@ enum JpaQueryFactory { * @return */ AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, String queryString, - @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider) { + @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, + QueryRewriterProvider queryRewriterProvider) { return method.isNativeQuery() - ? new NativeJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER) - : new SimpleJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER); + ? new NativeJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER, + queryRewriterProvider) + : new SimpleJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER, + queryRewriterProvider); } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 0db5d16bae..552c587afd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -15,13 +15,12 @@ */ package org.springframework.data.jpa.repository.query; -import java.lang.reflect.Method; - import jakarta.persistence.EntityManager; +import java.lang.reflect.Method; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.data.jpa.repository.Query; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.NamedQueries; @@ -41,6 +40,7 @@ * @author Thomas Darimont * @author Mark Paluch * @author Réda Housni Alaoui + * @author Greg Turnquist */ public final class JpaQueryLookupStrategy { @@ -61,6 +61,7 @@ private abstract static class AbstractQueryLookupStrategy implements QueryLookup private final EntityManager em; private final JpaQueryMethodFactory queryMethodFactory; + private final QueryRewriterProvider queryRewriterProvider; /** * Creates a new {@link AbstractQueryLookupStrategy}. @@ -68,13 +69,15 @@ private abstract static class AbstractQueryLookupStrategy implements QueryLookup * @param em must not be {@literal null}. * @param queryMethodFactory must not be {@literal null}. */ - public AbstractQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory) { + public AbstractQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory, + QueryRewriterProvider queryRewriterProvider) { Assert.notNull(em, "EntityManager must not be null!"); Assert.notNull(queryMethodFactory, "JpaQueryMethodFactory must not be null!"); this.em = em; this.queryMethodFactory = queryMethodFactory; + this.queryRewriterProvider = queryRewriterProvider; } @Override @@ -84,6 +87,10 @@ public final RepositoryQuery resolveQuery(Method method, RepositoryMetadata meta } protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries); + + protected QueryRewriterProvider getQueryRewriterSupplier() { + return queryRewriterProvider; + } } /** @@ -97,9 +104,9 @@ private static class CreateQueryLookupStrategy extends AbstractQueryLookupStrate private final EscapeCharacter escape; public CreateQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory, - EscapeCharacter escape) { + QueryRewriterProvider queryRewriterProvider, EscapeCharacter escape) { - super(em, queryMethodFactory); + super(em, queryMethodFactory, queryRewriterProvider); this.escape = escape; } @@ -130,9 +137,9 @@ private static class DeclaredQueryLookupStrategy extends AbstractQueryLookupStra * @param evaluationContextProvider must not be {@literal null}. */ public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory, - QueryMethodEvaluationContextProvider evaluationContextProvider) { + QueryMethodEvaluationContextProvider evaluationContextProvider, QueryRewriterProvider queryRewriterProvider) { - super(em, queryMethodFactory); + super(em, queryMethodFactory, queryRewriterProvider); this.evaluationContextProvider = evaluationContextProvider; } @@ -152,14 +159,13 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, } return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getRequiredAnnotatedQuery(), - getCountQuery(method, namedQueries, em), - evaluationContextProvider); + getCountQuery(method, namedQueries, em), evaluationContextProvider, getQueryRewriterSupplier()); } String name = method.getNamedQueryName(); if (namedQueries.hasQuery(name)) { - return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), getCountQuery(method, namedQueries, em), - evaluationContextProvider); + return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), + getCountQuery(method, namedQueries, em), evaluationContextProvider, getQueryRewriterSupplier()); } RepositoryQuery query = NamedQuery.lookupFrom(method, em); @@ -221,9 +227,10 @@ private static class CreateIfNotFoundQueryLookupStrategy extends AbstractQueryLo * @param lookupStrategy must not be {@literal null}. */ public CreateIfNotFoundQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory, - CreateQueryLookupStrategy createStrategy, DeclaredQueryLookupStrategy lookupStrategy) { + CreateQueryLookupStrategy createStrategy, DeclaredQueryLookupStrategy lookupStrategy, + QueryRewriterProvider queryRewriterProvider) { - super(em, queryMethodFactory); + super(em, queryMethodFactory, queryRewriterProvider); Assert.notNull(createStrategy, "CreateQueryLookupStrategy must not be null!"); Assert.notNull(lookupStrategy, "DeclaredQueryLookupStrategy must not be null!"); @@ -253,20 +260,22 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, * @param escape must not be {@literal null}. */ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory queryMethodFactory, - @Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider, EscapeCharacter escape) { + @Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider, + QueryRewriterProvider queryRewriterProvider, EscapeCharacter escape) { Assert.notNull(em, "EntityManager must not be null!"); Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!"); switch (key != null ? key : Key.CREATE_IF_NOT_FOUND) { case CREATE: - return new CreateQueryLookupStrategy(em, queryMethodFactory, escape); + return new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape); case USE_DECLARED_QUERY: - return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider); + return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, queryRewriterProvider); case CREATE_IF_NOT_FOUND: return new CreateIfNotFoundQueryLookupStrategy(em, queryMethodFactory, - new CreateQueryLookupStrategy(em, queryMethodFactory, escape), - new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider)); + new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape), + new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, queryRewriterProvider), + queryRewriterProvider); default: throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key)); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index 2c998d4db4..88e0855277 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -15,6 +15,9 @@ */ package org.springframework.data.jpa.repository.query; +import jakarta.persistence.LockModeType; +import jakarta.persistence.QueryHint; + import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; @@ -24,9 +27,6 @@ import java.util.Optional; import java.util.Set; -import jakarta.persistence.LockModeType; -import jakarta.persistence.QueryHint; - import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.jpa.provider.QueryExtractor; @@ -35,6 +35,7 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.QueryHints; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.Parameter; @@ -57,6 +58,7 @@ * @author Mark Paluch * @author Сергей Цыпанов * @author Réda Housni Alaoui + * @author Greg Turnquist */ public class JpaQueryMethod extends QueryMethod { @@ -430,4 +432,14 @@ StoredProcedureAttributes getProcedureAttributes() { return storedProcedureAttributes; } + /** + * Returns the {@link QueryRewriter} type. + * + * @return type of the {@link QueryRewriter} + * @since 3.0 + */ + @Nullable + Class getQueryRewriter() { + return getMergedOrDefaultAnnotationValue("queryRewriter", Query.class, Class.class); + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index f038669198..c517ff0ae9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -19,6 +19,8 @@ import jakarta.persistence.Query; import jakarta.persistence.Tuple; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; @@ -35,6 +37,7 @@ * @author Oliver Gierke * @author Jens Schauder * @author Mark Paluch + * @author Greg Turnquist */ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { @@ -48,9 +51,10 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { * @param evaluationContextProvider */ public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { + QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, + QueryRewriterProvider queryRewriterProvider) { - super(method, em, queryString, countQueryString, evaluationContextProvider, parser); + super(method, em, queryString, countQueryString, evaluationContextProvider, parser, queryRewriterProvider); Parameters parameters = method.getParameters(); @@ -60,12 +64,13 @@ public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryStrin } @Override - protected Query createJpaQuery(String queryString, ReturnedType returnedType) { + protected Query createJpaQuery(String queryString, Sort sort, Pageable pageable, ReturnedType returnedType) { EntityManager em = getEntityManager(); Class type = getTypeToQueryFor(returnedType); - return type == null ? em.createNativeQuery(queryString) : em.createNativeQuery(queryString, type); + return type == null ? em.createNativeQuery(potentiallyRewriteQuery(queryString, sort, pageable)) + : em.createNativeQuery(potentiallyRewriteQuery(queryString, sort, pageable), type); } @Nullable diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java new file mode 100644 index 0000000000..102dc6c942 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.data.jpa.repository.QueryRewriter; + +/** + * A {@link BeanFactory}-based {@link QueryRewriterProvider}. + * + * @author Greg Turnquist + * @since 3.0 + */ +public class QueryRewriterBeanFactoryProvider extends QueryRewriterProvider { + + private static final Log LOGGER = LogFactory.getLog(QueryRewriterBeanFactoryProvider.class); + + private final BeanFactory beanFactory; + + public QueryRewriterBeanFactoryProvider(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + protected QueryRewriter extractQueryRewriterBean(Class queryRewriter) { + + try { + return beanFactory.getBean(queryRewriter); + } catch (BeansException e) { + LOGGER.error(e.toString()); + return null; + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java new file mode 100644 index 0000000000..094e377cd6 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java @@ -0,0 +1,45 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.data.jpa.repository.QueryRewriter; + +/** + * {@link QueryRewriterProvider} that does nothing. + * + * @author Greg Turnquist + * @since 3.0 + */ +public class QueryRewriterNoopProvider extends QueryRewriterProvider { + + private static final Log LOGGER = LogFactory.getLog(QueryRewriterNoopProvider.class); + + /** + * Returns {@literal null}, signaling there is no rewriting. + * + * @param queryRewriter class definition to find in the context + * @return {@literal null} since this doesn't actually rewrite anything. + */ + @Override + public QueryRewriter extractQueryRewriterBean(Class queryRewriter) { + + LOGGER.warn("You have NOT configured JpaRepositoryFactory with a QueryRewriterProvider!"); + + return null; + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java new file mode 100644 index 0000000000..ba8071e5cf --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java @@ -0,0 +1,71 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.function.Supplier; + +import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.lang.Nullable; + +/** + * Provide a {@link QueryRewriter} based upon the {@link JpaQueryMethod} and the surrounding context (Spring, CDI, etc.) + * + * @author Greg Turnquist + * @since 3.0 + */ +public abstract class QueryRewriterProvider { + + /** + * Using a {@link JpaQueryMethod}, extract a potential {@link QueryRewriter}. Wrap all this in a {@link Supplier} to + * defer the lookup until needed. + * + * @param method - JpaQueryMethod + * @return a {@link Supplier}-wrapped callback to fetch the {@link QueryRewriter} + */ + public Supplier of(JpaQueryMethod method) { + return () -> findQueryRewriter(method); + } + + /** + * Using the {@link org.springframework.data.jpa.repository.QueryRewrite} annotation, look for a {@link QueryRewriter} + * and instantiate one. NOTE: If its {@link QueryRewriter.NoopQueryRewriter}, it will just return {@literal null} and + * NOT do any rewrite operations. + * + * @param method - {@link JpaQueryMethod} that has the annotation details + * @return a {@link QueryRewriter for the method or {@code null} + */ + @Nullable + private QueryRewriter findQueryRewriter(JpaQueryMethod method) { + + Class queryRewriter = method.getQueryRewriter(); + + if (queryRewriter == null || queryRewriter == QueryRewriter.NoopQueryRewriter.class) { + return null; + } + + return extractQueryRewriterBean(queryRewriter); + } + + /** + * Extract an instance of {@link QueryRewriter} from the context. Implementations choose what context means, whether + * that is Spring, CDI, or whatever. + * + * @param queryRewriter + * @return a Java bean that implements {@link QueryRewriter}. {@literal null} is valid if no bean is found. + */ + @Nullable + protected abstract QueryRewriter extractQueryRewriterBean(Class queryRewriter); +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index cd0b4f228f..6e48f10bd0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -31,6 +31,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Mark Paluch + * @author Greg Turnquist */ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { @@ -44,8 +45,10 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { * @param parser must not be {@literal null} */ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { - this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, evaluationContextProvider, parser); + QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, + QueryRewriterProvider queryRewriterProvider) { + this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, evaluationContextProvider, parser, + queryRewriterProvider); } /** @@ -59,9 +62,10 @@ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String * @param parser must not be {@literal null} */ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { + QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, + QueryRewriterProvider queryRewriterProvider) { - super(method, em, queryString, countQueryString, evaluationContextProvider, parser); + super(method, em, queryString, countQueryString, evaluationContextProvider, parser, queryRewriterProvider); validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s!", method); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 9b6f2471e1..367d0bf920 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -17,29 +17,24 @@ import static org.springframework.data.querydsl.QuerydslUtils.*; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; + import java.io.Serializable; import java.lang.reflect.Method; import java.util.Optional; import java.util.stream.Stream; -import jakarta.persistence.EntityManager; -import jakarta.persistence.Tuple; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.jpa.projection.CollectionAwareProjectionFactory; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.query.AbstractJpaQuery; -import org.springframework.data.jpa.repository.query.DefaultJpaQueryMethodFactory; -import org.springframework.data.jpa.repository.query.EscapeCharacter; -import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy; -import org.springframework.data.jpa.repository.query.JpaQueryMethod; -import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory; -import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.jpa.repository.query.*; import org.springframework.data.jpa.util.JpaMetamodel; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.querydsl.EntityPathResolver; @@ -82,6 +77,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { private EntityPathResolver entityPathResolver; private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT; private JpaQueryMethodFactory queryMethodFactory; + private QueryRewriterProvider queryRewriterProvider; /** * Creates a new {@link JpaRepositoryFactory}. @@ -98,6 +94,12 @@ public JpaRepositoryFactory(EntityManager entityManager) { this.entityPathResolver = SimpleEntityPathResolver.INSTANCE; this.queryMethodFactory = new DefaultJpaQueryMethodFactory(extractor); + /** + * Default to {@link QueryRewriterNoopProvider}. If there is a {@link BeanFactory} or {@link BeanManager}, this will + * result in later overriding this with the proper version. + */ + this.queryRewriterProvider = new QueryRewriterNoopProvider(); + addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor); addRepositoryProxyPostProcessor((factory, repositoryInformation) -> { @@ -118,6 +120,23 @@ public void setBeanClassLoader(ClassLoader classLoader) { this.crudMethodMetadataPostProcessor.setBeanClassLoader(classLoader); } + /** + * If a {@link BeanFactory} is being set, this is clearly in a Spring context, and so we can capture the + * {@link QueryRewriterProvider} being a {@link QueryRewriterBeanFactoryProvider}. + * + * @param beanFactory + * @throws BeansException + */ + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + + super.setBeanFactory(beanFactory); + + Assert.notNull(beanFactory, "BeanFactory must not be null!"); + + setQueryRewriterProvider(new QueryRewriterBeanFactoryProvider(beanFactory)); + } + /** * Configures the {@link EntityPathResolver} to be used. Defaults to {@link SimpleEntityPathResolver#INSTANCE}. * @@ -151,6 +170,17 @@ public void setQueryMethodFactory(JpaQueryMethodFactory queryMethodFactory) { this.queryMethodFactory = queryMethodFactory; } + /** + * Configures the {@link QueryRewriterProvider} to be used. Defaults to {@link QueryRewriterNoopProvider}. + * + * @param queryRewriterProvider must not be {@literal null} + */ + public void setQueryRewriterProvider(QueryRewriterProvider queryRewriterProvider) { + + Assert.notNull(queryRewriterProvider, "QueryRewriterProvider must not be null!"); + this.queryRewriterProvider = queryRewriterProvider; + } + @Override protected final JpaRepositoryImplementation getTargetRepository(RepositoryInformation information) { @@ -199,7 +229,7 @@ protected Optional getQueryLookupStrategy(@Nullable Key key QueryMethodEvaluationContextProvider evaluationContextProvider) { return Optional.of(JpaQueryLookupStrategy.create(entityManager, queryMethodFactory, key, evaluationContextProvider, - escapeCharacter)); + queryRewriterProvider, escapeCharacter)); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java index 5f0a69407c..55f1a30215 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java @@ -24,6 +24,7 @@ import jakarta.enterprise.inject.se.SeContainerInitializer; import jakarta.enterprise.inject.spi.Bean; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.apache.commons.logging.Log; @@ -43,7 +44,7 @@ class CdiExtensionIntegrationTests { private static Log LOGGER = LogFactory.getLog(CdiExtensionIntegrationTests.class); @BeforeAll - static void setUp() { + static void setUpCdi() { container = SeContainerInitializer.newInstance() // .disableDiscovery() // @@ -53,6 +54,11 @@ static void setUp() { LOGGER.debug("CDI container bootstrapped!"); } + @AfterAll + static void tearDownCdi() { + container.close(); + } + @Test // DATAJPA-319, DATAJPA-1180 @SuppressWarnings("rawtypes") void foo() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java new file mode 100644 index 0000000000..aae03b3743 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java @@ -0,0 +1,234 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.cdi; + +import static org.assertj.core.api.Assertions.*; + +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.se.SeContainer; +import jakarta.enterprise.inject.se.SeContainerInitializer; +import jakarta.enterprise.inject.spi.Bean; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.data.repository.cdi.Eager; + +/** + * Unit tests for repository with {@link Query} and {@link QueryRewrite} in a CDI environment. + * + * @author Greg Turnquist + */ +public class JpaQueryRewriterWithCdiIntegrationTests { + + private static SeContainer container; + private static Log LOGGER = LogFactory.getLog(CdiExtensionIntegrationTests.class); + + private UserRepositoryWithRewriter repository; + + // Results + static final String ORIGINAL_QUERY = "original query"; + static final String REWRITTEN_QUERY = "rewritten query"; + static final String SORT = "sort"; + static Map results = new HashMap<>(); + + @BeforeAll + static void setUpCdi() { + + container = SeContainerInitializer.newInstance() // + .disableDiscovery() // + .addPackages(UserRepositoryWithRewriter.class) // + .initialize(); + + LOGGER.debug("CDI container bootstrapped!"); + } + + @AfterAll + static void tearDownCdi() { + container.close(); + } + + @BeforeEach + void setUp() { + + Bean repositoryBean = container.getBeanManager().getBeans(UserRepositoryWithRewriter.class).iterator().next(); + CreationalContext context = container.getBeanManager().createCreationalContext(repositoryBean); + this.repository = (UserRepositoryWithRewriter) container.getBeanManager().getReference(repositoryBean, + UserRepositoryWithRewriter.class, context); + + results.clear(); + } + + @Test + void nativeQueryShouldHandleRewrites() throws NoSuchMethodException { + + repository.findByNativeQuery("Matthews"); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, "select original_user_alias.* from SD_USER original_user_alias"), // + entry(REWRITTEN_QUERY, "select rewritten_user_alias.* from SD_USER rewritten_user_alias"), // + entry(SORT, Sort.unsorted().toString())); + } + + @Test + void nonNativeQueryShouldHandleRewrites() throws NoSuchMethodException { + + repository.findByNonNativeQuery("Matthews"); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, "select original_user_alias from User original_user_alias"), // + entry(REWRITTEN_QUERY, "select rewritten_user_alias from User rewritten_user_alias"), // + entry(SORT, Sort.unsorted().toString())); + } + + @Test + void nonNativeQueryWithSortShouldHandleRewrites() throws NoSuchMethodException { + + repository.findByNonNativeSortedQuery("Matthews", Sort.by("lastname")); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, + "select original_user_alias from User original_user_alias order by original_user_alias.lastname asc"), // + entry(REWRITTEN_QUERY, + "select rewritten_user_alias from User rewritten_user_alias order by rewritten_user_alias.lastname asc"), // + entry(SORT, Sort.by("lastname").ascending().toString())); + + repository.findByNonNativeSortedQuery("Matthews", Sort.by("firstname").descending()); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, + "select original_user_alias from User original_user_alias order by original_user_alias.firstname desc"), // + entry(REWRITTEN_QUERY, + "select rewritten_user_alias from User rewritten_user_alias order by rewritten_user_alias.firstname desc"), // + entry(SORT, Sort.by("firstname").descending().toString())); + } + + @Test + void nonNativeQueryWithPageableShouldHandleRewrites() throws NoSuchMethodException { + + repository.findByNonNativePagedQuery("Matthews", PageRequest.of(2, 1)); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, "select original_user_alias from User original_user_alias"), // + entry(REWRITTEN_QUERY, "select rewritten_user_alias from User rewritten_user_alias"), // + entry(SORT, Sort.unsorted().toString())); + } + + @Test + void nativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethodException { + + repository.findByNativeQueryWithNoRewrite("Matthews"); + + assertThat(results).isEmpty(); + } + + @Test + void nonNativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethodException { + + repository.findByNonNativeQueryWithNoRewrite("Matthews"); + + assertThat(results).isEmpty(); + } + + @Test + void nativeQueryShouldHandleRewritesUsingRepositoryRewriter() throws NoSuchMethodException { + + repository.findByNativeQueryUsingRepository("Matthews"); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, "select original_user_alias.* from SD_USER original_user_alias"), // + entry(REWRITTEN_QUERY, "select rewritten_user_alias.* from SD_USER rewritten_user_alias"), // + entry(SORT, Sort.unsorted().toString())); + } + + /** + * {@link QueryRewriter} implemented by the repository. + */ + @Eager + public interface UserRepositoryWithRewriter extends JpaRepository, QueryRewriter { + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", nativeQuery = true, + queryRewriter = TestQueryRewriter.class) + List findByNativeQuery(String param); + + @Query(value = "select original_user_alias from User original_user_alias", queryRewriter = TestQueryRewriter.class) + List findByNonNativeQuery(String param); + + @Query(value = "select original_user_alias from User original_user_alias", queryRewriter = TestQueryRewriter.class) + List findByNonNativeSortedQuery(String param, Sort sort); + + @Query(value = "select original_user_alias from User original_user_alias", queryRewriter = TestQueryRewriter.class) + List findByNonNativePagedQuery(String param, Pageable pageable); + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", nativeQuery = true) + List findByNativeQueryWithNoRewrite(String param); + + @Query(value = "select original_user_alias from User original_user_alias") + List findByNonNativeQueryWithNoRewrite(String param); + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", nativeQuery = true, + queryRewriter = UserRepositoryWithRewriter.class) + List findByNativeQueryUsingRepository(String param); + + @Override + default String rewrite(String query, Sort sort) { + return replaceAlias(query, sort); + } + } + + /** + * Stand-alone {@link QueryRewriter}. + */ + static class TestQueryRewriter implements QueryRewriter { + + @Override + public String rewrite(String query, Sort sort) { + return replaceAlias(query, sort); + } + } + + /** + * One query rewriter function to rule them all! + * + * @param query + * @param sort + */ + private static String replaceAlias(String query, Sort sort) { + + String rewrittenQuery = query.replaceAll("original_user_alias", "rewritten_user_alias"); + + // Capture results for testing. + results.put(ORIGINAL_QUERY, query); + results.put(REWRITTEN_QUERY, rewrittenQuery); + results.put(SORT, sort.toString()); + + return rewrittenQuery; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index a7254bc961..09f7789b95 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -18,19 +18,22 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.lang.reflect.Method; -import java.util.Set; - import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.Tuple; +import java.lang.reflect.Method; +import java.util.Set; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; @@ -43,6 +46,7 @@ * Integration tests for {@link AbstractStringBasedJpaQuery}. * * @author Oliver Gierke + * @author Greg Turnquist * @soundtrack Henrik Freischlader Trio - Nobody Else To Blame (Openness) */ @ExtendWith(SpringExtension.class) @@ -51,6 +55,8 @@ public class AbstractStringBasedJpaQueryIntegrationTests { @PersistenceContext EntityManager em; + @Autowired BeanFactory beanFactory; + @Test // DATAJPA-885 void createsNormalQueryForJpaManagedReturnTypes() throws Exception { @@ -60,10 +66,12 @@ void createsNormalQueryForJpaManagedReturnTypes() throws Exception { when(mock.getMetamodel()).thenReturn(em.getMetamodel()); JpaQueryMethod method = getMethod("findRolesByEmailAddress", String.class); - AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, - null, QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser()); + AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, null, + QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser(), + new QueryRewriterBeanFactoryProvider(beanFactory)); - jpaQuery.createJpaQuery(method.getAnnotatedQuery(), method.getResultProcessor().getReturnedType()); + jpaQuery.createJpaQuery(method.getAnnotatedQuery(), Sort.unsorted(), null, + method.getResultProcessor().getReturnedType()); verify(mock, times(1)).createQuery(anyString()); verify(mock, times(0)).createQuery(anyString(), eq(Tuple.class)); @@ -80,7 +88,7 @@ private JpaQueryMethod getMethod(String name, Class... parameterTypes) throws interface SampleRepository extends Repository { - @org.springframework.data.jpa.repository.Query("select u.roles from User u where u.emailAddress = ?1") + @Query("select u.roles from User u where u.emailAddress = ?1") Set findRolesByEmailAddress(String emailAddress); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index 187df77ea7..ed71fc8269 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -19,13 +19,13 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.lang.reflect.Method; -import java.util.List; - import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.metamodel.Metamodel; +import java.lang.reflect.Method; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,7 +33,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - +import org.springframework.beans.factory.BeanFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -57,6 +57,7 @@ * @author Thomas Darimont * @author Jens Schauder * @author Réda Housni Alaoui + * @author Greg Turnquist */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -70,6 +71,7 @@ public class JpaQueryLookupStrategyUnitTests { @Mock NamedQueries namedQueries; @Mock Metamodel metamodel; @Mock ProjectionFactory projectionFactory; + @Mock BeanFactory beanFactory; private JpaQueryMethodFactory queryMethodFactory; @@ -87,7 +89,7 @@ void setUp() { void invalidAnnotatedQueryCausesException() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("findByFoo", String.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); @@ -103,7 +105,7 @@ void invalidAnnotatedQueryCausesException() throws Exception { void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("findByInvalidNativeQuery", String.class, Sort.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); @@ -117,7 +119,7 @@ void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() thro void considersNamedCountQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); when(namedQueries.hasQuery("foo.count")).thenReturn(true); when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); @@ -139,7 +141,7 @@ void considersNamedCountQuery() throws Exception { void considersNamedCountOnStringQueryQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); when(namedQueries.hasQuery("foo.count")).thenReturn(true); when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); @@ -158,7 +160,7 @@ void considersNamedCountOnStringQueryQuery() throws Exception { void prefersDeclaredQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("annotatedQueryWithQueryAndQueryName"); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java new file mode 100644 index 0000000000..f11d645fc9 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java @@ -0,0 +1,218 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.ImportResource; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Unit tests for repository with {@link Query} and {@link QueryRewrite}. + * + * @author Greg Turnquist + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration +public class JpaQueryRewriteIntegrationTests { + + @Autowired private UserRepositoryWithRewriter repository; + + // Results + static final String ORIGINAL_QUERY = "original query"; + static final String REWRITTEN_QUERY = "rewritten query"; + static final String SORT = "sort"; + static Map results = new HashMap<>(); + + @BeforeEach + void setUp() { + results.clear(); + } + + @Test + void nativeQueryShouldHandleRewrites() throws NoSuchMethodException { + + repository.findByNativeQuery("Matthews"); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, "select original_user_alias.* from SD_USER original_user_alias"), // + entry(REWRITTEN_QUERY, "select rewritten_user_alias.* from SD_USER rewritten_user_alias"), // + entry(SORT, Sort.unsorted().toString())); + } + + @Test + void nonNativeQueryShouldHandleRewrites() throws NoSuchMethodException { + + repository.findByNonNativeQuery("Matthews"); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, "select original_user_alias from User original_user_alias"), // + entry(REWRITTEN_QUERY, "select rewritten_user_alias from User rewritten_user_alias"), // + entry(SORT, Sort.unsorted().toString())); + } + + @Test + void nonNativeQueryWithSortShouldHandleRewrites() throws NoSuchMethodException { + + repository.findByNonNativeSortedQuery("Matthews", Sort.by("lastname")); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, + "select original_user_alias from User original_user_alias order by original_user_alias.lastname asc"), // + entry(REWRITTEN_QUERY, + "select rewritten_user_alias from User rewritten_user_alias order by rewritten_user_alias.lastname asc"), // + entry(SORT, Sort.by("lastname").ascending().toString())); + + repository.findByNonNativeSortedQuery("Matthews", Sort.by("firstname").descending()); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, + "select original_user_alias from User original_user_alias order by original_user_alias.firstname desc"), // + entry(REWRITTEN_QUERY, + "select rewritten_user_alias from User rewritten_user_alias order by rewritten_user_alias.firstname desc"), // + entry(SORT, Sort.by("firstname").descending().toString())); + } + + @Test + void nonNativeQueryWithPageableShouldHandleRewrites() throws NoSuchMethodException { + + repository.findByNonNativePagedQuery("Matthews", PageRequest.of(2, 1)); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, "select original_user_alias from User original_user_alias"), // + entry(REWRITTEN_QUERY, "select rewritten_user_alias from User rewritten_user_alias"), // + entry(SORT, Sort.unsorted().toString())); + } + + @Test + void nativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethodException { + + repository.findByNativeQueryWithNoRewrite("Matthews"); + + assertThat(results).isEmpty(); + } + + @Test + void nonNativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethodException { + + repository.findByNonNativeQueryWithNoRewrite("Matthews"); + + assertThat(results).isEmpty(); + } + + @Test + void nativeQueryShouldHandleRewritesUsingRepositoryRewriter() throws NoSuchMethodException { + + repository.findByNativeQueryUsingRepository("Matthews"); + + assertThat(results).containsExactly( // + entry(ORIGINAL_QUERY, "select original_user_alias.* from SD_USER original_user_alias"), // + entry(REWRITTEN_QUERY, "select rewritten_user_alias.* from SD_USER rewritten_user_alias"), // + entry(SORT, Sort.unsorted().toString())); + } + + public interface UserRepositoryWithRewriter extends JpaRepository, QueryRewriter { + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", nativeQuery = true, + queryRewriter = TestQueryRewriter.class) + List findByNativeQuery(String param); + + @Query(value = "select original_user_alias from User original_user_alias", queryRewriter = TestQueryRewriter.class) + List findByNonNativeQuery(String param); + + @Query(value = "select original_user_alias from User original_user_alias", queryRewriter = TestQueryRewriter.class) + List findByNonNativeSortedQuery(String param, Sort sort); + + @Query(value = "select original_user_alias from User original_user_alias", queryRewriter = TestQueryRewriter.class) + List findByNonNativePagedQuery(String param, Pageable pageable); + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", nativeQuery = true) + List findByNativeQueryWithNoRewrite(String param); + + @Query(value = "select original_user_alias from User original_user_alias") + List findByNonNativeQueryWithNoRewrite(String param); + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", nativeQuery = true, + queryRewriter = UserRepositoryWithRewriter.class) + List findByNativeQueryUsingRepository(String param); + + @Override + default String rewrite(String query, Sort sort) { + return replaceAlias(query, sort); + } + } + + static class TestQueryRewriter implements QueryRewriter { + + @Override + public String rewrite(String query, Sort sort) { + return replaceAlias(query, sort); + } + } + + /** + * One query rewriter function to rule them all! + * + * @param query + * @param sort + */ + private static String replaceAlias(String query, Sort sort) { + + String rewrittenQuery = query.replaceAll("original_user_alias", "rewritten_user_alias"); + + // Capture results for testing. + results.put(ORIGINAL_QUERY, query); + results.put(REWRITTEN_QUERY, rewrittenQuery); + results.put(SORT, sort.toString()); + + return rewrittenQuery; + } + + @Configuration + @ImportResource("classpath:infrastructure.xml") + @EnableJpaRepositories(considerNestedRepositories = true, basePackageClasses = UserRepositoryWithRewriter.class, // + includeFilters = @ComponentScan.Filter(value = { UserRepositoryWithRewriter.class }, + type = FilterType.ASSIGNABLE_TYPE)) + static class JpaRepositoryConfig { + + @Bean + QueryRewriter queryRewriter() { + return new TestQueryRewriter(); + } + + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 077b0474f5..d394bfc5c9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -19,16 +19,16 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.List; - import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Tuple; import jakarta.persistence.TypedQuery; import jakarta.persistence.metamodel.Metamodel; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -37,7 +37,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - +import org.springframework.beans.factory.BeanFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -62,6 +62,7 @@ * @author Jens Schauder * @author Tom Hombergs * @author Mark Paluch + * @author Greg Turnquist */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -81,6 +82,7 @@ class SimpleJpaQueryUnitTests { @Mock RepositoryMetadata metadata; @Mock ParameterBinder binder; @Mock Metamodel metamodel; + @Mock BeanFactory beanFactory; private ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); @@ -107,12 +109,12 @@ void setUp() throws SecurityException, NoSuchMethodException { void prefersDeclaredCountQueryOverCreatingOne() throws Exception { method = new JpaQueryMethod( - SimpleJpaQueryUnitTests.class.getDeclaredMethod("prefersDeclaredCountQueryOverCreatingOne"), - metadata, factory, extractor); + SimpleJpaQueryUnitTests.class.getDeclaredMethod("prefersDeclaredCountQueryOverCreatingOne"), metadata, factory, + extractor); when(em.createQuery("foo", Long.class)).thenReturn(typedQuery); SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", null, EVALUATION_CONTEXT_PROVIDER, - PARSER); + PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); assertThat(jpaQuery.createCountQuery(new JpaParametersParameterAccessor(method.getParameters(), new Object[] {}))) .isEqualTo(typedQuery); @@ -127,8 +129,7 @@ void doesNotApplyPaginationToCountQuery() throws Exception { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", null, - EVALUATION_CONTEXT_PROVIDER, - PARSER); + EVALUATION_CONTEXT_PROVIDER, PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -143,7 +144,7 @@ void discoversNativeQuery() throws Exception { Method method = SampleRepository.class.getMethod("findNativeByLastname", String.class); JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, - queryMethod.getAnnotatedQuery(), null, EVALUATION_CONTEXT_PROVIDER); + queryMethod.getAnnotatedQuery(), null, EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory)); assertThat(jpaQuery instanceof NativeJpaQuery).isTrue(); @@ -246,8 +247,7 @@ void resolvesExpressionInCountQuery() throws Exception { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", - "select count(u.id) from #{#entityName} u", EVALUATION_CONTEXT_PROVIDER, - PARSER); + "select count(u.id) from #{#entityName} u", EVALUATION_CONTEXT_PROVIDER, PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -259,7 +259,7 @@ private AbstractJpaQuery createJpaQuery(Method method) { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, queryMethod.getAnnotatedQuery(), null, - EVALUATION_CONTEXT_PROVIDER); + EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory)); } interface SampleRepository { diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 7fd3d8bfa5..664f72e612 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -293,7 +293,7 @@ public class User { [[jpa.query-methods.named-queries.declaring-interfaces]] ==== Declaring Interfaces -To allow these named queries, specify the `UserRepository` as follows: +To allow these named queries, specify the `UserRepositoryWithRewriter` as follows: .Query method declaration in UserRepository ==== From 6c94e0e4527ee50298dd15a499063efb2b3b18d0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 29 Apr 2022 13:39:29 +0200 Subject: [PATCH 176/821] Polishing. Eagerly resolve QueryRewriter instances when creating JPA query objects. Tweak documentation wording. Tweak type names to align with naming scheme. See #2162. --- .../data/jpa/repository/Query.java | 4 +- .../data/jpa/repository/QueryRewriter.java | 42 +++++++++----- ... => BeanManagerQueryRewriterProvider.java} | 43 +++++++++----- .../jpa/repository/cdi/JpaRepositoryBean.java | 2 +- .../query/AbstractStringBasedJpaQuery.java | 23 +++----- ... => BeanFactoryQueryRewriterProvider.java} | 30 +++++----- .../query/DelegatingQueryRewriter.java | 48 +++++++++++++++ .../jpa/repository/query/JpaQueryFactory.java | 15 ++--- .../query/JpaQueryLookupStrategy.java | 30 +++++----- .../jpa/repository/query/JpaQueryMethod.java | 9 ++- .../jpa/repository/query/NativeJpaQuery.java | 9 +-- .../query/QueryRewriterNoopProvider.java | 45 -------------- .../query/QueryRewriterProvider.java | 58 ++++++++----------- .../jpa/repository/query/SimpleJpaQuery.java | 16 ++--- .../support/JpaRepositoryFactory.java | 33 ++++++----- ...aQueryRewriterWithCdiIntegrationTests.java | 4 +- ...ctStringBasedJpaQueryIntegrationTests.java | 6 +- .../JpaQueryLookupStrategyUnitTests.java | 10 ++-- .../JpaQueryRewriteIntegrationTests.java | 18 +++--- .../query/SimpleJpaQueryUnitTests.java | 18 +++--- 20 files changed, 244 insertions(+), 219 deletions(-) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/{QueryRewriterBeanManagerProvider.java => BeanManagerQueryRewriterProvider.java} (50%) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/{QueryRewriterBeanFactoryProvider.java => BeanFactoryQueryRewriterProvider.java} (53%) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java delete mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java index 614bee69cb..6a5aadaf18 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java @@ -80,10 +80,10 @@ String countName() default ""; /** - * Define the {@link QueryRewriter} bean that should be applied to this query after the query is full assembled. + * Define a {@link QueryRewriter} that should be applied to the query string after the query is fully assembled. * * @return * @since 3.0 */ - Class queryRewriter() default QueryRewriter.NoopQueryRewriter.class; + Class queryRewriter() default QueryRewriter.IdentityQueryRewriter.class; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java index d80a998c23..426a08df6c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,33 +19,41 @@ import org.springframework.data.domain.Sort; /** - * Callback to rewrite a query right before it's handed to the EntityManager. + * Callback to rewrite a query and apply sorting and pagination settings that cannot be applied based on a regularly + * detectable scheme. + *

+ * The underlying the query is the one right before it is used for query object creation, so everything that Spring Data + * and tools intends to do has been done. You can customize the query to apply final changes. Rewriting can only make + * use of already existing contextual data. That is, adding or replacing query text or reuse of bound parameters. Query + * rewriting must not add additional bindable parameters as these cannot be materialized. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.0 + * @see jakarta.persistence.EntityManager#createQuery + * @see jakarta.persistence.EntityManager#createNativeQuery */ @FunctionalInterface public interface QueryRewriter { /** - * The assembled query and current {@link Sort} settings are offered. This is the query right before it's handed to - * the EntityManager, so everything that Spring Data and tools intends to do has been done. The user is able to make - * any last minute changes.
- *
+ * Rewrite the assembled query with the given {@link Sort}. + *

* WARNING: No checks are performed before the transformed query is passed to the EntityManager. - * - * @param query - the assembled generated query, right before it's handed over to the EntityManager. - * @param sort - current {@link Sort} settings provided by the method, or {@link Sort#unsorted()}} if there are none. - * @return alter the query however you like. + * + * @param query the assembled query. + * @param sort current {@link Sort} settings provided by the method, or {@link Sort#unsorted()}} if there are none. + * @return the query to be used with the {@code EntityManager}. */ String rewrite(String query, Sort sort); /** - * This alternative is used to handle {@link Pageable}-based methods. - * - * @param query - the assembled generated query, right before it's handed over to the EntityManager. - * @param pageRequest - * @return + * Rewrite the assembled query with the given {@link Pageable}. + * + * @param query the assembled query. + * @param pageRequest current {@link Pageable} settings provided by the method, or {@link Pageable#unpaged()} if not + * paged. + * @return the query to be used with the {@code EntityManager}. */ default String rewrite(String query, Pageable pageRequest) { return rewrite(query, pageRequest.getSort()); @@ -54,7 +62,9 @@ default String rewrite(String query, Pageable pageRequest) { /** * A {@link QueryRewriter} that doesn't change the query. */ - public class NoopQueryRewriter implements QueryRewriter { + enum IdentityQueryRewriter implements QueryRewriter { + + INSTANCE; @Override public String rewrite(String query, Sort sort) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java similarity index 50% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java index 8a4369da82..d83a578b01 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/QueryRewriterBeanManagerProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,37 +19,52 @@ import jakarta.enterprise.inject.spi.Bean; import jakarta.enterprise.inject.spi.BeanManager; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import java.util.Iterator; + +import org.springframework.beans.BeanUtils; import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.data.jpa.repository.query.DelegatingQueryRewriter; +import org.springframework.data.jpa.repository.query.JpaQueryMethod; import org.springframework.data.jpa.repository.query.QueryRewriterProvider; +import org.springframework.data.util.Lazy; /** * A {@link BeanManager}-based {@link QueryRewriterProvider}. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.0 */ -public class QueryRewriterBeanManagerProvider extends QueryRewriterProvider { - - private static final Log LOGGER = LogFactory.getLog(QueryRewriterBeanManagerProvider.class); +public class BeanManagerQueryRewriterProvider implements QueryRewriterProvider { private final BeanManager beanManager; - public QueryRewriterBeanManagerProvider(BeanManager beanManager) { + public BeanManagerQueryRewriterProvider(BeanManager beanManager) { this.beanManager = beanManager; } @Override - protected QueryRewriter extractQueryRewriterBean(Class queryRewriter) { + @SuppressWarnings("unchecked") + public QueryRewriter getQueryRewriter(JpaQueryMethod method) { + + Class queryRewriter = method.getQueryRewriter(); + if (queryRewriter == QueryRewriter.IdentityQueryRewriter.class) { + return QueryRewriter.IdentityQueryRewriter.INSTANCE; + } + + Iterator> iterator = beanManager.getBeans(queryRewriter).iterator(); - try { - Bean bean = (Bean) beanManager.getBeans(queryRewriter).iterator().next(); + if (iterator.hasNext()) { + + Bean bean = (Bean) iterator.next(); CreationalContext context = beanManager.createCreationalContext(bean); - return (QueryRewriter) beanManager.getReference(bean, queryRewriter, context); - } catch (Exception e) { - LOGGER.error(e.toString()); - return null; + Lazy rewriter = Lazy + .of(() -> (QueryRewriter) beanManager.getReference(bean, queryRewriter, context)); + + return new DelegatingQueryRewriter(rewriter); } + + return BeanUtils.instantiateClass(queryRewriter); } + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index 3f9b522d24..01412b744b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -62,7 +62,7 @@ class JpaRepositoryBean extends CdiRepositoryBean { Assert.notNull(entityManagerBean, "EntityManager bean must not be null!"); this.entityManagerBean = entityManagerBean; - this.queryRewriterProvider = new QueryRewriterBeanManagerProvider(beanManager); + this.queryRewriterProvider = new BeanManagerQueryRewriterProvider(beanManager); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 6f4eb5bdb2..20611985b0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -18,10 +18,9 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; -import java.util.function.Supplier; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.QueryRewriter; @@ -46,14 +45,12 @@ */ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { - private static final Log LOGGER = LogFactory.getLog(AbstractStringBasedJpaQuery.class); - private final DeclaredQuery query; private final DeclaredQuery countQuery; private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final SpelExpressionParser parser; private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache(); - private final Supplier queryRewriterSupplier; + private final QueryRewriter queryRewriter; /** * Creates a new {@link AbstractStringBasedJpaQuery} from the given {@link JpaQueryMethod}, {@link EntityManager} and @@ -65,16 +62,18 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { * @param countQueryString must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}. * @param parser must not be {@literal null}. + * @param queryRewriter must not be {@literal null}. */ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, - @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, - SpelExpressionParser parser, QueryRewriterProvider queryRewriterProvider) { + @Nullable String countQueryString, QueryRewriter queryRewriter, QueryMethodEvaluationContextProvider evaluationContextProvider, + SpelExpressionParser parser) { super(method, em); Assert.hasText(queryString, "Query string must not be null or empty!"); Assert.notNull(evaluationContextProvider, "ExpressionEvaluationContextProvider must not be null!"); Assert.notNull(parser, "Parser must not be null!"); + Assert.notNull(queryRewriter, "QueryRewriter must not be null!"); this.evaluationContextProvider = evaluationContextProvider; this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser, @@ -85,7 +84,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri method.isNativeQuery()); this.parser = parser; - this.queryRewriterSupplier = queryRewriterProvider.of(method); + this.queryRewriter = queryRewriter; Assert.isTrue(method.isNativeQuery() || !query.usesJdbcStyleParameters(), "JDBC style parameters (?) are not supported for JPA queries."); @@ -169,7 +168,7 @@ protected Query createJpaQuery(String queryString, Sort sort, @Nullable Pageable /** * Use the {@link QueryRewriter}, potentially rewrite the query, using relevant {@link Sort} and {@link Pageable} * information. - * + * * @param originalQuery * @param sort * @param pageable @@ -177,12 +176,6 @@ protected Query createJpaQuery(String queryString, Sort sort, @Nullable Pageable */ protected String potentiallyRewriteQuery(String originalQuery, Sort sort, @Nullable Pageable pageable) { - QueryRewriter queryRewriter = this.queryRewriterSupplier.get(); - - if (queryRewriter == null) { - return originalQuery; - } - return pageable != null && pageable.isPaged() // ? queryRewriter.rewrite(originalQuery, pageable) // : queryRewriter.rewrite(originalQuery, sort); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java similarity index 53% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java index 102dc6c942..bf34780a42 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterBeanFactoryProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,36 +15,38 @@ */ package org.springframework.data.jpa.repository.query; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.data.util.Lazy; /** * A {@link BeanFactory}-based {@link QueryRewriterProvider}. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.0 */ -public class QueryRewriterBeanFactoryProvider extends QueryRewriterProvider { - - private static final Log LOGGER = LogFactory.getLog(QueryRewriterBeanFactoryProvider.class); +public class BeanFactoryQueryRewriterProvider implements QueryRewriterProvider { private final BeanFactory beanFactory; - public QueryRewriterBeanFactoryProvider(BeanFactory beanFactory) { + public BeanFactoryQueryRewriterProvider(BeanFactory beanFactory) { this.beanFactory = beanFactory; } @Override - protected QueryRewriter extractQueryRewriterBean(Class queryRewriter) { + @SuppressWarnings("unchecked") + public QueryRewriter getQueryRewriter(JpaQueryMethod method) { - try { - return beanFactory.getBean(queryRewriter); - } catch (BeansException e) { - LOGGER.error(e.toString()); - return null; + Class queryRewriter = method.getQueryRewriter(); + if (queryRewriter == QueryRewriter.IdentityQueryRewriter.class) { + return QueryRewriter.IdentityQueryRewriter.INSTANCE; } + + Lazy rewriter = Lazy.of(() -> beanFactory.getBeanProvider((Class) queryRewriter) + .getIfAvailable(() -> BeanUtils.instantiateClass(queryRewriter))); + + return new DelegatingQueryRewriter(rewriter); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java new file mode 100644 index 0000000000..27aec0f9ab --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java @@ -0,0 +1,48 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.function.Supplier; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.QueryRewriter; + +/** + * Delegating {@link QueryRewriter} that delegates rewrite calls to a {@link QueryRewriter delegate} provided by a + * {@link Supplier}. + * + * @author Mark Paluch + * @since 3.0 + */ +public class DelegatingQueryRewriter implements QueryRewriter { + + private final Supplier delegate; + + public DelegatingQueryRewriter(Supplier delegate) { + this.delegate = delegate; + } + + @Override + public String rewrite(String query, Sort sort) { + return delegate.get().rewrite(query, sort); + } + + @Override + public String rewrite(String query, Pageable pageRequest) { + return delegate.get().rewrite(query, pageRequest); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 83df010e50..63b0bffe60 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -17,6 +17,7 @@ import jakarta.persistence.EntityManager; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -39,20 +40,20 @@ enum JpaQueryFactory { * * @param method must not be {@literal null}. * @param em must not be {@literal null}. - * @param queryString must not be {@literal null} or empty. * @param countQueryString + * @param queryString must not be {@literal null}. * @param evaluationContextProvider * @return */ AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, String queryString, - @Nullable String countQueryString, QueryMethodEvaluationContextProvider evaluationContextProvider, - QueryRewriterProvider queryRewriterProvider) { + @Nullable String countQueryString, QueryRewriter queryRewriter, + QueryMethodEvaluationContextProvider evaluationContextProvider) { return method.isNativeQuery() - ? new NativeJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER, - queryRewriterProvider) - : new SimpleJpaQuery(method, em, queryString, countQueryString, evaluationContextProvider, PARSER, - queryRewriterProvider); + ? new NativeJpaQuery(method, em, queryString, countQueryString, queryRewriter, evaluationContextProvider, + PARSER) + : new SimpleJpaQuery(method, em, queryString, countQueryString, queryRewriter, evaluationContextProvider, + PARSER); } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 552c587afd..5fba2b6a47 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -22,6 +22,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryMetadata; @@ -83,14 +84,13 @@ public AbstractQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory query @Override public final RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { - return resolveQuery(queryMethodFactory.build(method, metadata, factory), em, namedQueries); + JpaQueryMethod queryMethod = queryMethodFactory.build(method, metadata, factory); + return resolveQuery(queryMethod, queryRewriterProvider.getQueryRewriter(queryMethod), em, namedQueries); } - protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries); + protected abstract RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, + EntityManager em, NamedQueries namedQueries); - protected QueryRewriterProvider getQueryRewriterSupplier() { - return queryRewriterProvider; - } } /** @@ -112,7 +112,8 @@ public CreateQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMe } @Override - protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { + protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em, + NamedQueries namedQueries) { return new PartTreeJpaQuery(method, em, escape); } } @@ -145,7 +146,8 @@ public DeclaredQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory query } @Override - protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { + protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em, + NamedQueries namedQueries) { if (method.isProcedureQuery()) { return JpaQueryFactory.INSTANCE.fromProcedureAnnotation(method, em); @@ -159,13 +161,13 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, } return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getRequiredAnnotatedQuery(), - getCountQuery(method, namedQueries, em), evaluationContextProvider, getQueryRewriterSupplier()); + getCountQuery(method, namedQueries, em), queryRewriter, evaluationContextProvider); } String name = method.getNamedQueryName(); if (namedQueries.hasQuery(name)) { return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, namedQueries.getQuery(name), - getCountQuery(method, namedQueries, em), evaluationContextProvider, getQueryRewriterSupplier()); + getCountQuery(method, namedQueries, em), queryRewriter, evaluationContextProvider); } RepositoryQuery query = NamedQuery.lookupFrom(method, em); @@ -240,12 +242,13 @@ public CreateIfNotFoundQueryLookupStrategy(EntityManager em, JpaQueryMethodFacto } @Override - protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) { + protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em, + NamedQueries namedQueries) { try { - return lookupStrategy.resolveQuery(method, em, namedQueries); + return lookupStrategy.resolveQuery(method, queryRewriter, em, namedQueries); } catch (IllegalStateException e) { - return createStrategy.resolveQuery(method, em, namedQueries); + return createStrategy.resolveQuery(method, queryRewriter, em, namedQueries); } } } @@ -270,7 +273,8 @@ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory case CREATE: return new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape); case USE_DECLARED_QUERY: - return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, queryRewriterProvider); + return new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, + queryRewriterProvider); case CREATE_IF_NOT_FOUND: return new CreateIfNotFoundQueryLookupStrategy(em, queryMethodFactory, new CreateQueryLookupStrategy(em, queryMethodFactory, queryRewriterProvider, escape), diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index 88e0855277..e3138c5bd7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -266,7 +266,7 @@ Class getReturnType() { * @return */ @Nullable - String getAnnotatedQuery() { + public String getAnnotatedQuery() { String query = getAnnotationValue("value", String.class); return StringUtils.hasText(query) ? query : null; @@ -287,7 +287,7 @@ boolean hasAnnotatedQueryName() { * @throws IllegalStateException if no {@link Query} annotation is present or the query is empty. * @since 2.0 */ - String getRequiredAnnotatedQuery() throws IllegalStateException { + public String getRequiredAnnotatedQuery() throws IllegalStateException { String query = getAnnotatedQuery(); @@ -305,7 +305,7 @@ String getRequiredAnnotatedQuery() throws IllegalStateException { * @return */ @Nullable - String getCountQuery() { + public String getCountQuery() { String countQuery = getAnnotationValue("countQuery", String.class); return StringUtils.hasText(countQuery) ? countQuery : null; @@ -438,8 +438,7 @@ StoredProcedureAttributes getProcedureAttributes() { * @return type of the {@link QueryRewriter} * @since 3.0 */ - @Nullable - Class getQueryRewriter() { + public Class getQueryRewriter() { return getMergedOrDefaultAnnotationValue("queryRewriter", Query.class, Class.class); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index c517ff0ae9..205e7d1f23 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -21,6 +21,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; @@ -48,13 +49,13 @@ final class NativeJpaQuery extends AbstractStringBasedJpaQuery { * @param em must not be {@literal null}. * @param queryString must not be {@literal null} or empty. * @param countQueryString must not be {@literal null} or empty. - * @param evaluationContextProvider + * @param rewriter the query rewriter to use. */ public NativeJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, - QueryRewriterProvider queryRewriterProvider) { + QueryRewriter rewriter, QueryMethodEvaluationContextProvider evaluationContextProvider, + SpelExpressionParser parser) { - super(method, em, queryString, countQueryString, evaluationContextProvider, parser, queryRewriterProvider); + super(method, em, queryString, countQueryString, rewriter, evaluationContextProvider, parser); Parameters parameters = method.getParameters(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java deleted file mode 100644 index 094e377cd6..0000000000 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterNoopProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2008-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.query; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.data.jpa.repository.QueryRewriter; - -/** - * {@link QueryRewriterProvider} that does nothing. - * - * @author Greg Turnquist - * @since 3.0 - */ -public class QueryRewriterNoopProvider extends QueryRewriterProvider { - - private static final Log LOGGER = LogFactory.getLog(QueryRewriterNoopProvider.class); - - /** - * Returns {@literal null}, signaling there is no rewriting. - * - * @param queryRewriter class definition to find in the context - * @return {@literal null} since this doesn't actually rewrite anything. - */ - @Override - public QueryRewriter extractQueryRewriterBean(Class queryRewriter) { - - LOGGER.warn("You have NOT configured JpaRepositoryFactory with a QueryRewriterProvider!"); - - return null; - } -} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java index ba8071e5cf..ed955d9d51 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,57 +15,45 @@ */ package org.springframework.data.jpa.repository.query; -import java.util.function.Supplier; - +import org.springframework.beans.BeanUtils; import org.springframework.data.jpa.repository.QueryRewriter; -import org.springframework.lang.Nullable; /** - * Provide a {@link QueryRewriter} based upon the {@link JpaQueryMethod} and the surrounding context (Spring, CDI, etc.) + * Provide a {@link QueryRewriter} based upon the {@link JpaQueryMethod}. {@code QueryRewriter} instances may be + * contextual or plain objects that are not attached to a bean factory or CDI context. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.0 + * @see QueryRewriter */ -public abstract class QueryRewriterProvider { - - /** - * Using a {@link JpaQueryMethod}, extract a potential {@link QueryRewriter}. Wrap all this in a {@link Supplier} to - * defer the lookup until needed. - * - * @param method - JpaQueryMethod - * @return a {@link Supplier}-wrapped callback to fetch the {@link QueryRewriter} - */ - public Supplier of(JpaQueryMethod method) { - return () -> findQueryRewriter(method); - } +public interface QueryRewriterProvider { /** - * Using the {@link org.springframework.data.jpa.repository.QueryRewrite} annotation, look for a {@link QueryRewriter} - * and instantiate one. NOTE: If its {@link QueryRewriter.NoopQueryRewriter}, it will just return {@literal null} and - * NOT do any rewrite operations. + * Return a simple {@code QueryRewriterProvider} that uses + * {@link org.springframework.beans.BeanUtils#instantiateClass(Class)} to obtain a {@link QueryRewriter} instance. * - * @param method - {@link JpaQueryMethod} that has the annotation details - * @return a {@link QueryRewriter for the method or {@code null} + * @return a simple {@link QueryRewriterProvider}. */ - @Nullable - private QueryRewriter findQueryRewriter(JpaQueryMethod method) { + static QueryRewriterProvider simple() { + + return method -> { - Class queryRewriter = method.getQueryRewriter(); + Class queryRewriter = method.getQueryRewriter(); - if (queryRewriter == null || queryRewriter == QueryRewriter.NoopQueryRewriter.class) { - return null; - } + if (queryRewriter == QueryRewriter.IdentityQueryRewriter.class) { + return QueryRewriter.IdentityQueryRewriter.INSTANCE; + } - return extractQueryRewriterBean(queryRewriter); + return BeanUtils.instantiateClass(queryRewriter); + }; } /** - * Extract an instance of {@link QueryRewriter} from the context. Implementations choose what context means, whether - * that is Spring, CDI, or whatever. + * Obtain an instance of {@link QueryRewriter} for a {@link JpaQueryMethod}. * - * @param queryRewriter - * @return a Java bean that implements {@link QueryRewriter}. {@literal null} is valid if no bean is found. + * @param method the underlying JPA query method. + * @return a Java bean that implements {@link QueryRewriter}. */ - @Nullable - protected abstract QueryRewriter extractQueryRewriterBean(Class queryRewriter); + QueryRewriter getQueryRewriter(JpaQueryMethod method); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index 6e48f10bd0..1e765db9c3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -18,6 +18,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -41,14 +42,13 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { * @param method must not be {@literal null} * @param em must not be {@literal null} * @param countQueryString + * @param queryRewriter must not be {@literal null} * @param evaluationContextProvider must not be {@literal null} * @param parser must not be {@literal null} */ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, - QueryRewriterProvider queryRewriterProvider) { - this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, evaluationContextProvider, parser, - queryRewriterProvider); + QueryRewriter queryRewriter, QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { + this(method, em, method.getRequiredAnnotatedQuery(), countQueryString, queryRewriter, evaluationContextProvider, parser); } /** @@ -58,14 +58,14 @@ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, @Nullable String * @param em must not be {@literal null} * @param queryString must not be {@literal null} or empty * @param countQueryString + * @param queryRewriter * @param evaluationContextProvider must not be {@literal null} * @param parser must not be {@literal null} */ - public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, - QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser, - QueryRewriterProvider queryRewriterProvider) { + public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, @Nullable String countQueryString, QueryRewriter queryRewriter, + QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { - super(method, em, queryString, countQueryString, evaluationContextProvider, parser, queryRewriterProvider); + super(method, em, queryString, countQueryString, queryRewriter, evaluationContextProvider, parser); validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s!", method); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 367d0bf920..46f8624c7e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -27,6 +27,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.dao.InvalidDataAccessApiUsageException; @@ -34,7 +36,15 @@ import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.query.*; +import org.springframework.data.jpa.repository.query.AbstractJpaQuery; +import org.springframework.data.jpa.repository.query.BeanFactoryQueryRewriterProvider; +import org.springframework.data.jpa.repository.query.DefaultJpaQueryMethodFactory; +import org.springframework.data.jpa.repository.query.EscapeCharacter; +import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy; +import org.springframework.data.jpa.repository.query.JpaQueryMethod; +import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory; +import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.jpa.repository.query.QueryRewriterProvider; import org.springframework.data.jpa.util.JpaMetamodel; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.querydsl.EntityPathResolver; @@ -93,12 +103,7 @@ public JpaRepositoryFactory(EntityManager entityManager) { this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor(); this.entityPathResolver = SimpleEntityPathResolver.INSTANCE; this.queryMethodFactory = new DefaultJpaQueryMethodFactory(extractor); - - /** - * Default to {@link QueryRewriterNoopProvider}. If there is a {@link BeanFactory} or {@link BeanManager}, this will - * result in later overriding this with the proper version. - */ - this.queryRewriterProvider = new QueryRewriterNoopProvider(); + this.queryRewriterProvider = QueryRewriterProvider.simple(); addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor); addRepositoryProxyPostProcessor((factory, repositoryInformation) -> { @@ -122,8 +127,8 @@ public void setBeanClassLoader(ClassLoader classLoader) { /** * If a {@link BeanFactory} is being set, this is clearly in a Spring context, and so we can capture the - * {@link QueryRewriterProvider} being a {@link QueryRewriterBeanFactoryProvider}. - * + * {@link QueryRewriterProvider} being a {@link BeanFactoryQueryRewriterProvider}. + * * @param beanFactory * @throws BeansException */ @@ -134,7 +139,7 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException { Assert.notNull(beanFactory, "BeanFactory must not be null!"); - setQueryRewriterProvider(new QueryRewriterBeanFactoryProvider(beanFactory)); + setQueryRewriterProvider(new BeanFactoryQueryRewriterProvider(beanFactory)); } /** @@ -171,9 +176,11 @@ public void setQueryMethodFactory(JpaQueryMethodFactory queryMethodFactory) { } /** - * Configures the {@link QueryRewriterProvider} to be used. Defaults to {@link QueryRewriterNoopProvider}. + * Configures the {@link QueryRewriterProvider} to be used. Defaults to instantiate query rewriters through + * {@link BeanUtils#instantiateClass(Class)}. * - * @param queryRewriterProvider must not be {@literal null} + * @param queryRewriterProvider must not be {@literal null}. + * @since 3.0 */ public void setQueryRewriterProvider(QueryRewriterProvider queryRewriterProvider) { @@ -254,7 +261,7 @@ protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata * * @param metadata repository metadata. * @param entityManager the entity manager. - * @param resolver resolver to translate an plain domain class into a {@link EntityPath}. + * @param resolver resolver to translate a plain domain class into a {@link EntityPath}. * @param crudMethodMetadata metadata about the invoked CRUD methods. * @return * @since 2.5.1 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java index aae03b3743..3077542d71 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ /** * Unit tests for repository with {@link Query} and {@link QueryRewrite} in a CDI environment. - * + * * @author Greg Turnquist */ public class JpaQueryRewriterWithCdiIntegrationTests { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index 09f7789b95..33abbeebb9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -34,6 +34,7 @@ import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; @@ -66,9 +67,8 @@ void createsNormalQueryForJpaManagedReturnTypes() throws Exception { when(mock.getMetamodel()).thenReturn(em.getMetamodel()); JpaQueryMethod method = getMethod("findRolesByEmailAddress", String.class); - AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, null, - QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser(), - new QueryRewriterBeanFactoryProvider(beanFactory)); + AbstractStringBasedJpaQuery jpaQuery = new SimpleJpaQuery(method, mock, null, QueryRewriter.IdentityQueryRewriter.INSTANCE, + QueryMethodEvaluationContextProvider.DEFAULT, new SpelExpressionParser()); jpaQuery.createJpaQuery(method.getAnnotatedQuery(), Sort.unsorted(), null, method.getResultProcessor().getReturnedType()); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index ed71fc8269..0ba5d98e93 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -89,7 +89,7 @@ void setUp() { void invalidAnnotatedQueryCausesException() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("findByFoo", String.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); @@ -105,7 +105,7 @@ void invalidAnnotatedQueryCausesException() throws Exception { void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("findByInvalidNativeQuery", String.class, Sort.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); @@ -119,7 +119,7 @@ void sholdThrowMorePreciseExceptionIfTryingToUsePaginationInNativeQueries() thro void considersNamedCountQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); when(namedQueries.hasQuery("foo.count")).thenReturn(true); when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); @@ -141,7 +141,7 @@ void considersNamedCountQuery() throws Exception { void considersNamedCountOnStringQueryQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); when(namedQueries.hasQuery("foo.count")).thenReturn(true); when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); @@ -160,7 +160,7 @@ void considersNamedCountOnStringQueryQuery() throws Exception { void prefersDeclaredQuery() throws Exception { QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory), EscapeCharacter.DEFAULT); + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); Method method = UserRepository.class.getMethod("annotatedQueryWithQueryAndQueryName"); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java index f11d645fc9..61d0db240b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ /** * Unit tests for repository with {@link Query} and {@link QueryRewrite}. - * + * * @author Greg Turnquist */ @ExtendWith(SpringExtension.class) @@ -64,7 +64,7 @@ void setUp() { } @Test - void nativeQueryShouldHandleRewrites() throws NoSuchMethodException { + void nativeQueryShouldHandleRewrites() { repository.findByNativeQuery("Matthews"); @@ -75,7 +75,7 @@ void nativeQueryShouldHandleRewrites() throws NoSuchMethodException { } @Test - void nonNativeQueryShouldHandleRewrites() throws NoSuchMethodException { + void nonNativeQueryShouldHandleRewrites() { repository.findByNonNativeQuery("Matthews"); @@ -86,7 +86,7 @@ void nonNativeQueryShouldHandleRewrites() throws NoSuchMethodException { } @Test - void nonNativeQueryWithSortShouldHandleRewrites() throws NoSuchMethodException { + void nonNativeQueryWithSortShouldHandleRewrites() { repository.findByNonNativeSortedQuery("Matthews", Sort.by("lastname")); @@ -108,7 +108,7 @@ void nonNativeQueryWithSortShouldHandleRewrites() throws NoSuchMethodException { } @Test - void nonNativeQueryWithPageableShouldHandleRewrites() throws NoSuchMethodException { + void nonNativeQueryWithPageableShouldHandleRewrites() { repository.findByNonNativePagedQuery("Matthews", PageRequest.of(2, 1)); @@ -119,7 +119,7 @@ void nonNativeQueryWithPageableShouldHandleRewrites() throws NoSuchMethodExcepti } @Test - void nativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethodException { + void nativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() { repository.findByNativeQueryWithNoRewrite("Matthews"); @@ -127,7 +127,7 @@ void nativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethod } @Test - void nonNativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMethodException { + void nonNativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() { repository.findByNonNativeQueryWithNoRewrite("Matthews"); @@ -135,7 +135,7 @@ void nonNativeQueryWithNoRewriteAnnotationShouldNotDoRewrites() throws NoSuchMet } @Test - void nativeQueryShouldHandleRewritesUsingRepositoryRewriter() throws NoSuchMethodException { + void nativeQueryShouldHandleRewritesUsingRepositoryRewriter() { repository.findByNativeQueryUsingRepository("Matthews"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index d394bfc5c9..8ebb5a4e8c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -37,7 +37,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; -import org.springframework.beans.factory.BeanFactory; + import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -45,6 +45,7 @@ import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -82,7 +83,6 @@ class SimpleJpaQueryUnitTests { @Mock RepositoryMetadata metadata; @Mock ParameterBinder binder; @Mock Metamodel metamodel; - @Mock BeanFactory beanFactory; private ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); @@ -113,8 +113,8 @@ void prefersDeclaredCountQueryOverCreatingOne() throws Exception { extractor); when(em.createQuery("foo", Long.class)).thenReturn(typedQuery); - SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", null, EVALUATION_CONTEXT_PROVIDER, - PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); + SimpleJpaQuery jpaQuery = new SimpleJpaQuery(method, em, "select u from User u", null, + QueryRewriter.IdentityQueryRewriter.INSTANCE, EVALUATION_CONTEXT_PROVIDER, PARSER); assertThat(jpaQuery.createCountQuery(new JpaParametersParameterAccessor(method.getParameters(), new Object[] {}))) .isEqualTo(typedQuery); @@ -129,7 +129,7 @@ void doesNotApplyPaginationToCountQuery() throws Exception { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", null, - EVALUATION_CONTEXT_PROVIDER, PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); + QueryRewriter.IdentityQueryRewriter.INSTANCE, EVALUATION_CONTEXT_PROVIDER, PARSER); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -144,7 +144,8 @@ void discoversNativeQuery() throws Exception { Method method = SampleRepository.class.getMethod("findNativeByLastname", String.class); JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, - queryMethod.getAnnotatedQuery(), null, EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory)); + queryMethod.getAnnotatedQuery(), null, QueryRewriter.IdentityQueryRewriter.INSTANCE, + EVALUATION_CONTEXT_PROVIDER); assertThat(jpaQuery instanceof NativeJpaQuery).isTrue(); @@ -247,7 +248,8 @@ void resolvesExpressionInCountQuery() throws Exception { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); AbstractJpaQuery jpaQuery = new SimpleJpaQuery(queryMethod, em, "select u from User u", - "select count(u.id) from #{#entityName} u", EVALUATION_CONTEXT_PROVIDER, PARSER, new QueryRewriterBeanFactoryProvider(beanFactory)); + "select count(u.id) from #{#entityName} u", QueryRewriter.IdentityQueryRewriter.INSTANCE, + EVALUATION_CONTEXT_PROVIDER, PARSER); jpaQuery.createCountQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { PageRequest.of(1, 10) })); @@ -259,7 +261,7 @@ private AbstractJpaQuery createJpaQuery(Method method) { JpaQueryMethod queryMethod = new JpaQueryMethod(method, metadata, factory, extractor); return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(queryMethod, em, queryMethod.getAnnotatedQuery(), null, - EVALUATION_CONTEXT_PROVIDER, new QueryRewriterBeanFactoryProvider(beanFactory)); + QueryRewriter.IdentityQueryRewriter.INSTANCE, EVALUATION_CONTEXT_PROVIDER); } interface SampleRepository { From 3f40705eeb8b69b943cd2047850d5da872a5e770 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 2 May 2022 11:51:03 -0500 Subject: [PATCH 177/821] Update reference docs with QueryRewriter details. See #2162. --- src/main/asciidoc/jpa.adoc | 81 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 664f72e612..f7e1fb04f4 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -332,6 +332,87 @@ public interface UserRepository extends JpaRepository { ---- ==== +[[jpa.query-methods.query-rewriter]] +==== Applying a QueryRewriter + +Sometimes, no matter how many features you try to apply, it seems impossible to get Spring Data JPA to apply every thing +you'd like to a query before it is sent to the `EntityManager`. + +You have the ability to get your hands on the query, right before it's sent to the `EntityManager` and "rewrite" it. That is, +you can make any alterations at the last moment. + +.Declare a QueryRewriter using `@Query` +==== +[source, java] +---- +public interface MyRepository extends JpaRepository { + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", + nativeQuery = true, + queryRewriter = MyQueryRewriter.class) + List findByNativeQuery(String param); + + @Query(value = "select original_user_alias from User original_user_alias", + queryRewriter = MyQueryRewriter.class) + List findByNonNativeQuery(String param); +} +---- +==== + +This example shows both a native (pure SQL) rewriter as well as a JPQL query, both leveraging the same `QueryRewriter`. +In this scenario, Spring Data JPA will look for a bean registered in the application context of the corresponding type. + +You can write a query rewriter like this: + +.Example `QueryRewriter` +==== +[source, java] +---- +public class MyQueryRewriter implements QueryRewriter { + + @Override + public String rewrite(String query, Sort sort) { + return query.replaceAll("original_user_alias", "rewritten_user_alias"); + } +} +---- +==== + +You have to ensure your `QueryRewriter` is registered in the application context, whether it's by applying one of Spring Framework's +`@Component`-based annotations, or having it as part of a `@Bean` method inside an `@Configuration` class. + +Another option is to have the repository itself implement the interface. + +.Repository that provides the `QueryRewriter` +==== +[source, java] +---- +public interface MyRepository extends JpaRepository, QueryRewriter { + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", + nativeQuery = true, + queryRewriter = MyRepository.class) + List findByNativeQuery(String param); + + @Query(value = "select original_user_alias from User original_user_alias", + queryRewriter = MyRepository.class) + List findByNonNativeQuery(String param); + + @Override + default String rewrite(String query, Sort sort) { + return query.replaceAll("original_user_alias", "rewritten_user_alias"); + } +} +---- +==== + +Depending on what you're doing with your `QueryRewriter`, it may be advisable to have more than one, each registered with the +application context. + +NOTE: In a CDI-based environment, Spring Data JPA will search the `BeanManager` for instances of your implementation of +`QueryRewriter`. + + [[jpa.query-methods.at-query.advanced-like]] ==== Using Advanced `LIKE` Expressions From e4e153d273cd023392871e3989d3b1bb84d911dd Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Thu, 28 Apr 2022 15:57:40 +0200 Subject: [PATCH 178/821] Fixes `*` bug in `createCountQueryFor`. In commit 3e64d9ad9b7d45fcf1231dbaf207be49cc481e7a a bug got introduced that uses the next symbol after the table name for the count function. With this commit this should be now resolved. The count query will use `*` when there is no alias present nor a variable. Related tickets #2341, #2177, #2260, #2511 --- .../data/jpa/repository/query/QueryUtils.java | 9 +++++++-- .../query/QueryEnhancerUnitTests.java | 20 +++++++++++++++++++ .../repository/query/QueryUtilsUnitTests.java | 17 ++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index da041122da..56151a04b4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -525,14 +525,19 @@ public static String createCountQueryFor(String originalQuery, @Nullable String boolean useVariable = StringUtils.hasText(variable) // && !variable.startsWith(" new") // && !variable.startsWith("count(") // - && !variable.contains(",") // - && !variable.contains("*"); + && !variable.contains(","); String complexCountValue = matcher.matches() && StringUtils.hasText(matcher.group(COMPLEX_COUNT_FIRST_INDEX)) ? COMPLEX_COUNT_VALUE : COMPLEX_COUNT_LAST_VALUE; String replacement = useVariable ? SIMPLE_COUNT_VALUE : complexCountValue; + + String alias = QueryUtils.detectAlias(originalQuery); + if("*".equals(variable) && alias != null) { + replacement = alias; + } + countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, replacement)); } else { countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, countProjection)); 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 e40535c1ee..1b56f36293 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 @@ -712,6 +712,26 @@ void correctApplySortOnComplexNestedFunctionQuery() { assertThat(result).containsIgnoringCase("order by dd.institutesIds"); } + + @Test //GH-2511 + void countQueryUsesCorrectVariable() { + StringQuery nativeQuery = new StringQuery("SELECT * FROM User WHERE created_at > $1", true); + QueryEnhancer queryEnhancer = getEnhancer(nativeQuery); + String countQueryFor = queryEnhancer.createCountQueryFor(); + assertThat(countQueryFor).isEqualTo("SELECT count(*) FROM User WHERE created_at > $1"); + + nativeQuery = new StringQuery("SELECT * FROM (select * from test) ",true); + queryEnhancer = getEnhancer(nativeQuery); + countQueryFor = queryEnhancer.createCountQueryFor(); + assertThat(countQueryFor).isEqualTo("SELECT count(*) FROM (SELECT * FROM test)"); + + nativeQuery = new StringQuery("SELECT * FROM (select * from test) as test",true); + queryEnhancer = getEnhancer(nativeQuery); + countQueryFor = queryEnhancer.createCountQueryFor(); + assertThat(countQueryFor).isEqualTo("SELECT count(test) FROM (SELECT * FROM test) AS test"); + } + + public static Stream detectsJoinAliasesCorrectlySource() { return Stream.of( // diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 9a3e86965e..e6a9a3f4b1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -638,4 +638,21 @@ void applySortingAccountsForNativeWindowFunction() { "select * from (select * from user order by 1, 2, 3 desc limit 10) u order by u.active asc, age desc"); } + @Test //GH-2511 + void countQueryUsesCorrectVariable() { + String countQueryFor = createCountQueryFor("SELECT * FROM User WHERE created_at > $1"); + assertThat(countQueryFor).isEqualTo("select count(*) FROM User WHERE created_at > $1"); + + countQueryFor = createCountQueryFor("SELECT * FROM mytable WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); + assertThat(countQueryFor).isEqualTo("select count(*) FROM mytable WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); + + countQueryFor = createCountQueryFor("SELECT * FROM context ORDER BY time"); + assertThat(countQueryFor).isEqualTo("select count(*) FROM context"); + + countQueryFor = createCountQueryFor("select * FROM users_statuses WHERE (user_created_at BETWEEN $1 AND $2)"); + assertThat(countQueryFor).isEqualTo("select count(*) FROM users_statuses WHERE (user_created_at BETWEEN $1 AND $2)"); + + countQueryFor = createCountQueryFor("SELECT * FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); + assertThat(countQueryFor).isEqualTo("select count(us) FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); + } } From 1f2e4a179a0d703872d57f09c8a459c1fdb37349 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 4 May 2022 10:31:48 -0500 Subject: [PATCH 179/821] Polishing. See #2514. --- .../jpa/repository/query/QueryEnhancerUnitTests.java | 9 ++++----- .../data/jpa/repository/query/QueryUtilsUnitTests.java | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) 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 1b56f36293..c181425f12 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 @@ -712,26 +712,25 @@ void correctApplySortOnComplexNestedFunctionQuery() { assertThat(result).containsIgnoringCase("order by dd.institutesIds"); } - - @Test //GH-2511 + @Test // GH-2511 void countQueryUsesCorrectVariable() { + StringQuery nativeQuery = new StringQuery("SELECT * FROM User WHERE created_at > $1", true); QueryEnhancer queryEnhancer = getEnhancer(nativeQuery); String countQueryFor = queryEnhancer.createCountQueryFor(); assertThat(countQueryFor).isEqualTo("SELECT count(*) FROM User WHERE created_at > $1"); - nativeQuery = new StringQuery("SELECT * FROM (select * from test) ",true); + nativeQuery = new StringQuery("SELECT * FROM (select * from test) ", true); queryEnhancer = getEnhancer(nativeQuery); countQueryFor = queryEnhancer.createCountQueryFor(); assertThat(countQueryFor).isEqualTo("SELECT count(*) FROM (SELECT * FROM test)"); - nativeQuery = new StringQuery("SELECT * FROM (select * from test) as test",true); + nativeQuery = new StringQuery("SELECT * FROM (select * from test) as test", true); queryEnhancer = getEnhancer(nativeQuery); countQueryFor = queryEnhancer.createCountQueryFor(); assertThat(countQueryFor).isEqualTo("SELECT count(test) FROM (SELECT * FROM test) AS test"); } - public static Stream detectsJoinAliasesCorrectlySource() { return Stream.of( // diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index e6a9a3f4b1..3da2af35ff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -638,8 +638,9 @@ void applySortingAccountsForNativeWindowFunction() { "select * from (select * from user order by 1, 2, 3 desc limit 10) u order by u.active asc, age desc"); } - @Test //GH-2511 + @Test // GH-2511 void countQueryUsesCorrectVariable() { + String countQueryFor = createCountQueryFor("SELECT * FROM User WHERE created_at > $1"); assertThat(countQueryFor).isEqualTo("select count(*) FROM User WHERE created_at > $1"); From 5103be3f289ea1e0d49aa0ccd88513eba746007f Mon Sep 17 00:00:00 2001 From: Darin Manica Date: Sun, 1 May 2022 06:51:38 -0600 Subject: [PATCH 180/821] Correctly handle order by aliases. In order to correctly identify aliases in the order by clause, we cannot process the from clauses left-to-right using regular expressions. These must be removed inner-to-outer. Commit c93aa25 resulted in a bug where the subquery would be incorrectly identified as the alias, as by the following query: ``` from Parent p join p.children c where not c.id not in (select c2.id from Child c2) ``` Passing in a Sort.by("name") would result in "order by c2.name" instead of "order by p.name". Thus, it was using the alias of the inner query instead of the outer query. [This comment](https://github.com/spring-projects/spring-data-jpa/issues/2260#issuecomment-929510358) suggests removing the content of the inner query, with the caveat of the entire query being surrounded by parenthesis. This commit does exactly that, by removing the subquery before the alias is identified. It also handles the case when the entire query is surrounded by parenthesis. Unit tests illustrate this along with several examples of removing the subquery to correctly identify the alias for the order by clause. See #2260 (c93aa25), #2500, #2518. --- .../data/jpa/repository/query/QueryUtils.java | 62 ++++++++++++++++++- .../repository/query/QueryUtilsUnitTests.java | 48 +++++++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 56151a04b4..30cb53f6ad 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -76,6 +76,7 @@ * @author Greg Turnquist * @author Diego Krupitza * @author Jędrzej Biedrzycki + * @author Darin Manica */ public abstract class QueryUtils { @@ -100,6 +101,8 @@ public abstract class QueryUtils { private static final Pattern ALIAS_MATCH; private static final Pattern COUNT_MATCH; + private static final Pattern STARTS_WITH_PAREN = Pattern.compile("^\\s*\\("); + private static final Pattern PARENS_TO_REMOVE = Pattern.compile("(\\(.*\\bfrom\\b[^)]+\\))", CASE_INSENSITIVE); private static final Pattern PROJECTION_CLAUSE = Pattern.compile("select\\s+(?:distinct\\s+)?(.+)\\s+from", Pattern.CASE_INSENSITIVE); @@ -431,13 +434,70 @@ private static String toJpaDirection(Order order) { @Deprecated public static String detectAlias(String query) { String alias = null; - Matcher matcher = ALIAS_MATCH.matcher(query); + Matcher matcher = ALIAS_MATCH.matcher(removeSubqueries(query)); while (matcher.find()) { alias = matcher.group(2); } return alias; } + /** + * Remove subqueries from the query, in order to identify the correct alias + * in order by clauses. If the entire query is surrounded by parenthesis, the + * outermost parenthesis are not removed. + * + * @param query + * @return query with all subqueries removed. + */ + static String removeSubqueries(String query) { + if (!StringUtils.hasText(query)) { + return query; + } + + final List opens = new ArrayList<>(); + final List closes = new ArrayList<>(); + final List closeMatches = new ArrayList<>(); + for (int i=0; i=(startsWithParen?1:0); i--) { + final Integer open = opens.get(i); + final Integer close = findClose(open, closes, closeMatches) + 1; + + + if (close > open) { + final String subquery = sb.substring(open, close); + final Matcher matcher = PARENS_TO_REMOVE.matcher(subquery); + if (matcher.find()) { + sb.replace(open, close, new String(new char[close-open]).replace('\0', ' ')); + } + } + } + + return sb.toString(); + } + + private static Integer findClose(final Integer open, final List closes, final List closeMatches) { + for (int i=0; i open && !closeMatches.get(i)) { + closeMatches.set(i, Boolean.TRUE); + return close; + } + } + + return -1; + } + /** * Creates a where-clause referencing the given entities and appends it to the given query string. Binds the given * entities to the query. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 3da2af35ff..4e8e244b6e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -20,6 +20,8 @@ import java.util.Collections; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; @@ -27,6 +29,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.JpaSort; +import org.springframework.util.StringUtils; /** * Unit test for {@link QueryUtils}. @@ -41,6 +44,7 @@ * @author Mohammad Hewedy * @author Greg Turnquist * @author Jędrzej Biedrzycki + * @author Darin Manica */ class QueryUtilsUnitTests { @@ -50,6 +54,7 @@ class QueryUtilsUnitTests { private static final String COUNT_QUERY = "select count(u) from User u"; private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?"; + private static final Pattern MULTI_WHITESPACE = Pattern.compile("\\s+"); @Test void createsCountQueryCorrectly() { @@ -102,7 +107,7 @@ void allowsShortJpaSyntax() { assertCountQuery(SIMPLE_QUERY, COUNT_QUERY); } - @Test + @Test // GH-2260 void detectsAliasCorrectly() { assertThat(detectAlias(QUERY)).isEqualTo("u"); @@ -113,6 +118,47 @@ void detectsAliasCorrectly() { assertThat(detectAlias("select u from User u")).isEqualTo("u"); assertThat(detectAlias("select u from com.acme.User u")).isEqualTo("u"); assertThat(detectAlias("select u from T05User u")).isEqualTo("u"); + assertThat(detectAlias("select u from User u where not exists (from User u2)")).isEqualTo("u"); + assertThat(detectAlias("(select u from User u where not exists (from User u2))")).isEqualTo("u"); + assertThat(detectAlias("(select u from User u where not exists ((from User u2 where not exists (from User u3))))")).isEqualTo("u"); + assertThat(detectAlias("from Foo f left join f.bar b with type(b) = BarChild where (f.id = (select max(f.id) from Foo f2 where type(f2) = FooChild) or 1 <> 1) and 1=1")).isEqualTo("f"); + assertThat(detectAlias("(from Foo f max(f) ((((select * from Foo f2 (from Foo f3) max(*)) (from Foo f4)) max(f5)) (f6)) (from Foo f7))")).isEqualTo("f"); + } + + @Test // GH-2260 + void testRemoveSubqueries() throws Exception { + // boundary conditions + assertThat(removeSubqueries(null)).isNull(); + assertThat(removeSubqueries("")).isEmpty(); + assertThat(removeSubqueries(" ")).isEqualTo(" "); + assertThat(removeSubqueries("(")).isEqualTo("("); + assertThat(removeSubqueries(")")).isEqualTo(")"); + assertThat(removeSubqueries("(()")).isEqualTo("(()"); + assertThat(removeSubqueries("())")).isEqualTo("())"); + + // realistic conditions + assertThat(removeSubqueries(QUERY)).isEqualTo(QUERY); + assertThat(removeSubqueries(SIMPLE_QUERY)).isEqualTo(SIMPLE_QUERY); + assertThat(removeSubqueries(COUNT_QUERY)).isEqualTo(COUNT_QUERY); + assertThat(removeSubqueries(QUERY_WITH_AS)).isEqualTo(QUERY_WITH_AS); + assertThat(removeSubqueries("SELECT FROM USER U")).isEqualTo("SELECT FROM USER U"); + assertThat(removeSubqueries("select u from User u")).isEqualTo("select u from User u"); + assertThat(removeSubqueries("select u from com.acme.User u")).isEqualTo("select u from com.acme.User u"); + assertThat(removeSubqueries("select u from T05User u")).isEqualTo("select u from T05User u"); + assertThat(normalizeWhitespace(removeSubqueries("select u from User u where not exists (from User u2)"))).isEqualTo("select u from User u where not exists"); + assertThat(normalizeWhitespace(removeSubqueries("(select u from User u where not exists (from User u2))"))).isEqualTo("(select u from User u where not exists )"); + assertThat(normalizeWhitespace(removeSubqueries("select u from User u where not exists (from User u2 where not exists (from User u3))"))).isEqualTo("select u from User u where not exists"); + assertThat(normalizeWhitespace(removeSubqueries("select u from User u where not exists ((from User u2 where not exists (from User u3)))"))).isEqualTo("select u from User u where not exists ( )"); + assertThat(normalizeWhitespace(removeSubqueries("(select u from User u where not exists ((from User u2 where not exists (from User u3))))"))).isEqualTo("(select u from User u where not exists ( ))"); + } + + private String normalizeWhitespace(String s) { + Matcher matcher = MULTI_WHITESPACE.matcher(s); + if (matcher.find()) { + return matcher.replaceAll(" ").trim(); + } + + return StringUtils.trimWhitespace(s); } @Test From 7bcc8042c1e5c6871b8d91cd9dda9525af9911b7 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 4 May 2022 11:31:16 -0500 Subject: [PATCH 181/821] Polishing. See #2260 (c93aa25), #2500, #2518. --- .../data/jpa/repository/query/QueryUtils.java | 44 ++++++++++-------- .../repository/query/QueryUtilsUnitTests.java | 45 +++++++++++++------ 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 30cb53f6ad..fb7a0a0bbf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -433,6 +433,7 @@ private static String toJpaDirection(Order order) { @Nullable @Deprecated public static String detectAlias(String query) { + String alias = null; Matcher matcher = ALIAS_MATCH.matcher(removeSubqueries(query)); while (matcher.find()) { @@ -442,23 +443,25 @@ public static String detectAlias(String query) { } /** - * Remove subqueries from the query, in order to identify the correct alias - * in order by clauses. If the entire query is surrounded by parenthesis, the - * outermost parenthesis are not removed. + * Remove subqueries from the query, in order to identify the correct alias in order by clauses. If the entire query + * is surrounded by parenthesis, the outermost parenthesis are not removed. * * @param query * @return query with all subqueries removed. */ static String removeSubqueries(String query) { + if (!StringUtils.hasText(query)) { return query; } - final List opens = new ArrayList<>(); - final List closes = new ArrayList<>(); - final List closeMatches = new ArrayList<>(); - for (int i=0; i opens = new ArrayList<>(); + List closes = new ArrayList<>(); + List closeMatches = new ArrayList<>(); + + for (int i = 0; i < query.length(); i++) { + + char c = query.charAt(i); if (c == '(') { opens.add(i); } else if (c == ')') { @@ -467,18 +470,19 @@ static String removeSubqueries(String query) { } } - final StringBuilder sb = new StringBuilder(query); - final boolean startsWithParen = STARTS_WITH_PAREN.matcher(query).find(); - for (int i=opens.size()-1; i>=(startsWithParen?1:0); i--) { - final Integer open = opens.get(i); - final Integer close = findClose(open, closes, closeMatches) + 1; + StringBuilder sb = new StringBuilder(query); + boolean startsWithParen = STARTS_WITH_PAREN.matcher(query).find(); + for (int i = opens.size() - 1; i >= (startsWithParen ? 1 : 0); i--) { + Integer open = opens.get(i); + Integer close = findClose(open, closes, closeMatches) + 1; if (close > open) { - final String subquery = sb.substring(open, close); - final Matcher matcher = PARENS_TO_REMOVE.matcher(subquery); + + String subquery = sb.substring(open, close); + Matcher matcher = PARENS_TO_REMOVE.matcher(subquery); if (matcher.find()) { - sb.replace(open, close, new String(new char[close-open]).replace('\0', ' ')); + sb.replace(open, close, new String(new char[close - open]).replace('\0', ' ')); } } } @@ -487,8 +491,10 @@ static String removeSubqueries(String query) { } private static Integer findClose(final Integer open, final List closes, final List closeMatches) { - for (int i=0; i open && !closeMatches.get(i)) { closeMatches.set(i, Boolean.TRUE); return close; @@ -594,7 +600,7 @@ public static String createCountQueryFor(String originalQuery, @Nullable String String replacement = useVariable ? SIMPLE_COUNT_VALUE : complexCountValue; String alias = QueryUtils.detectAlias(originalQuery); - if("*".equals(variable) && alias != null) { + if ("*".equals(variable) && alias != null) { replacement = alias; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 4e8e244b6e..08c1820839 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -120,13 +120,19 @@ void detectsAliasCorrectly() { assertThat(detectAlias("select u from T05User u")).isEqualTo("u"); assertThat(detectAlias("select u from User u where not exists (from User u2)")).isEqualTo("u"); assertThat(detectAlias("(select u from User u where not exists (from User u2))")).isEqualTo("u"); - assertThat(detectAlias("(select u from User u where not exists ((from User u2 where not exists (from User u3))))")).isEqualTo("u"); - assertThat(detectAlias("from Foo f left join f.bar b with type(b) = BarChild where (f.id = (select max(f.id) from Foo f2 where type(f2) = FooChild) or 1 <> 1) and 1=1")).isEqualTo("f"); - assertThat(detectAlias("(from Foo f max(f) ((((select * from Foo f2 (from Foo f3) max(*)) (from Foo f4)) max(f5)) (f6)) (from Foo f7))")).isEqualTo("f"); + assertThat(detectAlias("(select u from User u where not exists ((from User u2 where not exists (from User u3))))")) + .isEqualTo("u"); + assertThat(detectAlias( + "from Foo f left join f.bar b with type(b) = BarChild where (f.id = (select max(f.id) from Foo f2 where type(f2) = FooChild) or 1 <> 1) and 1=1")) + .isEqualTo("f"); + assertThat(detectAlias( + "(from Foo f max(f) ((((select * from Foo f2 (from Foo f3) max(*)) (from Foo f4)) max(f5)) (f6)) (from Foo f7))")) + .isEqualTo("f"); } @Test // GH-2260 void testRemoveSubqueries() throws Exception { + // boundary conditions assertThat(removeSubqueries(null)).isNull(); assertThat(removeSubqueries("")).isEmpty(); @@ -145,11 +151,19 @@ void testRemoveSubqueries() throws Exception { assertThat(removeSubqueries("select u from User u")).isEqualTo("select u from User u"); assertThat(removeSubqueries("select u from com.acme.User u")).isEqualTo("select u from com.acme.User u"); assertThat(removeSubqueries("select u from T05User u")).isEqualTo("select u from T05User u"); - assertThat(normalizeWhitespace(removeSubqueries("select u from User u where not exists (from User u2)"))).isEqualTo("select u from User u where not exists"); - assertThat(normalizeWhitespace(removeSubqueries("(select u from User u where not exists (from User u2))"))).isEqualTo("(select u from User u where not exists )"); - assertThat(normalizeWhitespace(removeSubqueries("select u from User u where not exists (from User u2 where not exists (from User u3))"))).isEqualTo("select u from User u where not exists"); - assertThat(normalizeWhitespace(removeSubqueries("select u from User u where not exists ((from User u2 where not exists (from User u3)))"))).isEqualTo("select u from User u where not exists ( )"); - assertThat(normalizeWhitespace(removeSubqueries("(select u from User u where not exists ((from User u2 where not exists (from User u3))))"))).isEqualTo("(select u from User u where not exists ( ))"); + assertThat(normalizeWhitespace(removeSubqueries("select u from User u where not exists (from User u2)"))) + .isEqualTo("select u from User u where not exists"); + assertThat(normalizeWhitespace(removeSubqueries("(select u from User u where not exists (from User u2))"))) + .isEqualTo("(select u from User u where not exists )"); + assertThat(normalizeWhitespace( + removeSubqueries("select u from User u where not exists (from User u2 where not exists (from User u3))"))) + .isEqualTo("select u from User u where not exists"); + assertThat(normalizeWhitespace( + removeSubqueries("select u from User u where not exists ((from User u2 where not exists (from User u3)))"))) + .isEqualTo("select u from User u where not exists ( )"); + assertThat(normalizeWhitespace( + removeSubqueries("(select u from User u where not exists ((from User u2 where not exists (from User u3))))"))) + .isEqualTo("(select u from User u where not exists ( ))"); } private String normalizeWhitespace(String s) { @@ -690,16 +704,21 @@ void countQueryUsesCorrectVariable() { String countQueryFor = createCountQueryFor("SELECT * FROM User WHERE created_at > $1"); assertThat(countQueryFor).isEqualTo("select count(*) FROM User WHERE created_at > $1"); - countQueryFor = createCountQueryFor("SELECT * FROM mytable WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); - assertThat(countQueryFor).isEqualTo("select count(*) FROM mytable WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); + countQueryFor = createCountQueryFor( + "SELECT * FROM mytable WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); + assertThat(countQueryFor) + .isEqualTo("select count(*) FROM mytable WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); countQueryFor = createCountQueryFor("SELECT * FROM context ORDER BY time"); assertThat(countQueryFor).isEqualTo("select count(*) FROM context"); countQueryFor = createCountQueryFor("select * FROM users_statuses WHERE (user_created_at BETWEEN $1 AND $2)"); - assertThat(countQueryFor).isEqualTo("select count(*) FROM users_statuses WHERE (user_created_at BETWEEN $1 AND $2)"); + assertThat(countQueryFor) + .isEqualTo("select count(*) FROM users_statuses WHERE (user_created_at BETWEEN $1 AND $2)"); - countQueryFor = createCountQueryFor("SELECT * FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); - assertThat(countQueryFor).isEqualTo("select count(us) FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); + countQueryFor = createCountQueryFor( + "SELECT * FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); + assertThat(countQueryFor) + .isEqualTo("select count(us) FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); } } From afcfe85cb7c877db5c84fd8b25de2f2027e23f39 Mon Sep 17 00:00:00 2001 From: "Bajohr, Rayk" Date: Mon, 25 Apr 2022 14:56:56 +0200 Subject: [PATCH 182/821] Update Maven project meta information. See #2506. --- pom.xml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 069cad460c..ca8ee5b81e 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,15 @@ Spring Data JPA Parent Parent module for Spring Data JPA repositories. - https://projects.spring.io/spring-data-jpa + https://spring.io/projects/spring-data-jpa + + scm:git:git://github.com:spring-projects/spring-data-jpa.git + scm:git:git@github.com:spring-projects/spring-data-jpa.git + https://github.com/spring-projects/spring-data-jpa + + + https://github.com/spring-projects/spring-data-jpa/issues + org.springframework.data.build From 624c6c681f71d3fe7043ed4428452e469b492a73 Mon Sep 17 00:00:00 2001 From: Darin Manica Date: Thu, 5 May 2022 06:22:39 -0600 Subject: [PATCH 183/821] Fix case where the from clause is misidentified. Add a zero-width word boundary to the regex that identifies the from clause. This is used in alias detection. See #2508, #2260. --- .../data/jpa/repository/query/QueryUtils.java | 2 +- .../jpa/repository/query/QueryUtilsUnitTests.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index fb7a0a0bbf..32410b90da 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -138,7 +138,7 @@ public abstract class QueryUtils { static { StringBuilder builder = new StringBuilder(); - builder.append("(?<=from)"); // from as starting delimiter + builder.append("(?<=\\bfrom)"); // from as starting delimiter builder.append("(?:\\s)+"); // at least one space separating builder.append(IDENTIFIER_GROUP); // Entity name, can be qualified (any builder.append("(?:\\sas)*"); // exclude possible "as" keyword diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 08c1820839..1adc41c7f3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -128,6 +128,18 @@ void detectsAliasCorrectly() { assertThat(detectAlias( "(from Foo f max(f) ((((select * from Foo f2 (from Foo f3) max(*)) (from Foo f4)) max(f5)) (f6)) (from Foo f7))")) .isEqualTo("f"); + assertThat(detectAlias( + "SELECT e FROM DbEvent e WHERE (CAST(:modifiedFrom AS date) IS NULL OR e.modificationDate >= :modifiedFrom)")) + .isEqualTo("e"); + assertThat(detectAlias("from User u where (cast(:effective as date) is null) OR :effective >= u.createdAt")) + .isEqualTo("u"); + assertThat(detectAlias("from User u where (cast(:effectiveDate as date) is null) OR :effectiveDate >= u.createdAt")) + .isEqualTo("u"); + assertThat(detectAlias("from User u where (cast(:effectiveFrom as date) is null) OR :effectiveFrom >= u.createdAt")) + .isEqualTo("u"); + assertThat( + detectAlias("from User u where (cast(:e1f2f3ectiveFrom as date) is null) OR :effectiveFrom >= u.createdAt")) + .isEqualTo("u"); } @Test // GH-2260 From 2e5ed1d0ec06d52cc553008afdfe3050fac24fcf Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Sat, 28 Dec 2019 23:19:44 +0800 Subject: [PATCH 184/821] Provide Specification allOf and anyOf operators to help compose Specifications.. See #1943. Original PR: #404. --- .../data/jpa/domain/Specification.java | 45 +++++++++++++ .../jpa/domain/SpecificationUnitTests.java | 37 +++++++++++ .../jpa/repository/UserRepositoryTests.java | 63 ++++++++++++++----- 3 files changed, 129 insertions(+), 16 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java index 75708b0ec0..5efadbf6b8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java @@ -21,6 +21,8 @@ import jakarta.persistence.criteria.Root; import java.io.Serializable; +import java.util.Arrays; +import java.util.stream.StreamSupport; import org.springframework.lang.Nullable; @@ -33,6 +35,7 @@ * @author Sebastian Staudt * @author Mark Paluch * @author Jens Schauder + * @author Daniel Shuy */ public interface Specification extends Serializable { @@ -98,4 +101,46 @@ default Specification or(@Nullable Specification other) { */ @Nullable Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder); + + /** + * Applies an AND operation to all the given {@link Specification}s. + * + * @param specifications The {@link Specification}s to compose. Can contain {@code null}s. + * @return The conjunction of the specifications + * @see #and(Specification) + */ + static Specification allOf(Iterable> specifications) { + + return StreamSupport.stream(specifications.spliterator(), false) // + .reduce(Specification.where(null), Specification::and); + } + + /** + * @see #allOf(Iterable) + */ + @SafeVarargs + static Specification allOf(Specification... specifications) { + return allOf(Arrays.asList(specifications)); + } + + /** + * Applies an OR operation to all the given {@link Specification}s. + * + * @param specifications The {@link Specification}s to compose. Can contain {@code null}s. + * @return The disjunction of the specifications + * @see #or(Specification) + */ + static Specification anyOf(Iterable> specifications) { + + return StreamSupport.stream(specifications.spliterator(), false) // + .reduce(Specification.where(null), Specification::or); + } + + /** + * @see #anyOf(Iterable) + */ + @SafeVarargs + static Specification anyOf(Specification... specifications) { + return anyOf(Arrays.asList(specifications)); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java index 84ca50c178..695ee67b66 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java @@ -44,6 +44,7 @@ * @author Sebastian Staudt * @author Jens Schauder * @author Mark Paluch + * @author Daniel Shuy */ @SuppressWarnings("serial") @ExtendWith(MockitoExtension.class) @@ -118,6 +119,42 @@ void orConcatenatesNullSpecToSpec() { assertThat(specification.toPredicate(root, query, builder)).isEqualTo(predicate); } + @Test // DATAJPA-1651 + public void allOfConcatenatesNull() { + + Specification specification = Specification.allOf(null, spec, null); + + assertThat(specification).isNotNull(); + assertThat(specification.toPredicate(root, query, builder)).isEqualTo(predicate); + } + + @Test // DATAJPA-1651 + public void anyOfConcatenatesNull() { + + Specification specification = Specification.anyOf(null, spec, null); + + assertThat(specification).isNotNull(); + assertThat(specification.toPredicate(root, query, builder)).isEqualTo(predicate); + } + + @Test // DATAJPA-1651 + public void emptyAllOfReturnsEmptySpecification() { + + Specification specification = Specification.allOf(); + + assertThat(specification).isNotNull(); + assertThat(specification.toPredicate(root, query, builder)).isNull(); + } + + @Test // DATAJPA-1651 + public void emptyAnyOfReturnsEmptySpecification() { + + Specification specification = Specification.anyOf(); + + assertThat(specification).isNotNull(); + assertThat(specification.toPredicate(root, query, builder)).isNull(); + } + @Test // DATAJPA-523 void specificationsShouldBeSerializable() { 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 fcf384fac7..d120dee135 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 @@ -94,6 +94,7 @@ * @author Jesse Wouters * @author Greg Turnquist * @author Diego Krupitza + * @author Daniel Shuy */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @@ -489,12 +490,21 @@ void throwsExceptionForUnderSpecifiedSingleEntitySpecification() { .isThrownBy(() -> repository.findOne(userHasFirstnameLike("e"))); } - @Test + @Test // DATAJPA-1651 void executesCombinedSpecificationsCorrectly() { flushTestUsers(); - Specification spec = userHasFirstname("Oliver").or(userHasLastname("Arrasz")); - assertThat(repository.findAll(spec)).hasSize(2); + Specification spec1 = userHasFirstname("Oliver").or(userHasLastname("Arrasz")); + List users1 = repository.findAll(spec1); + assertThat(users1).hasSize(2); + + Specification spec2 = Specification.anyOf( // + userHasFirstname("Oliver"), // + userHasLastname("Arrasz")); + List users2 = repository.findAll(spec2); + assertThat(users2).hasSize(2); + + assertThat(users1).containsExactlyInAnyOrderElementsOf(users2); } @Test // DATAJPA-253 @@ -506,16 +516,27 @@ void executesNegatingSpecificationCorrectly() { assertThat(repository.findAll(spec)).containsOnly(secondUser); } - @Test + @Test // DATAJPA-1651 void executesCombinedSpecificationsWithPageableCorrectly() { flushTestUsers(); - Specification spec = userHasFirstname("Oliver").or(userHasLastname("Arrasz")); + Specification spec1 = userHasFirstname("Oliver").or(userHasLastname("Arrasz")); - Page users = repository.findAll(spec, PageRequest.of(0, 1)); - assertThat(users.getSize()).isEqualTo(1); - assertThat(users.hasPrevious()).isFalse(); - assertThat(users.getTotalElements()).isEqualTo(2L); + Page users1 = repository.findAll(spec1, PageRequest.of(0, 1)); + assertThat(users1.getSize()).isEqualTo(1); + assertThat(users1.hasPrevious()).isFalse(); + assertThat(users1.getTotalElements()).isEqualTo(2L); + + Specification spec2 = Specification.anyOf( // + userHasFirstname("Oliver"), // + userHasLastname("Arrasz")); + + Page users2 = repository.findAll(spec2, PageRequest.of(0, 1)); + assertThat(users2.getSize()).isEqualTo(1); + assertThat(users2.hasPrevious()).isFalse(); + assertThat(users2.getTotalElements()).isEqualTo(2L); + + assertThat(users1).containsExactlyInAnyOrderElementsOf(users2); } @Test @@ -602,14 +623,14 @@ void removeDetachedObject() { assertThat(repository.count()).isEqualTo(3L); } - @Test + @Test // DATAJPA-1651 void executesPagedSpecificationsCorrectly() { Page result = executeSpecWithSort(Sort.unsorted()); assertThat(result.getContent()).isSubsetOf(firstUser, thirdUser); } - @Test + @Test // DATAJPA-1651 void executesPagedSpecificationsWithSortCorrectly() { Page result = executeSpecWithSort(Sort.by(Direction.ASC, "lastname")); @@ -617,7 +638,7 @@ void executesPagedSpecificationsWithSortCorrectly() { assertThat(result.getContent()).contains(firstUser).doesNotContain(secondUser, thirdUser); } - @Test + @Test // DATAJPA-1651 void executesPagedSpecificationWithSortCorrectly2() { Page result = executeSpecWithSort(Sort.by(Direction.DESC, "lastname")); @@ -2821,11 +2842,21 @@ private Page executeSpecWithSort(Sort sort) { flushTestUsers(); - Specification spec = userHasFirstname("Oliver").or(userHasLastname("Matthews")); + Specification spec1 = userHasFirstname("Oliver").or(userHasLastname("Matthews")); - Page result = repository.findAll(spec, PageRequest.of(0, 1, sort)); - assertThat(result.getTotalElements()).isEqualTo(2L); - return result; + Page result1 = repository.findAll(spec1, PageRequest.of(0, 1, sort)); + assertThat(result1.getTotalElements()).isEqualTo(2L); + + Specification spec2 = Specification.anyOf( // + userHasFirstname("Oliver"), // + userHasLastname("Matthews")); + + Page result2 = repository.findAll(spec2, PageRequest.of(0, 1, sort)); + assertThat(result2.getTotalElements()).isEqualTo(2L); + + assertThat(result1).containsExactlyElementsOf(result2); + + return result2; } private interface UserProjectionInterfaceBased { From 6128cfdc2c51eb442827cb458c2c964ce87a1a70 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 5 May 2022 10:31:49 -0500 Subject: [PATCH 185/821] Polishing. See #1943. --- .../data/jpa/domain/SpecificationUnitTests.java | 12 ++++++------ .../data/jpa/repository/UserRepositoryTests.java | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java index 695ee67b66..8f08e7d6f7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java @@ -21,13 +21,13 @@ import static org.springframework.data.jpa.domain.Specification.not; import static org.springframework.util.SerializationUtils.*; -import java.io.Serializable; - import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import java.io.Serializable; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -119,7 +119,7 @@ void orConcatenatesNullSpecToSpec() { assertThat(specification.toPredicate(root, query, builder)).isEqualTo(predicate); } - @Test // DATAJPA-1651 + @Test // GH-1943 public void allOfConcatenatesNull() { Specification specification = Specification.allOf(null, spec, null); @@ -128,7 +128,7 @@ public void allOfConcatenatesNull() { assertThat(specification.toPredicate(root, query, builder)).isEqualTo(predicate); } - @Test // DATAJPA-1651 + @Test // GH-1943 public void anyOfConcatenatesNull() { Specification specification = Specification.anyOf(null, spec, null); @@ -137,7 +137,7 @@ public void anyOfConcatenatesNull() { assertThat(specification.toPredicate(root, query, builder)).isEqualTo(predicate); } - @Test // DATAJPA-1651 + @Test // GH-1943 public void emptyAllOfReturnsEmptySpecification() { Specification specification = Specification.allOf(); @@ -146,7 +146,7 @@ public void emptyAllOfReturnsEmptySpecification() { assertThat(specification.toPredicate(root, query, builder)).isNull(); } - @Test // DATAJPA-1651 + @Test // GH-1943 public void emptyAnyOfReturnsEmptySpecification() { Specification specification = Specification.anyOf(); 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 d120dee135..59b45711c6 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 @@ -490,7 +490,7 @@ void throwsExceptionForUnderSpecifiedSingleEntitySpecification() { .isThrownBy(() -> repository.findOne(userHasFirstnameLike("e"))); } - @Test // DATAJPA-1651 + @Test // GH-1943 void executesCombinedSpecificationsCorrectly() { flushTestUsers(); @@ -516,7 +516,7 @@ void executesNegatingSpecificationCorrectly() { assertThat(repository.findAll(spec)).containsOnly(secondUser); } - @Test // DATAJPA-1651 + @Test // GH-1943 void executesCombinedSpecificationsWithPageableCorrectly() { flushTestUsers(); @@ -623,14 +623,14 @@ void removeDetachedObject() { assertThat(repository.count()).isEqualTo(3L); } - @Test // DATAJPA-1651 + @Test void executesPagedSpecificationsCorrectly() { Page result = executeSpecWithSort(Sort.unsorted()); assertThat(result.getContent()).isSubsetOf(firstUser, thirdUser); } - @Test // DATAJPA-1651 + @Test void executesPagedSpecificationsWithSortCorrectly() { Page result = executeSpecWithSort(Sort.by(Direction.ASC, "lastname")); @@ -638,7 +638,7 @@ void executesPagedSpecificationsWithSortCorrectly() { assertThat(result.getContent()).contains(firstUser).doesNotContain(secondUser, thirdUser); } - @Test // DATAJPA-1651 + @Test void executesPagedSpecificationWithSortCorrectly2() { Page result = executeSpecWithSort(Sort.by(Direction.DESC, "lastname")); From 904959490801bdc55ebb6626505c892d68bee88f Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 5 May 2022 11:57:15 -0500 Subject: [PATCH 186/821] Bump h2 from 1.4.200 to 2.1.212 in /spring-data-envers. See #2501. --- spring-data-envers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 5e82ca77cc..8e2de43f59 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -79,7 +79,7 @@ com.h2database h2 - 1.4.200 + 2.1.212 test From c0cadfa40080b95e9bcda3df8cce74945dc5b891 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 5 May 2022 16:20:51 -0500 Subject: [PATCH 187/821] Introduce delete(Specification) on JpaSpecificationExecutor. Ever since JPA 2.1, CriteriaBuilder has offered createCriteriaDelete, returning a CriteriaDelete. With Spring Data JPA 3.0 rebased on JPA 3.0, we are able to guarantee this SPI, and hence rollout support for this feature. See #1262. --- .../data/jpa/domain/Specification.java | 1 + .../jpa/repository/JpaSpecificationExecutor.java | 8 ++++++++ .../repository/support/SimpleJpaRepository.java | 16 ++++++++++++++++ .../data/jpa/repository/UserRepositoryTests.java | 13 +++++++++++++ src/main/asciidoc/jpa.adoc | 15 +++++++++++++++ 5 files changed, 53 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java index 5efadbf6b8..c656cfa9f6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.domain; import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index b90b88d8fd..0ba4d03716 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -87,6 +87,14 @@ public interface JpaSpecificationExecutor { */ boolean exists(Specification spec); + /** + * Deletes by the {@link Specification} and returns the number of rows deleted. + * + * @param spec the {@link Specification} to use for the existence check. Must not be {@literal null}. + * @return the number of entities deleted + */ + long delete(Specification spec); + /** * Returns entities matching the given {@link Specification} applying the {@code queryFunction} that defines the query * and its result type. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index ebdec761b3..4dd01244fb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -24,6 +24,7 @@ import jakarta.persistence.Query; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.ParameterExpression; import jakarta.persistence.criteria.Path; @@ -485,6 +486,21 @@ public boolean exists(Specification spec) { return query.setMaxResults(1).getResultList().size() == 1; } + @Override + public long delete(Specification spec) { + + CriteriaBuilder builder = this.em.getCriteriaBuilder(); + CriteriaDelete delete = builder.createCriteriaDelete(getDomainClass()); + + Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder); + + if (predicate != null) { + delete.where(predicate); + } + + return this.em.createQuery(delete).executeUpdate(); + } + @Override public List findAll(Example example) { return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) 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 59b45711c6..3099c04cb0 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 @@ -2838,6 +2838,19 @@ void existsWithSpec() { assertThat(repository.exists(hundredYearsOld)).isTrue(); } + @Test // GH-1262 + void deleteWithSpec() { + + flushTestUsers(); + + Specification usersWithEInTheirName = userHasFirstnameLike("e"); + + long initialCount = repository.count(); + assertThat(repository.delete(usersWithEInTheirName)).isEqualTo(3L); + long finalCount = repository.count(); + assertThat(initialCount - finalCount).isEqualTo(3L); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index f7e1fb04f4..9d1a7c24bd 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -943,6 +943,21 @@ List customers = customerRepository.findAll( `Specification` offers some "`glue-code`" default methods to chain and combine `Specification` instances. These methods let you extend your data access layer by creating new `Specification` implementations and combining them with already existing implementations. ==== +And with JPA 2.1, the `CriteriaBuilder` API introduced `CriteriaDelete`. This is provided through `JpaSpecificationExecutor`'s `delete(Specification)` API. + +.Using a `Specification` to delete entries. +==== +[source, java] +---- +Specification ageLessThan18 = (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), 18) + +userRepository.delete(ageLessThan18); +---- +The `Specification` builds up a criteria where the `age` field (cast as an integer) is less than `18`. +Passed on to the `userRepository`, it will use JPA's `CriteriaDelete` feature to generate the right `DELETE` operation. +It then returns the number of entities deleted. +==== + include::{spring-data-commons-docs}/query-by-example.adoc[leveloffset=+1] include::query-by-example.adoc[leveloffset=+1] From 8ea70c1e420183c60df5cd3094def1c06a979148 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 9 May 2022 09:03:52 -0500 Subject: [PATCH 188/821] Remove duplicate 'distinct' applied in count operations. When performing a count operation, we are using countDistinct from JPA, and hence, don't need the JpaQueryCreator applying distinct outside the whole thing. Also added some details in the ref docs to help guide users on writing proper distinct-based queries. See #1380. --- .../query/JpaCountQueryCreator.java | 8 +++++-- .../jpa/repository/query/JpaQueryCreator.java | 14 ++++++------- .../JpaQueryRewriteIntegrationTests.java | 19 ++++++++++++++++- .../src/test/resources/logback.xml | 9 ++++---- src/main/asciidoc/jpa.adoc | 21 +++++++++++++++++++ 5 files changed, 57 insertions(+), 14 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index af91145928..6b6c2a6602 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -32,9 +32,12 @@ * @author Oliver Gierke * @author Marc Lefrançois * @author Mark Paluch + * @author Greg Turnquist */ public class JpaCountQueryCreator extends JpaQueryCreator { + private boolean distinct; + /** * Creates a new {@link JpaCountQueryCreator}. * @@ -46,6 +49,7 @@ public class JpaCountQueryCreator extends JpaQueryCreator { public JpaCountQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder, ParameterMetadataProvider provider) { super(tree, type, builder, provider); + this.distinct = tree.isDistinct(); } @Override @@ -63,7 +67,7 @@ protected CriteriaQuery complete(@Nullable Predicate predicate } @SuppressWarnings("rawtypes") - private static Expression getCountQuery(CriteriaQuery query, CriteriaBuilder builder, Root root) { - return query.isDistinct() ? builder.countDistinct(root) : builder.count(root); + private Expression getCountQuery(CriteriaQuery query, CriteriaBuilder builder, Root root) { + return distinct ? builder.countDistinct(root) : builder.count(root); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index c23eef9002..8a6527b119 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -18,12 +18,6 @@ import static org.springframework.data.jpa.repository.query.QueryUtils.*; import static org.springframework.data.repository.query.parser.Part.Type.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.stream.Collectors; - import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Expression; @@ -34,6 +28,12 @@ import jakarta.persistence.criteria.Selection; import jakarta.persistence.metamodel.SingularAttribute; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; import org.springframework.data.mapping.PropertyPath; @@ -84,7 +84,7 @@ public JpaQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder CriteriaQuery criteriaQuery = createCriteriaQuery(builder, type); this.builder = builder; - this.query = criteriaQuery.distinct(tree.isDistinct()); + this.query = criteriaQuery.distinct(tree.isDistinct() && !tree.isCountProjection()); this.root = query.from(type.getDomainType()); this.provider = provider; this.returnedType = type; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java index 61d0db240b..37497195e7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java @@ -35,6 +35,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @@ -145,7 +146,21 @@ void nativeQueryShouldHandleRewritesUsingRepositoryRewriter() { entry(SORT, Sort.unsorted().toString())); } - public interface UserRepositoryWithRewriter extends JpaRepository, QueryRewriter { + @Test // GH-1380 + void counting() { + + repository.saveAllAndFlush(List.of( // + new User("Frodo", "Baggins", "ringdude@aol.com"), // + new User("Bilbo", "Baggins", "riddler@hotmail.com"), // + new User("Samwise", "Gamgee", "gardener@gmail.com"))); + + assertThat(repository.count()).isEqualTo(3); + assertThat(repository.countDistinctByLastname("Baggins")).isEqualTo(2); + assertThat(repository.countDistinctByLastname("Gamgee")).isEqualTo(1); + } + + public interface UserRepositoryWithRewriter + extends JpaRepository, QueryRewriter, JpaSpecificationExecutor { @Query(value = "select original_user_alias.* from SD_USER original_user_alias", nativeQuery = true, queryRewriter = TestQueryRewriter.class) @@ -170,6 +185,8 @@ public interface UserRepositoryWithRewriter extends JpaRepository queryRewriter = UserRepositoryWithRewriter.class) List findByNativeQueryUsingRepository(String param); + long countDistinctByLastname(String lastname); + @Override default String rewrite(String query, Sort sort) { return replaceAlias(query, sort); diff --git a/spring-data-jpa/src/test/resources/logback.xml b/spring-data-jpa/src/test/resources/logback.xml index 2bb9893fdc..d7b83cb955 100644 --- a/spring-data-jpa/src/test/resources/logback.xml +++ b/spring-data-jpa/src/test/resources/logback.xml @@ -10,12 +10,13 @@ - - - + + + + - \ No newline at end of file + diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 9d1a7c24bd..125dd50945 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -253,6 +253,27 @@ The following table describes the keywords supported for JPA and what a method c NOTE: `In` and `NotIn` also take any subclass of `Collection` as a parameter as well as arrays or varargs. For other syntactical versions of the same logical operator, check "`<>`". +[WARNING] +==== +`DISTINCT` can be tricky and not always producing the results you expect. +For example, `select distinct u from User u` will produce a complete different result than `select distinct u.lastname from User u`. +In the first case, since you are including `User.id`, nothing will duplicated, hence you'll get the whole table, and it would be of `User` objects. + +However, that latter query would narrow the focus to just `User.lastname` and find all unique last names for that table. +This would also yield a `List` result set instead of a `List result set. + + +`countDistinctByLastname(String lastname)` can also produce unexpected results. +Spring Data JPA will derive `select count(distinct u.id) from User u where u.lastname = ?1`. +Again, since `u.id` won't hit any duplicates, this query will count up all the users that had the binding last name. +Which would the same as `countByLastname(String lastname)`! + +What is the point of this query anyway? To find the number of people with a given last name? To find the number of _distinct_ people with that binding last name? +To find the number of _distinct last names_? (That last one is an entirely different query!) +Using `distinct` sometimes requires writing the query by hand and using `@Query` to best capture the information you seek, since you also may be needing a projection +to capture the result set. +==== + [[jpa.query-methods.named-queries]] === Using JPA Named Queries From 7cb16de9e1f6db97bf9e274721ec197bc6425f39 Mon Sep 17 00:00:00 2001 From: "evgeny.bovykin" Date: Tue, 10 May 2022 13:43:54 +0200 Subject: [PATCH 189/821] Fix debug logging in MergingPersistenceUnitManager. A String.format token was missing, so `mappingFileName` was printed instead of a `persistenceUnitName`, and `persistenceUnitName` wasn't actually printed at all. See #2526. --- .../data/jpa/support/MergingPersistenceUnitManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index f26e1f800b..c729ba812d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -82,7 +82,7 @@ void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui, PersistenceU if (!pui.getMappingFileNames().contains(mappingFileName)) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Adding mapping file to persistence unit %s.", mappingFileName, persistenceUnitName)); + LOG.debug(String.format("Adding mapping file %s to persistence unit %s.", mappingFileName, persistenceUnitName)); } pui.addMappingFileName(mappingFileName); } From 481d1b0a0df9cffa24cb5d2b72d190a7b51c332c Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 12 May 2022 14:38:18 -0500 Subject: [PATCH 190/821] Verify custom finders can handle PageRequest as method parameter. Spring Data Commons patched handling subclasses of special parameter types through spring-projects/spring-data-commons#2626. This commit adds test cases ensuring things work properly with Spring Data JPA. See #2013. --- .../jpa/repository/UserRepositoryTests.java | 26 +++++++++++++++++++ .../jpa/repository/sample/UserRepository.java | 14 +++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) 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 3099c04cb0..4fd24561d0 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 @@ -1709,6 +1709,32 @@ void findByEmptyCollectionOfIntegers() { assertThat(users).hasSize(0); } + @Test // GH-2013 + void findByCollectionWithPageable() { + + flushTestUsers(); + + Page userPage = repository.findByAgeIn(List.of(28, 35), (Pageable) PageRequest.of(0, 2)); + + assertThat(userPage).hasSize(2); + assertThat(userPage.getTotalElements()).isEqualTo(2); + assertThat(userPage.getTotalPages()).isEqualTo(1); + assertThat(userPage.getContent()).containsExactlyInAnyOrder(firstUser, secondUser); + } + + @Test // GH-2013 + void findByCollectionWithPageRequest() { + + flushTestUsers(); + + Page userPage = repository.findByAgeIn(List.of(28, 35), (PageRequest) PageRequest.of(0, 2)); + + assertThat(userPage).hasSize(2); + assertThat(userPage.getTotalElements()).isEqualTo(2); + assertThat(userPage.getTotalPages()).isEqualTo(1); + assertThat(userPage.getContent()).containsExactlyInAnyOrder(firstUser, secondUser); + } + @Test // DATAJPA-606 void findByEmptyArrayOfIntegers() { 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 e5a748e97a..7937474d1e 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 @@ -15,6 +15,9 @@ */ package org.springframework.data.jpa.repository.sample; +import jakarta.persistence.EntityManager; +import jakarta.persistence.QueryHint; + import java.util.Collection; import java.util.Date; import java.util.List; @@ -23,10 +26,8 @@ import java.util.Set; import java.util.stream.Stream; -import jakarta.persistence.EntityManager; -import jakarta.persistence.QueryHint; - import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; @@ -52,6 +53,7 @@ * @author Jeff Sheets * @author Andrey Kovalev * @author JyotirmoyVS + * @author Greg Turnquist */ public interface UserRepository extends JpaRepository, JpaSpecificationExecutor, UserRepositoryCustom { @@ -504,6 +506,12 @@ List findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity // DATAJPA-606 List findByAgeIn(Collection ages); + // GH-2013 + Page findByAgeIn(Collection ages, Pageable pageable); + + // GH-2013 + Page findByAgeIn(Collection ages, PageRequest pageable); + // DATAJPA-606 List queryByAgeIn(Integer[] ages); From 688bf63b86b8f3c974f86bb7f4de37b33fe5e0c7 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 13 May 2022 10:43:19 +0200 Subject: [PATCH 191/821] Prepare 3.0 M4 (2022.0.0). See #2471 --- pom.xml | 646 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 417 insertions(+), 229 deletions(-) diff --git a/pom.xml b/pom.xml index ca8ee5b81e..4429d32f0e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,241 +1,429 @@ - - 4.0.0 - - org.springframework.data - spring-data-jpa-parent - 3.0.0-SNAPSHOT - pom - - Spring Data JPA Parent - Parent module for Spring Data JPA repositories. - https://spring.io/projects/spring-data-jpa - - scm:git:git://github.com:spring-projects/spring-data-jpa.git - scm:git:git@github.com:spring-projects/spring-data-jpa.git - https://github.com/spring-projects/spring-data-jpa - - - https://github.com/spring-projects/spring-data-jpa/issues - - - - org.springframework.data.build - spring-data-parent - 3.0.0-SNAPSHOT - - - - 16 + + 4.0.0 + + org.springframework.data + + spring-data-jpa-parent + + 3.0.0-SNAPSHOT + + pom + + Spring Data JPA Parent + + Parent module for Spring Data JPA repositories. + + https://spring.io/projects/spring-data-jpa + + + + scm:git:git://github.com:spring-projects/spring-data-jpa.git + + scm:git:git@github.com:spring-projects/spring-data-jpa.git + + https://github.com/spring-projects/spring-data-jpa + + + + + + https://github.com/spring-projects/spring-data-jpa/issues + + + + + + org.springframework.data.build + + spring-data-parent + + 3.0.0-M4 + + + + + + 16 + - - 3.0.2 - 5.6.0.Final - 4.3 - 8.0.23 - 42.2.19 - 3.0.0-SNAPSHOT - 0.10.3 - - org.hibernate - - reuseReports - - - + + 3.0.2 + + 5.6.0.Final + + 4.3 + + 8.0.23 + + 42.2.19 + + 3.0.0-M4 + + 0.10.3 + + org.hibernate + + reuseReports + + + - spring-data-envers - spring-data-jpa - spring-data-jpa-distribution - - - - - - all-dbs - - - - org.apache.maven.plugins - maven-surefire-plugin - - - mysql-test - test - - test - - - - **/MySql*IntegrationTests.java - - - - - postgres-test - test - - test - - - - **/Postgres*IntegrationTests.java - - - - - - - - - - hibernate-next - - 6.0.0-SNAPSHOT - - - - jboss - https://repository.jboss.org/nexus/content/repositories/public - - - - - - - - - - org.testcontainers - testcontainers-bom - ${testcontainers} - pom - import - - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - org.springframework - spring-instrument - ${spring} - runtime - - - - - - default-test - - - **/* - - - - - unit-test - - test - - test - - - **/*UnitTests.java - - - - - integration-test - - test - - test - - - **/*IntegrationTests.java - **/*Tests.java - - - **/*UnitTests.java - **/OpenJpa* - **/EclipseLink* - **/MySql* - **/Postgres* - - + + spring-data-envers + + spring-data-jpa + + spring-data-jpa-distribution + + + + + + + + all-dbs + + + + + + + + org.apache.maven.plugins + + maven-surefire-plugin + + + + + + mysql-test + + test + + + + test + + + + + + + + **/MySql*IntegrationTests.java + + + + + + + + + + postgres-test + + test + + + + test + + + + + + + + **/Postgres*IntegrationTests.java + + + + + + + + + + + + + + + + + + + + hibernate-next + + + + 6.0.0-SNAPSHOT + + + + + + + + jboss + + https://repository.jboss.org/nexus/content/repositories/public + + + + + + + + + + + + + + + + org.testcontainers + + testcontainers-bom + + ${testcontainers} + + pom + + import + + + + + + + + + + + + + + org.apache.maven.plugins + + maven-surefire-plugin + + + + + + org.springframework + + spring-instrument + + ${spring} + + runtime + + + + + + + + + + + + default-test + + + + + + **/* + + + + + + + + + + unit-test + + + + test + + + + test + + + + + + **/*UnitTests.java + + + + + + + + + + integration-test + + + + test + + + + test + + + + + + **/*IntegrationTests.java + + **/*Tests.java + + + + + + **/*UnitTests.java + + **/OpenJpa* + + **/EclipseLink* + + **/MySql* + + **/Postgres* + + + + -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} - - - - eclipselink-test - - test - - test - - - **/EclipseLink*Tests.java - - + + + + + + + + eclipselink-test + + + + test + + + + test + + + + + + **/EclipseLink*Tests.java + + + + -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar - - - - - - - maven-compiler-plugin - - - java-test-compile - test-compile - - testCompile - - - false - - - - - - - - - - - spring-libs-snapshot - https://repo.spring.io/libs-snapshot - - - - - - spring-plugins-release - https://repo.spring.io/plugins-release - - - spring-libs-milestone - https://repo.spring.io/libs-milestone - - - spring-libs-snapshot - https://repo.spring.io/libs-snapshot - - - + + + + + + + + + + + + maven-compiler-plugin + + + + + + java-test-compile + + test-compile + + + + testCompile + + + + + + false + + + + + + + + + + + + + + + + + + spring-libs-milestone + + https://repo.spring.io/libs-milestone + + + + + + + + + + spring-plugins-release + + https://repo.spring.io/plugins-release + + + + + + spring-libs-milestone + + https://repo.spring.io/libs-milestone + + + + + + spring-libs-snapshot + + https://repo.spring.io/libs-snapshot + + + + + From 5b4a4d949026a970e2aa7ce2f18daaaed28425c7 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 13 May 2022 10:43:58 +0200 Subject: [PATCH 192/821] Release version 3.0 M4 (2022.0.0). See #2471 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 4429d32f0e..e037b2a778 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M4 pom diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 8e2de43f59..7a320279a4 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-SNAPSHOT + 3.0.0-M4 org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M4 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index bba8571672..7fcb17ce44 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M4 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index ba9220cc2a..7c5ef3548b 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-SNAPSHOT + 3.0.0-M4 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M4 ../pom.xml From 95201377d1cc858dc57e4821930057f53b0daa3c Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 13 May 2022 10:53:23 +0200 Subject: [PATCH 193/821] Prepare next development iteration. See #2471 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index e037b2a778..4429d32f0e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ spring-data-jpa-parent - 3.0.0-M4 + 3.0.0-SNAPSHOT pom diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 7a320279a4..8e2de43f59 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-M4 + 3.0.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.0.0-M4 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 7fcb17ce44..bba8571672 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M4 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 7c5ef3548b..ba9220cc2a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-M4 + 3.0.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M4 + 3.0.0-SNAPSHOT ../pom.xml From 665a905e5d623f467706387ee82a7550e5172b2d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 13 May 2022 10:53:25 +0200 Subject: [PATCH 194/821] After release cleanups. See #2471 --- pom.xml | 638 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 424 insertions(+), 214 deletions(-) diff --git a/pom.xml b/pom.xml index 4429d32f0e..c97e72be6d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,429 +1,639 @@ - + + 4.0.0 - + + org.springframework.data - + + spring-data-jpa-parent - + + 3.0.0-SNAPSHOT - + + pom - + + Spring Data JPA Parent - + + Parent module for Spring Data JPA repositories. - + + https://spring.io/projects/spring-data-jpa - + + - + + scm:git:git://github.com:spring-projects/spring-data-jpa.git - + + scm:git:git@github.com:spring-projects/spring-data-jpa.git - + + https://github.com/spring-projects/spring-data-jpa - + + - + + - + + https://github.com/spring-projects/spring-data-jpa/issues - + + - + + - + + org.springframework.data.build - + + spring-data-parent - - 3.0.0-M4 - + + + 3.0.0-SNAPSHOT + + - + + - + + 16 - + + - + + 3.0.2 - + + 5.6.0.Final - + + 4.3 - + + 8.0.23 - + + 42.2.19 - - 3.0.0-M4 - + + + 3.0.0-SNAPSHOT + + 0.10.3 - + + org.hibernate - + + reuseReports - + + - + + - + + spring-data-envers - + + spring-data-jpa - + + spring-data-jpa-distribution - + + - + + - + + - + + all-dbs - + + - + + - + + - + + org.apache.maven.plugins - + + maven-surefire-plugin - + + - + + - + + mysql-test - + + test - + + - + + test - + + - + + - + + - + + **/MySql*IntegrationTests.java - + + - + + - + + - + + - + + postgres-test - + + test - + + - + + test - + + - + + - + + - + + **/Postgres*IntegrationTests.java - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + hibernate-next - + + - + + 6.0.0-SNAPSHOT - + + - + + - + + - + + jboss - + + https://repository.jboss.org/nexus/content/repositories/public - + + - + + - + + - + + - + + - + + - + + - + + org.testcontainers - + + testcontainers-bom - + + ${testcontainers} - + + pom - + + import - + + - + + - + + - + + - + + - + + - + + org.apache.maven.plugins - + + maven-surefire-plugin - + + - + + - + + org.springframework - + + spring-instrument - + + ${spring} - + + runtime - + + - + + - + + - + + - + + - + + default-test - + + - + + - + + **/* - + + - + + - + + - + + - + + unit-test - + + - + + test - + + - + + test - + + - + + - + + **/*UnitTests.java - + + - + + - + + - + + - + + integration-test - + + - + + test - + + - + + test - + + - + + - + + **/*IntegrationTests.java - + + **/*Tests.java - + + - + + - + + **/*UnitTests.java - + + **/OpenJpa* - + + **/EclipseLink* - + + **/MySql* - + + **/Postgres* - + + - + + -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} - + + - + + - + + - + + eclipselink-test - + + - + + test - + + - + + test - + + - + + - + + **/EclipseLink*Tests.java - + + - + + -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar - + + - + + - + + - + + - + + - + + maven-compiler-plugin - + + - + + - + + java-test-compile - + + test-compile - + + - + + testCompile - + + - + + - + + false - + + - + + - + + - + + - + + - + + - + + - + + - - spring-libs-milestone - - https://repo.spring.io/libs-milestone - + + + spring-libs-snapshot + + + https://repo.spring.io/libs-snapshot + + - + + - + + - + + - + + spring-plugins-release - + + https://repo.spring.io/plugins-release - + + - + + - + + spring-libs-milestone - + + https://repo.spring.io/libs-milestone - + + - + + - + + spring-libs-snapshot - + + https://repo.spring.io/libs-snapshot - + + - - + + + + From fed7bf8036788d993db3f1a2e9f110107eb88db7 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 13 May 2022 12:54:00 +0200 Subject: [PATCH 195/821] Fix pom.xml formatting. This commit reverts formatting changes introduced via 688bf63b86b8f3c974f86bb7f4de37b33fe5e0c7. See #2471 --- pom.xml | 854 +++++++++++++++----------------------------------------- 1 file changed, 228 insertions(+), 626 deletions(-) diff --git a/pom.xml b/pom.xml index c97e72be6d..ca8ee5b81e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,639 +1,241 @@ - - - 4.0.0 - - - org.springframework.data - - - spring-data-jpa-parent - - - 3.0.0-SNAPSHOT - - - pom - - - Spring Data JPA Parent - - - Parent module for Spring Data JPA repositories. - - - https://spring.io/projects/spring-data-jpa - - - - - - scm:git:git://github.com:spring-projects/spring-data-jpa.git - - - scm:git:git@github.com:spring-projects/spring-data-jpa.git - - - https://github.com/spring-projects/spring-data-jpa - - - - - - - - - https://github.com/spring-projects/spring-data-jpa/issues - - - - - - - - - org.springframework.data.build - - - spring-data-parent - - - 3.0.0-SNAPSHOT - - - - - - - - - 16 - - + + 4.0.0 + + org.springframework.data + spring-data-jpa-parent + 3.0.0-SNAPSHOT + pom + + Spring Data JPA Parent + Parent module for Spring Data JPA repositories. + https://spring.io/projects/spring-data-jpa + + scm:git:git://github.com:spring-projects/spring-data-jpa.git + scm:git:git@github.com:spring-projects/spring-data-jpa.git + https://github.com/spring-projects/spring-data-jpa + + + https://github.com/spring-projects/spring-data-jpa/issues + + + + org.springframework.data.build + spring-data-parent + 3.0.0-SNAPSHOT + + + + 16 - - - 3.0.2 - - - 5.6.0.Final - - - 4.3 - - - 8.0.23 - - - 42.2.19 - - - 3.0.0-SNAPSHOT - - - 0.10.3 - - - org.hibernate - - - reuseReports - - - - - + + 3.0.2 + 5.6.0.Final + 4.3 + 8.0.23 + 42.2.19 + 3.0.0-SNAPSHOT + 0.10.3 + + org.hibernate + + reuseReports + + + - - - spring-data-envers - - - spring-data-jpa - - - spring-data-jpa-distribution - - - - - - - - - - - - all-dbs - - - - - - - - - - - - org.apache.maven.plugins - - - maven-surefire-plugin - - - - - - - - - mysql-test - - - test - - - - - - test - - - - - - - - - - - - **/MySql*IntegrationTests.java - - - - - - - - - - - - - - - postgres-test - - - test - - - - - - test - - - - - - - - - - - - **/Postgres*IntegrationTests.java - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - hibernate-next - - - - - - 6.0.0-SNAPSHOT - - - - - - - - - - - - jboss - - - https://repository.jboss.org/nexus/content/repositories/public - - - - - - - - - - - - - - - - - - - - - - - - org.testcontainers - - - testcontainers-bom - - - ${testcontainers} - - - pom - - - import - - - - - - - - - - - - - - - - - - - - - org.apache.maven.plugins - - - maven-surefire-plugin - - - - - - - - - org.springframework - - - spring-instrument - - - ${spring} - - - runtime - - - - - - - - - - - - - - - - - - default-test - - - - - - - - - **/* - - - - - - - - - - - - - - - unit-test - - - - - - test - - - - - - test - - - - - - - - - **/*UnitTests.java - - - - - - - - - - - - - - - integration-test - - - - - - test - - - - - - test - - - - - - - - - **/*IntegrationTests.java - - - **/*Tests.java - - - - - - - - - **/*UnitTests.java - - - **/OpenJpa* - - - **/EclipseLink* - - - **/MySql* - - - **/Postgres* - - - - - - + spring-data-envers + spring-data-jpa + spring-data-jpa-distribution + + + + + + all-dbs + + + + org.apache.maven.plugins + maven-surefire-plugin + + + mysql-test + test + + test + + + + **/MySql*IntegrationTests.java + + + + + postgres-test + test + + test + + + + **/Postgres*IntegrationTests.java + + + + + + + + + + hibernate-next + + 6.0.0-SNAPSHOT + + + + jboss + https://repository.jboss.org/nexus/content/repositories/public + + + + + + + + + + org.testcontainers + testcontainers-bom + ${testcontainers} + pom + import + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.springframework + spring-instrument + ${spring} + runtime + + + + + + default-test + + + **/* + + + + + unit-test + + test + + test + + + **/*UnitTests.java + + + + + integration-test + + test + + test + + + **/*IntegrationTests.java + **/*Tests.java + + + **/*UnitTests.java + **/OpenJpa* + **/EclipseLink* + **/MySql* + **/Postgres* + + -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} - - - - - - - - - - - - eclipselink-test - - - - - - test - - - - - - test - - - - - - - - - **/EclipseLink*Tests.java - - - - - - + + + + eclipselink-test + + test + + test + + + **/EclipseLink*Tests.java + + -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar - - - - - - - - - - - - - - - - - - maven-compiler-plugin - - - - - - - - - java-test-compile - - - test-compile - - - - - - testCompile - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - spring-libs-snapshot - - - https://repo.spring.io/libs-snapshot - - - - - - - - - - - - - - - spring-plugins-release - - - https://repo.spring.io/plugins-release - - - - - - - - - spring-libs-milestone - - - https://repo.spring.io/libs-milestone - - - - - - - - - spring-libs-snapshot - - - https://repo.spring.io/libs-snapshot - - - - - - - + + + + + + + maven-compiler-plugin + + + java-test-compile + test-compile + + testCompile + + + false + + + + + + + + + + + spring-libs-snapshot + https://repo.spring.io/libs-snapshot + + + + + + spring-plugins-release + https://repo.spring.io/plugins-release + + + spring-libs-milestone + https://repo.spring.io/libs-milestone + + + spring-libs-snapshot + https://repo.spring.io/libs-snapshot + + From 76412cce9c45ba882708845eb06fbe70d55e0038 Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 16 May 2022 11:35:36 -0700 Subject: [PATCH 196/821] Remove Docker Registry login. Closes #2533. --- Jenkinsfile | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index faec27db1f..4b9e1be19b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -36,12 +36,10 @@ pipeline { } steps { script { - docker.withRegistry(p['docker.registry'], p['docker.credentials']) { - docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { - sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}" - sh 'PROFILE=all-dbs ci/test.sh' - sh "ci/clean.sh" - } + docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { + sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}" + sh 'PROFILE=all-dbs ci/test.sh' + sh "ci/clean.sh" } } } @@ -66,17 +64,15 @@ pipeline { steps { script { - docker.withRegistry(p['docker.registry'], p['docker.credentials']) { - docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + - '-Dartifactory.server=https://repo.spring.io ' + - "-Dartifactory.username=${ARTIFACTORY_USR} " + - "-Dartifactory.password=${ARTIFACTORY_PSW} " + - "-Dartifactory.staging-repository=libs-snapshot-local " + - "-Dartifactory.build-name=spring-data-jpa " + - "-Dartifactory.build-number=${BUILD_NUMBER} " + - '-Dmaven.test.skip=true clean deploy -U -B' - } + docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + + '-Dartifactory.server=https://repo.spring.io ' + + "-Dartifactory.username=${ARTIFACTORY_USR} " + + "-Dartifactory.password=${ARTIFACTORY_PSW} " + + "-Dartifactory.staging-repository=libs-snapshot-local " + + "-Dartifactory.build-name=spring-data-jpa " + + "-Dartifactory.build-number=${BUILD_NUMBER} " + + '-Dmaven.test.skip=true clean deploy -U -B' } } } From 13ae7e586a801040e33a573b2d70fd4ac348f6d5 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 17 May 2022 17:42:01 +0200 Subject: [PATCH 197/821] Fix potential NullPointerException in ParameterMetadataProvider. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now guard against null values returned from ParameterExpression.getJavaType() before inspecting the value further as it turns out that Class.isAssignableFrom(…) chokes on null values being passed. Looks like Hibernate six returns null in scenarios it previously didn't. --- .../data/jpa/repository/query/ParameterMetadataProvider.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 9eeb694a63..e214ffe4c6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -237,6 +237,10 @@ public Object prepare(Object value) { Class expressionType = expression.getJavaType(); + if (expressionType == null) { + return value; + } + if (String.class.equals(expressionType)) { switch (type) { From 6be9b1460e45f4f5bbdfdbf6139a1651e7f75830 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 18 May 2022 09:01:07 -0500 Subject: [PATCH 198/821] JpaQueryLookupStrategy shouldn't use exceptions for flow control. By using exceptions for flow control, other critical exceptions are getting masked. The lack of a resolvable query should instead leverage some sort of null value object. Closes #2018. --- .../query/JpaQueryLookupStrategy.java | 42 ++++++++++++++----- .../data/jpa/domain/sample/User.java | 8 ++-- .../JpaQueryLookupStrategyUnitTests.java | 27 ++++++++++++ 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 5fba2b6a47..4df1c9ab8c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -28,6 +28,7 @@ import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; +import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.lang.Nullable; @@ -47,6 +48,12 @@ public final class JpaQueryLookupStrategy { private static final Log LOG = LogFactory.getLog(JpaQueryLookupStrategy.class); + /** + * A null-value instance used to signal if no declared query could be found. It checks many different formats before + * falling through to this value object. + */ + private static final RepositoryQuery NO_QUERY = new NoQuery(); + /** * Private constructor to prevent instantiation. */ @@ -172,12 +179,9 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter quer RepositoryQuery query = NamedQuery.lookupFrom(method, em); - if (null != query) { - return query; - } - - throw new IllegalStateException( - String.format("Did neither find a NamedQuery nor an annotated query for method %s!", method)); + return query != null // + ? query // + : NO_QUERY; } @Nullable @@ -245,11 +249,13 @@ public CreateIfNotFoundQueryLookupStrategy(EntityManager em, JpaQueryMethodFacto protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em, NamedQueries namedQueries) { - try { - return lookupStrategy.resolveQuery(method, queryRewriter, em, namedQueries); - } catch (IllegalStateException e) { - return createStrategy.resolveQuery(method, queryRewriter, em, namedQueries); + RepositoryQuery lookupQuery = lookupStrategy.resolveQuery(method, queryRewriter, em, namedQueries); + + if (lookupQuery != NO_QUERY) { + return lookupQuery; } + + return createStrategy.resolveQuery(method, queryRewriter, em, namedQueries); } } @@ -284,4 +290,20 @@ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key)); } } + + /** + * A null value type that represents the lack of a defined query. + */ + static class NoQuery implements RepositoryQuery { + + @Override + public Object execute(Object[] parameters) { + throw new IllegalStateException("NoQuery should not be executed!"); + } + + @Override + public QueryMethod getQueryMethod() { + throw new IllegalStateException("NoQuery does not have a QueryMethod!"); + } + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java index 350724bf8f..ebf3b7f0a0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -15,13 +15,13 @@ */ package org.springframework.data.jpa.domain.sample; +import jakarta.persistence.*; + import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Set; -import jakarta.persistence.*; - /** * Domain class representing a person emphasizing the use of {@code AbstractEntity}. No declaration of an id is * required. The id is typed by the parameterizable superclass. @@ -32,6 +32,7 @@ * @author Jens Schauder * @author Jeff Sheets * @author JyotirmoyVS + * @author Greg Turnquist */ @Entity @NamedEntityGraphs({ @NamedEntityGraph(name = "User.overview", attributeNodes = { @NamedAttributeNode("roles") }), @@ -91,7 +92,8 @@ @Table(name = "SD_User") public class User { - @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; + @Id + @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private String firstname; private String lastname; private int age; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index 0ba5d98e93..76cc0cf132 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -169,6 +169,30 @@ void prefersDeclaredQuery() throws Exception { assertThat(repositoryQuery).isInstanceOf(AbstractStringBasedJpaQuery.class); } + @Test // GH-2018 + void namedQueryWithSortShouldThrowIllegalStateException() throws NoSuchMethodException { + + QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); + + Method method = UserRepository.class.getMethod("customNamedQuery", String.class, Sort.class); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); + + assertThatIllegalStateException() + .isThrownBy(() -> strategy.resolveQuery(method, metadata, projectionFactory, namedQueries)) + .withMessageContaining( + "is backed by a NamedQuery and must not contain a sort parameter as we cannot modify the query! Use @Query instead!"); + } + + @Test // GH-2018 + void noQueryShouldNotBeInvoked() { + + RepositoryQuery query = new JpaQueryLookupStrategy.NoQuery(); + + assertThatIllegalStateException().isThrownBy(() -> query.execute(new Object[] {})); + assertThatIllegalStateException().isThrownBy(() -> query.getQueryMethod()); + } + interface UserRepository extends Repository { @Query("something absurd") @@ -185,5 +209,8 @@ interface UserRepository extends Repository { @Query(value = "something absurd", name = "my-query-name") User annotatedQueryWithQueryAndQueryName(); + + // This is a named query with Sort parameter, which isn't supported + List customNamedQuery(String firstname, Sort sort); } } From 9fc36ef82ae782a5e7472de9816d85b04a1e01a8 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 16 May 2022 10:48:59 -0500 Subject: [PATCH 199/821] Check for page offsets exceeding Integer.MAX_VALUE. Instead of relying on the JPA provider to handle page offsets exceeding Integer.MAX_VALUE, do the check inside Spring Data JPA and provide a more meaningful error message. Closes #2502. --- .../jpa/repository/query/ParameterBinder.java | 3 +- .../FetchableFluentQueryByExample.java | 16 +++---- .../FetchableFluentQueryBySpecification.java | 3 +- .../support/SimpleJpaRepository.java | 3 +- .../data/jpa/support/PageableUtils.java | 47 +++++++++++++++++++ 5 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 6ffabbdb33..49d129cd0f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -18,6 +18,7 @@ import jakarta.persistence.Query; import org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling; +import org.springframework.data.jpa.support.PageableUtils; import org.springframework.util.Assert; /** @@ -99,7 +100,7 @@ Query bindAndPrepare(Query query, QueryParameterSetter.QueryMetadata metadata, return query; } - query.setFirstResult((int) accessor.getPageable().getOffset()); + query.setFirstResult(PageableUtils.getOffsetAsInteger(accessor.getPageable())); query.setMaxResults(accessor.getPageable().getPageSize()); return query; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index f2f0d7c55f..315244a3a1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -15,6 +15,9 @@ */ package org.springframework.data.jpa.repository.support; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -22,9 +25,6 @@ import java.util.function.Function; import java.util.stream.Stream; -import jakarta.persistence.EntityManager; -import jakarta.persistence.TypedQuery; - import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; @@ -32,6 +32,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.query.EscapeCharacter; +import org.springframework.data.jpa.support.PageableUtils; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.util.Assert; @@ -66,8 +67,7 @@ public FetchableFluentQueryByExample(Example example, Function example, Class entityType, Class returnType, Sort sort, Collection properties, Function> finder, Function, Long> countOperation, - Function, Boolean> existsOperation, - EntityManager entityManager, EscapeCharacter escapeCharacter) { + Function, Boolean> existsOperation, EntityManager entityManager, EscapeCharacter escapeCharacter) { super(returnType, sort, properties, entityType); this.example = example; @@ -83,8 +83,8 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null!"); - return new FetchableFluentQueryByExample<>(example, entityType, resultType, this.sort.and(sort), properties, - finder, countOperation, existsOperation, entityManager, escapeCharacter); + return new FetchableFluentQueryByExample<>(example, entityType, resultType, this.sort.and(sort), properties, finder, + countOperation, existsOperation, entityManager, escapeCharacter); } @Override @@ -168,7 +168,7 @@ private Page readPage(Pageable pageable) { TypedQuery pagedQuery = createSortedAndProjectedQuery(); if (pageable.isPaged()) { - pagedQuery.setFirstResult((int) pageable.getOffset()); + pagedQuery.setFirstResult(PageableUtils.getOffsetAsInteger(pageable)); pagedQuery.setMaxResults(pageable.getPageSize()); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java index b88111855f..f8061ec5d2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java @@ -31,6 +31,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.support.PageableUtils; import org.springframework.data.repository.query.FluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.util.Assert; @@ -170,7 +171,7 @@ private Page readPage(Pageable pageable) { TypedQuery pagedQuery = createSortedAndProjectedQuery(); if (pageable.isPaged()) { - pagedQuery.setFirstResult((int) pageable.getOffset()); + pagedQuery.setFirstResult(PageableUtils.getOffsetAsInteger(pageable)); pagedQuery.setMaxResults(pageable.getPageSize()); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 4dd01244fb..d47fd9883f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -55,6 +55,7 @@ import org.springframework.data.jpa.repository.query.EscapeCharacter; import org.springframework.data.jpa.repository.query.QueryUtils; import org.springframework.data.jpa.repository.support.QueryHints.NoHints; +import org.springframework.data.jpa.support.PageableUtils; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.data.util.ProxyUtils; @@ -650,7 +651,7 @@ protected Page readPage(TypedQuery query, final Class dom @Nullable Specification spec) { if (pageable.isPaged()) { - query.setFirstResult((int) pageable.getOffset()); + query.setFirstResult(PageableUtils.getOffsetAsInteger(pageable)); query.setMaxResults(pageable.getPageSize()); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java new file mode 100644 index 0000000000..b9df082b06 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.support; + +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Pageable; + +/** + * Provide a set of utility methods to support interfacing {@link Pageable}s. + * + * @author Greg Turnquist + * @since 3.0 + */ +public final class PageableUtils { + + private PageableUtils() { + throw new IllegalStateException("Cannot instantiate a utility class!"); + } + + /** + * Convert a {@link Pageable}'s offset value from {@link Long} to {@link Integer} to support JPA spec methods. + * + * @param pageable + * @return integer + */ + public static int getOffsetAsInteger(Pageable pageable) { + + if (pageable.getOffset() > Integer.MAX_VALUE) { + throw new InvalidDataAccessApiUsageException("Page offset exceeds Integer.MAX_VALUE (" + Integer.MAX_VALUE + ")"); + } + + return Math.toIntExact(pageable.getOffset()); + } +} From 2b6715a6e43360126e0fd0fa9abfae4eccd0f276 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 18 May 2022 17:47:54 -0500 Subject: [PATCH 200/821] Polishing. See #2502. --- .../org/springframework/data/jpa/support/PageableUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java index b9df082b06..932879629c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java @@ -19,7 +19,7 @@ import org.springframework.data.domain.Pageable; /** - * Provide a set of utility methods to support interfacing {@link Pageable}s. + * Provide a set of utility methods to support {@link Pageable}s. * * @author Greg Turnquist * @since 3.0 From e3439f34408faea367c8bf9705c176b136339562 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 19 May 2022 14:37:20 -0500 Subject: [PATCH 201/821] Better parse 'order by' clauses to handle subqueries. By properly parsing "order by" clauses, Spring Data JPA can apply sorting parameters to the end of queries in the event of complex queries that involve nested subselects. Closes ##2496, #2522, #2537, #2045. --- .../data/jpa/repository/query/QueryUtils.java | 4 +- .../repository/query/QueryUtilsUnitTests.java | 73 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 32410b90da..c39eb85da5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -113,8 +113,8 @@ public abstract class QueryUtils { private static final String EQUALS_CONDITION_STRING = "%s.%s = :%s"; private static final Pattern ORDER_BY = Pattern.compile("(order\\s+by\\s+)", CASE_INSENSITIVE); - private static final Pattern ORDER_BY_IN_WINDOW_OR_SUBSELECT = Pattern - .compile("(\\(\\s*[a-z0-9 ,.*]*order\\s+by\\s+[a-z0-9 ,.]*\\s*\\))", CASE_INSENSITIVE); + private static final Pattern ORDER_BY_IN_WINDOW_OR_SUBSELECT = Pattern.compile("\\([\\s\\S]*order\\s+by\\s[\\s\\S]*\\)", + CASE_INSENSITIVE); private static final Pattern NAMED_PARAMETER = Pattern.compile(COLON_NO_DOUBLE_COLON + IDENTIFIER + "|#" + IDENTIFIER, CASE_INSENSITIVE); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 1adc41c7f3..5b65b647f0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -733,4 +733,77 @@ void countQueryUsesCorrectVariable() { assertThat(countQueryFor) .isEqualTo("select count(us) FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); } + + @Test // GH-2496, GH-2522, GH-2537, GH-2045 + void orderByShouldWorkWithSubSelectStatements() { + + Sort sort = Sort.by(Order.desc("age")); + + assertThat(QueryUtils.applySorting("SELECT\n" // + + " foo_bar.*\n" // + + "FROM\n" // + + " foo foo\n" // + + "INNER JOIN\n" // + + " foo_bar_dnrmv foo_bar ON\n" // + + " foo_bar.foo_id = foo.foo_id\n" // + + "INNER JOIN\n" // + + " (\n" // + + " SELECT\n" // + + " foo_bar_action.*,\n" // + + " RANK() OVER (PARTITION BY \"foo_bar_action\".attributes->>'baz' ORDER BY \"foo_bar_action\".attributes->>'qux' DESC) AS ranking\n" // + + " FROM\n" // + + " foo_bar_action\n" // + + " WHERE\n" // + + " foo_bar_action.deleted_ts IS NULL)\n" // + + " foo_bar_action ON\n" // + + " foo_bar.foo_bar_id = foo_bar_action.foo_bar_id\n" // + + " AND ranking = 1\n" // + + "INNER JOIN\n" // + + " bar bar ON\n" // + + " foo_bar.bar_id = bar.bar_id\n" // + + "INNER JOIN\n" // + + " bar_metadata bar_metadata ON\n" // + + " bar.bar_metadata_key = bar_metadata.bar_metadata_key\n" // + + "WHERE\n" // + + " foo.tenant_id =:tenantId\n" // + + "AND (foo.attributes ->> :serialNum IN (:serialNumValue))", sort)).endsWith("order by foo.age desc"); + + assertThat(QueryUtils.applySorting("select r " // + + "From DataRecord r " // + + "where " // + + " ( " // + + " r.adusrId = :userId " // + + " or EXISTS( select 1 FROM DataRecordDvsRight dr WHERE dr.adusrId = :userId AND dr.dataRecord = r ) " // + + ")", sort)).endsWith("order by r.age desc"); + + assertThat(QueryUtils.applySorting("select distinct u " // + + "from FooBar u " // + + "where [REDACTED] " // + + "and (" // + + " not exists (" // + + " from FooBarGroup group " // + + " where group in :excludedGroups " // + + " and group in elements(u.groups)" // + + " )" // + + ")", sort)).endsWith("order by u.age desc"); + + assertThat(QueryUtils.applySorting("SELECT i " // + + "FROM Item i " // + + "FETCH ALL PROPERTIES \" " // + + "+ \"WHERE i.id IN (\" " // + + "+ \"SELECT max(i2.id) FROM Item i2 \" " // + + "+ \"WHERE i2.field.id = :fieldId \" " // + + "+ \"GROUP BY i2.field.id, i2.version)", sort)).endsWith("order by i.age desc"); + + assertThat(QueryUtils.applySorting("select \n" // + + " f.id,\n" // + + " (\n" // + + " select timestamp from bar\n" // + + " where date(bar.timestamp) > '2022-05-21'\n" // + + " and bar.foo_id = f.id \n" // + + " order by date(bar.timestamp) desc\n" // + + " limit 1\n" // + + ") as timestamp\n" // + + "from foo f", sort)).endsWith("order by f.age desc"); + } } From f5ddb7aa32b39c5ce7bf5a301f19f7556b31c275 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 19 May 2022 17:04:30 -0500 Subject: [PATCH 202/821] Fix order by clauses for functions with positional and named arguments. Related: #2045. Closes #425. --- .../data/jpa/repository/query/QueryUtils.java | 3 ++- .../data/jpa/repository/UserRepositoryTests.java | 11 +++++++++++ .../data/jpa/repository/sample/UserRepository.java | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index c39eb85da5..2a422e870f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -77,6 +77,7 @@ * @author Diego Krupitza * @author Jędrzej Biedrzycki * @author Darin Manica + * @author Simon Paradies */ public abstract class QueryUtils { @@ -182,7 +183,7 @@ public abstract class QueryUtils { builder = new StringBuilder(); // any function call including parameters within the brackets - builder.append("\\w+\\s*\\([\\w\\.,\\s'=]+\\)"); + builder.append("\\w+\\s*\\([\\w\\.,\\s'=:\\\\?]+\\)"); // the potential alias builder.append("\\s+[as|AS]+\\s+(([\\w\\.]+))"); 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 4fd24561d0..d637fe9525 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 @@ -95,6 +95,7 @@ * @author Greg Turnquist * @author Diego Krupitza * @author Daniel Shuy + * @author Simon Paradies */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @@ -2877,6 +2878,16 @@ void deleteWithSpec() { assertThat(initialCount - finalCount).isEqualTo(3L); } + @Test // GH-2045, GH-425 + public void correctlyBuildSortClauseWhenSortingByFunctionAliasAndFunctionContainsPositionalParameters() { + repository.findAllAndSortByFunctionResultPositionalParameter("prefix", "suffix", Sort.by("idWithPrefixAndSuffix")); + } + + @Test // GH-2045, GH-425 + public void correctlyBuildSortClauseWhenSortingByFunctionAliasAndFunctionContainsNamedParameters() { + repository.findAllAndSortByFunctionResultNamedParameter("prefix", "suffix", Sort.by("idWithPrefixAndSuffix")); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); 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 7937474d1e..c7ceb0e555 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 @@ -54,6 +54,7 @@ * @author Andrey Kovalev * @author JyotirmoyVS * @author Greg Turnquist + * @author Simon Paradies */ public interface UserRepository extends JpaRepository, JpaSpecificationExecutor, UserRepositoryCustom { @@ -625,6 +626,17 @@ Page findAllOrderedBySpecialNameMultipleParams(@Param("name") String name, // GH-2408 List findAllInterfaceProjectedBy(); + // GH-2045, GH-425 + @Query("select concat(?1,u.id,?2) as idWithPrefixAndSuffix from #{#entityName} u") + List findAllAndSortByFunctionResultPositionalParameter( + @Param("positionalParameter1") String positionalParameter1, + @Param("positionalParameter2") String positionalParameter2, Sort sort); + + // GH-2045, GH-425 + @Query("select concat(:namedParameter1,u.id,:namedParameter2) as idWithPrefixAndSuffix from #{#entityName} u") + List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter1") String namedParameter1, + @Param("namedParameter2") String namedParameter2, Sort sort); + interface RolesAndFirstname { String getFirstname(); From 724538999a73cfd5077b35ab03d1fa5e253a9c9f Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 12 Apr 2022 16:13:51 -0500 Subject: [PATCH 203/821] Make getPredicate return null when there are no predicates. This does integration testing with both EclipseLink and Hibernate, verifying that queries are run properly. I also inspected the generated queries in the debugger, verifying the "where true=1" was no longer generated. Closes #2282. --- .../QueryByExamplePredicateBuilder.java | 20 +++-- ...eryByExamplePredicateBuilderUnitTests.java | 42 ++++----- ...yByExampleEclipseLinkIntegrationTests.java | 86 +++++++++++++++++++ ...eryByExampleHibernateIntegrationTests.java | 86 +++++++++++++++++++ .../jpa/repository/sample/RoleRepository.java | 7 +- 5 files changed, 205 insertions(+), 36 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java index 6e45701f98..fc8732ec59 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java @@ -15,12 +15,6 @@ */ package org.springframework.data.jpa.convert; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.From; @@ -32,6 +26,12 @@ import jakarta.persistence.metamodel.ManagedType; import jakarta.persistence.metamodel.SingularAttribute; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; @@ -76,8 +76,9 @@ public class QueryByExamplePredicateBuilder { * @param root must not be {@literal null}. * @param cb must not be {@literal null}. * @param example must not be {@literal null}. - * @return never {@literal null}. + * @return {@literal null} indicates no {@link Predicate}. */ + @Nullable public static Predicate getPredicate(Root root, CriteriaBuilder cb, Example example) { return getPredicate(root, cb, example, EscapeCharacter.DEFAULT); } @@ -89,8 +90,9 @@ public static Predicate getPredicate(Root root, CriteriaBuilder cb, Examp * @param cb must not be {@literal null}. * @param example must not be {@literal null}. * @param escapeCharacter Must not be {@literal null}. - * @return never {@literal null}. + * @return {@literal null} indicates no constraints */ + @Nullable public static Predicate getPredicate(Root root, CriteriaBuilder cb, Example example, EscapeCharacter escapeCharacter) { @@ -105,7 +107,7 @@ public static Predicate getPredicate(Root root, CriteriaBuilder cb, Examp escapeCharacter); if (predicates.isEmpty()) { - return cb.isTrue(cb.literal(true)); + return null; } if (predicates.size() == 1) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java index 459604cb81..1d9fec4104 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java @@ -20,10 +20,6 @@ import static org.mockito.Mockito.*; import static org.springframework.data.domain.Example.*; -import java.lang.reflect.Member; -import java.util.LinkedHashSet; -import java.util.Set; - import jakarta.persistence.Id; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.Expression; @@ -37,6 +33,10 @@ import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.Type; +import java.lang.reflect.Member; +import java.util.LinkedHashSet; +import java.util.Set; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -45,7 +45,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; @@ -90,19 +89,16 @@ class QueryByExamplePredicateBuilderUnitTests { void setUp() { personIdAttribute = new SingularAttributeStub<>("id", PersistentAttributeType.BASIC, Long.class); - personFirstnameAttribute = new SingularAttributeStub<>("firstname", PersistentAttributeType.BASIC, - String.class); + personFirstnameAttribute = new SingularAttributeStub<>("firstname", PersistentAttributeType.BASIC, String.class); personAgeAttribute = new SingularAttributeStub<>("age", PersistentAttributeType.BASIC, Long.class); - personFatherAttribute = new SingularAttributeStub<>("father", PersistentAttributeType.MANY_TO_ONE, - Person.class, personEntityType); - personSkillAttribute = new SingularAttributeStub<>("skill", PersistentAttributeType.EMBEDDED, - Skill.class, skillEntityType); - personAddressAttribute = new SingularAttributeStub<>("address", PersistentAttributeType.EMBEDDED, - Address.class); - skillNameAttribute = new SingularAttributeStub<>("name", PersistentAttributeType.BASIC, - String.class); - skillNestedAttribute = new SingularAttributeStub<>("nested", PersistentAttributeType.MANY_TO_ONE, - Skill.class, skillEntityType); + personFatherAttribute = new SingularAttributeStub<>("father", PersistentAttributeType.MANY_TO_ONE, Person.class, + personEntityType); + personSkillAttribute = new SingularAttributeStub<>("skill", PersistentAttributeType.EMBEDDED, Skill.class, + skillEntityType); + personAddressAttribute = new SingularAttributeStub<>("address", PersistentAttributeType.EMBEDDED, Address.class); + skillNameAttribute = new SingularAttributeStub<>("name", PersistentAttributeType.BASIC, String.class); + skillNestedAttribute = new SingularAttributeStub<>("nested", PersistentAttributeType.MANY_TO_ONE, Skill.class, + skillEntityType); personEntityAttribtues = new LinkedHashSet<>(); personEntityAttribtues.add(personIdAttribute); @@ -153,9 +149,9 @@ void getPredicateShouldThrowExceptionOnNullExample() { } @Test // DATAJPA-218 - void emptyCriteriaListShouldResultTruePredicate() { + void emptyCriteriaListShouldResultInNullPredicate() { assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(new Person()), EscapeCharacter.DEFAULT)) - .isEqualTo(truePredicate); + .isNull(); } @Test // DATAJPA-218 @@ -306,13 +302,11 @@ static class SingularAttributeStub implements SingularAttribute { private Class javaType; private Type type; - SingularAttributeStub(String name, - jakarta.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class javaType) { + SingularAttributeStub(String name, PersistentAttributeType attributeType, Class javaType) { this(name, attributeType, javaType, null); } - SingularAttributeStub(String name, - jakarta.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class javaType, Type type) { + SingularAttributeStub(String name, PersistentAttributeType attributeType, Class javaType, Type type) { this.name = name; this.attributeType = attributeType; this.javaType = javaType; @@ -325,7 +319,7 @@ public String getName() { } @Override - public jakarta.persistence.metamodel.Attribute.PersistentAttributeType getPersistentAttributeType() { + public PersistentAttributeType getPersistentAttributeType() { return attributeType; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java new file mode 100644 index 0000000000..0016bc8523 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java @@ -0,0 +1,86 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository; + +import static org.assertj.core.api.Assertions.*; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder; +import org.springframework.data.jpa.domain.sample.Role; +import org.springframework.data.jpa.repository.sample.RoleRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Greg Turnquist + * @since 3.0 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration({ "classpath:eclipselink.xml", "classpath:config/namespace-application-context.xml" }) +@Transactional +public class QueryByExampleEclipseLinkIntegrationTests { + + @Autowired RoleRepository repository; + @Autowired EntityManager em; + + private Role drummer; + private Role guitarist; + private Role singer; + + @BeforeEach + void setUp() { + + drummer = repository.save(new Role("drummer")); + guitarist = repository.save(new Role("guitarist")); + singer = repository.save(new Role("singer")); + } + + @AfterEach + void clearUp() { + repository.deleteAll(); + } + + @Test // GH-2283 + void queryByExampleWithNoPredicatesShouldHaveNoWhereClause() { + + // given + Role probe = new Role(); + Example example = Example.of(probe); + + CriteriaBuilder builder = em.getCriteriaBuilder(); + CriteriaQuery query = builder.createQuery(Role.class); + Root root = query.from(Role.class); + + // when + Predicate predicate = QueryByExamplePredicateBuilder.getPredicate(root, builder, example); + + // then + assertThat(predicate).isNull(); + assertThat(repository.findAll(example)).containsExactlyInAnyOrder(drummer, guitarist, singer); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java new file mode 100644 index 0000000000..510cf4abb0 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java @@ -0,0 +1,86 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository; + +import static org.assertj.core.api.Assertions.*; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder; +import org.springframework.data.jpa.domain.sample.Role; +import org.springframework.data.jpa.repository.sample.RoleRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Greg Turnquist + * @since 3.0 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration({ "classpath:hibernate.xml", "classpath:config/namespace-application-context.xml" }) +@Transactional +public class QueryByExampleHibernateIntegrationTests { + + @Autowired RoleRepository repository; + @Autowired EntityManager em; + + private Role drummer; + private Role guitarist; + private Role singer; + + @BeforeEach + void setUp() { + + drummer = repository.save(new Role("drummer")); + guitarist = repository.save(new Role("guitarist")); + singer = repository.save(new Role("singer")); + } + + @AfterEach + void clearUp() { + repository.deleteAll(); + } + + @Test // GH-2283 + void queryByExampleWithNoPredicatesShouldHaveNoWhereClause() { + + // given + Role probe = new Role(); + Example example = Example.of(probe); + + CriteriaBuilder builder = em.getCriteriaBuilder(); + CriteriaQuery query = builder.createQuery(Role.class); + Root root = query.from(Role.class); + + // when + Predicate predicate = QueryByExamplePredicateBuilder.getPredicate(root, builder, example); + + // then + assertThat(predicate).isNull(); + assertThat(repository.findAll(example)).containsExactlyInAnyOrder(drummer, guitarist, singer); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java index e166589fb4..d161811112 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java @@ -18,13 +18,14 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.QueryHint; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.domain.sample.Role; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.QueryHints; import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.CrudRepository; import com.querydsl.core.types.Predicate; @@ -35,12 +36,12 @@ * @author Thomas Darimont * @author Yanming Zhou */ -public interface RoleRepository extends CrudRepository, QuerydslPredicateExecutor { +public interface RoleRepository extends JpaRepository, QuerydslPredicateExecutor { @Override @Lock(LockModeType.READ) @QueryHints(@QueryHint(name = "foo", value = "bar")) - Iterable findAll(); + List findAll(); @Override @Lock(LockModeType.READ) From 0aa6d7d6e79554e23cdfe65c18565735cac78a99 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 10 May 2022 10:34:10 -0500 Subject: [PATCH 204/821] Apply query hints to count queries for page-based Specification. Query hints are applied in many places, but not when doing a findAll(Specification, Pageable). Closes #2054. Original pull request #2528 --- .../support/SimpleJpaRepository.java | 24 ++++++++++++++++++- .../support/SimpleJpaRepositoryUnitTests.java | 17 ++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index d47fd9883f..9f4552d96f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -309,6 +309,13 @@ protected QueryHints getQueryHints() { return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata); } + /** + * Returns {@link QueryHints} with the query hints on the current {@link CrudMethodMetadata} for count queries. + */ + protected QueryHints getQueryHintsForCount() { + return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata).forCounts(); + } + @Deprecated @Override public T getOne(ID id) { @@ -750,7 +757,7 @@ protected TypedQuery getCountQuery(@Nullable Specification TypedQuery applyRepositoryMethodMetadataForCount(TypedQuery query) { + + if (metadata == null) { + return query; + } + + applyQueryHintsForCount(query); + + return query; + } + + private void applyQueryHintsForCount(Query query) { + getQueryHintsForCount().forEach(query::setHint); + } + /** * Executes a count query and transparently sums up all values returned. * diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index cc69535d33..ebbc549041 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -18,9 +18,7 @@ import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.Optional; +import static org.springframework.data.jpa.domain.Specification.*; import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; @@ -28,6 +26,9 @@ import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; +import java.util.Arrays; +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -192,4 +193,14 @@ void doNothingWhenNonExistentInstanceGetsDeleted() { verify(em, never()).remove(newUser); verify(em, never()).merge(newUser); } + + @Test // GH-2054 + void applyQueryHintsToCountQueriesForSpecificationPageables() { + + when(query.getResultList()).thenReturn(Arrays.asList(new User(), new User())); + + repo.findAll(where(null), PageRequest.of(2, 1)); + + verify(metadata).getQueryHintsForCount(); + } } From 718f9201fca54180b87a264fee8d9ff1a9c7d302 Mon Sep 17 00:00:00 2001 From: Cedomir Igaly Date: Mon, 23 May 2022 11:19:42 +0200 Subject: [PATCH 205/821] Improve robustness of HibernateJpaParametersParameterAccessor. This change avoids errors when no transactional EntityManager is available. Closes #2540 Original pull request #2542 --- ...bernateJpaParametersParameterAccessor.java | 5 +- ...aParametersParameterAccessorUnitTests.java | 49 +++++++++++++++++++ .../src/test/resources/hjppa-test.xml | 33 +++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java create mode 100644 spring-data-jpa/src/test/resources/hjppa-test.xml diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index 5948c519d7..d2a5ccd1a9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -16,7 +16,7 @@ package org.springframework.data.jpa.provider; import jakarta.persistence.EntityManager; -import org.hibernate.Session; +import org.hibernate.SessionFactory; import org.hibernate.TypeHelper; import org.hibernate.jpa.TypedParameterValue; import org.hibernate.type.Type; @@ -49,8 +49,7 @@ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAcce super(parameters, values); - Session session = em.unwrap(Session.class); - this.typeHelper = session.getSessionFactory().getTypeHelper(); + this.typeHelper = em.getEntityManagerFactory().unwrap(SessionFactory.class).getTypeHelper(); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java new file mode 100644 index 0000000000..a7665990cf --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java @@ -0,0 +1,49 @@ +package org.springframework.data.jpa.provider; + +import jakarta.persistence.EntityManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.repository.query.JpaParameters; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; + +import java.lang.reflect.Method; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration("classpath:hjppa-test.xml") +public class HibernateJpaParametersParameterAccessorUnitTests { + + @Autowired + private EntityManager em; + + @Autowired + private PlatformTransactionManager transactionManager; + + @Test + void withoutTransaction() throws NoSuchMethodException { + simpleTest(); + } + + @Test + void withinTransaction() throws Exception { + final TransactionStatus tx = transactionManager.getTransaction(new DefaultTransactionDefinition()); + try { + simpleTest(); + } finally { + transactionManager.rollback(tx); + } + } + + private void simpleTest() throws NoSuchMethodException { + final Method method = EntityManager.class.getMethod("flush"); + final JpaParameters parameters = new JpaParameters(method); + final HibernateJpaParametersParameterAccessor accessor = new HibernateJpaParametersParameterAccessor(parameters, new Object[]{}, em); + Assertions.assertEquals(0, accessor.getValues().length); + Assertions.assertEquals(parameters, accessor.getParameters()); + } +} diff --git a/spring-data-jpa/src/test/resources/hjppa-test.xml b/spring-data-jpa/src/test/resources/hjppa-test.xml new file mode 100644 index 0000000000..cec01327c5 --- /dev/null +++ b/spring-data-jpa/src/test/resources/hjppa-test.xml @@ -0,0 +1,33 @@ + + + + + + + + org.springframework.data.jpa.domain + org.springframework.data.jpa.domain.sample + + + + + + + + + + + + + + + + + + From 4aeac5521183997780e5033a663f4787bc7d0129 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 24 May 2022 15:07:43 +0200 Subject: [PATCH 206/821] Polishing. Added author tag, JavaDoc and improved test name. See #2540 Original pull request #2542 --- ...bernateJpaParametersParameterAccessor.java | 1 + ...aParametersParameterAccessorUnitTests.java | 33 +++++++++++-------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index d2a5ccd1a9..918ec58af8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -32,6 +32,7 @@ * * @author Wonchul Heo * @author Jens Schauder + * @author Cedomir Igaly * @since 2.7 */ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAccessor { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java index a7665990cf..68cbc79fa2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java @@ -1,6 +1,9 @@ package org.springframework.data.jpa.provider; import jakarta.persistence.EntityManager; + +import java.lang.reflect.Method; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -12,37 +15,41 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; -import java.lang.reflect.Method; - +/** + * Unit tests for {@link HibernateJpaParametersParameterAccessor}. + * + * @author Cedomir Igaly + */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:hjppa-test.xml") public class HibernateJpaParametersParameterAccessorUnitTests { - @Autowired - private EntityManager em; + @Autowired private EntityManager em; - @Autowired - private PlatformTransactionManager transactionManager; + @Autowired private PlatformTransactionManager transactionManager; @Test void withoutTransaction() throws NoSuchMethodException { - simpleTest(); + parametersCanGetAccessesOutsideTransaction(); } @Test void withinTransaction() throws Exception { - final TransactionStatus tx = transactionManager.getTransaction(new DefaultTransactionDefinition()); + + TransactionStatus tx = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { - simpleTest(); + parametersCanGetAccessesOutsideTransaction(); } finally { transactionManager.rollback(tx); } } - private void simpleTest() throws NoSuchMethodException { - final Method method = EntityManager.class.getMethod("flush"); - final JpaParameters parameters = new JpaParameters(method); - final HibernateJpaParametersParameterAccessor accessor = new HibernateJpaParametersParameterAccessor(parameters, new Object[]{}, em); + private void parametersCanGetAccessesOutsideTransaction() throws NoSuchMethodException { + + Method method = EntityManager.class.getMethod("flush"); + JpaParameters parameters = new JpaParameters(method); + HibernateJpaParametersParameterAccessor accessor = new HibernateJpaParametersParameterAccessor(parameters, + new Object[] {}, em); Assertions.assertEquals(0, accessor.getValues().length); Assertions.assertEquals(parameters, accessor.getParameters()); } From f8d85b6f14439c0fb7b46f6b495cb0011108c9e7 Mon Sep 17 00:00:00 2001 From: Conor McGale <75184002+ConorJEM@users.noreply.github.com> Date: Thu, 26 May 2022 03:35:23 +0100 Subject: [PATCH 207/821] Fixes broken link to 'Accessing JPA Data with REST' guide. Original pull request #2550 --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index ec7736e3bb..30b92719c7 100644 --- a/README.adoc +++ b/README.adoc @@ -179,7 +179,7 @@ The generated documentation is available from `target/site/reference/html/index. The https://spring.io/[spring.io] site contains several guides that show how to use Spring Data step-by-step: * https://spring.io/guides/gs/accessing-data-jpa/[Accessing Data with JPA]: Learn how to work with JPA data persistence using Spring Data JPA. -* https://spring.io/guides/gs/accessing-jpa-data-rest/[Accessing JPA Data with REST] is a guide to creating a REST web service exposing data stored with JPA through repositories. +* https://spring.io/guides/gs/accessing-data-rest/[Accessing JPA Data with REST] is a guide to creating a REST web service exposing data stored with JPA through repositories. == Examples From 782bcf325d1aaab5a0e9c6da37af147646f7bc12 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 31 May 2022 14:26:09 -0500 Subject: [PATCH 208/821] Fine tune checks for JDBC and JPA style parameters. The checks for JDBC and JPA parameters were sloppy and based on side effects. By using zero width lookaheads, we can precisely spot situtations where the user has both types of parameters. Otherwise, let the query on through to the JPA provider. Closes #2551. --- .../jpa/repository/query/StringQuery.java | 13 +++-- .../JpaQueryLookupStrategyUnitTests.java | 48 +++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 60ca72af33..994a7837c4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -47,6 +47,7 @@ * @author Mark Paluch * @author Jens Schauder * @author Diego Krupitza + * @author Greg Turnquist */ class StringQuery implements DeclaredQuery { @@ -165,6 +166,10 @@ enum ParameterBindingParser { // .............................................................^ start with a question mark. private static final Pattern PARAMETER_BINDING_BY_INDEX = Pattern.compile(POSITIONAL_OR_INDEXED_PARAMETER); private static final Pattern PARAMETER_BINDING_PATTERN; + private static final Pattern JDBC_STYLE_PARAM = Pattern.compile(" \\?(?!\\d)"); // ?[no digit] + private static final Pattern NUMBERED_STYLE_PARAM = Pattern.compile(" \\?(?=\\d)"); // ?[digit] + private static final Pattern NAMED_STYLE_PARAM = Pattern.compile(" :\\w+"); // :[text] + private static final String MESSAGE = "Already found parameter binding with same index / parameter name but differing binding type! " + "Already have: %s, found %s! If you bind a parameter multiple times make sure they use the same binding."; private static final int INDEXED_PARAMETER_GROUP = 4; @@ -243,13 +248,13 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St String expression = spelExtractor.getParameter(parameterName == null ? parameterIndexString : parameterName); String replacement = null; + queryMeta.usesJdbcStyleParameters = JDBC_STYLE_PARAM.matcher(resultingQuery).find(); + usesJpaStyleParameters = NUMBERED_STYLE_PARAM.matcher(resultingQuery).find() + || NAMED_STYLE_PARAM.matcher(resultingQuery).find(); + expressionParameterIndex++; if ("".equals(parameterIndexString)) { - - queryMeta.usesJdbcStyleParameters = true; parameterIndex = expressionParameterIndex; - } else { - usesJpaStyleParameters = true; } if (usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index 76cc0cf132..745d6cac5f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -193,6 +193,42 @@ void noQueryShouldNotBeInvoked() { assertThatIllegalStateException().isThrownBy(() -> query.getQueryMethod()); } + @Test // GH-2551 + void customQueryWithQuestionMarksShouldWork() throws NoSuchMethodException { + + QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND, + EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); + + Method namedMethod = UserRepository.class.getMethod("customQueryWithQuestionMarksAndNamedParam", String.class); + RepositoryMetadata namedMetadata = new DefaultRepositoryMetadata(UserRepository.class); + + strategy.resolveQuery(namedMethod, namedMetadata, projectionFactory, namedQueries); + + assertThatIllegalArgumentException().isThrownBy(() -> { + + Method jdbcStyleMethod = UserRepository.class.getMethod("customQueryWithQuestionMarksAndJdbcStyleParam", + String.class); + RepositoryMetadata jdbcStyleMetadata = new DefaultRepositoryMetadata(UserRepository.class); + + strategy.resolveQuery(jdbcStyleMethod, jdbcStyleMetadata, projectionFactory, namedQueries); + }).withMessageContaining("JDBC style parameters (?) are not supported for JPA queries."); + + Method jpaStyleMethod = UserRepository.class.getMethod("customQueryWithQuestionMarksAndNumberedStyleParam", + String.class); + RepositoryMetadata jpaStyleMetadata = new DefaultRepositoryMetadata(UserRepository.class); + + strategy.resolveQuery(jpaStyleMethod, jpaStyleMetadata, projectionFactory, namedQueries); + + assertThatIllegalArgumentException().isThrownBy(() -> { + + Method jpaAndJdbcStyleMethod = UserRepository.class + .getMethod("customQueryWithQuestionMarksAndJdbcStyleAndNumberedStyleParam", String.class, String.class); + RepositoryMetadata jpaAndJdbcMetadata = new DefaultRepositoryMetadata(UserRepository.class); + + strategy.resolveQuery(jpaAndJdbcStyleMethod, jpaAndJdbcMetadata, projectionFactory, namedQueries); + }).withMessageContaining("Mixing of ? parameters and other forms like ?1 is not supported"); + } + interface UserRepository extends Repository { @Query("something absurd") @@ -210,6 +246,18 @@ interface UserRepository extends Repository { @Query(value = "something absurd", name = "my-query-name") User annotatedQueryWithQueryAndQueryName(); + @Query("SELECT * FROM table WHERE (json_col->'jsonKey')::jsonb \\?\\? :param ") + List customQueryWithQuestionMarksAndNamedParam(String param); + + @Query("SELECT * FROM table WHERE (json_col->'jsonKey')::jsonb \\?\\? ? ") + List customQueryWithQuestionMarksAndJdbcStyleParam(String param); + + @Query("SELECT * FROM table WHERE (json_col->'jsonKey')::jsonb \\?\\? ?1 ") + List customQueryWithQuestionMarksAndNumberedStyleParam(String param); + + @Query("SELECT * FROM table WHERE (json_col->'jsonKey')::jsonb \\?\\? ?1 and other_col = ? ") + List customQueryWithQuestionMarksAndJdbcStyleAndNumberedStyleParam(String param1, String param2); + // This is a named query with Sort parameter, which isn't supported List customNamedQuery(String firstname, Sort sort); } From ddc36dd7236a6e6cf5a9ab75c838465a3994063c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 3 Jun 2022 09:32:46 +0200 Subject: [PATCH 209/821] Upgrade to Maven Wrapper 3.8.5. See #2560 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index e4e722b003..75534d80c8 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Mon Oct 11 14:30:21 CEST 2021 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip +#Fri Jun 03 09:32:46 CEST 2022 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip From c2a305f01d705fa3d87c175cbd5f743159fc3e17 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 3 Jun 2022 09:34:19 +0200 Subject: [PATCH 210/821] Update CI properties. See #2531 --- ci/pipeline.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 43b4e65e48..57e4868d49 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,5 +1,5 @@ # Java versions -java.main.tag=17.0.2_8-jdk +java.main.tag=17.0.3_7-jdk # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} From a344a76959e0b356ac15e81d578f87e860566232 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Thu, 2 Jun 2022 10:33:09 +0200 Subject: [PATCH 211/821] Make JSqlParserQueryEnhancer work with updating statements. This implementation of QueryEnhancer was originally designed for SELECT statements. This commit now handles DELETE and UPDATE operations by side-stepping any sorting or other changes. Keep in mind that "enhancing" non selects does not have any effect on them (and the current default implementation `QueryUtils` does not care either aka it often just returns the same query, null or empty string). Closes #2555 --- .../query/JSqlParserQueryEnhancer.java | 65 ++++++++++++++++++- .../jpa/repository/UserRepositoryTests.java | 13 ++++ .../query/QueryEnhancerUnitTests.java | 19 ++++++ .../jpa/repository/sample/UserRepository.java | 5 ++ 4 files changed, 101 insertions(+), 1 deletion(-) 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 3c4d038c06..d7aad1e4cf 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 @@ -24,11 +24,14 @@ import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.delete.Delete; 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.SelectExpressionItem; import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.statement.update.Update; import java.util.ArrayList; import java.util.Collections; @@ -54,20 +57,49 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { private final DeclaredQuery query; + private final ParsedType parsedType; /** * @param query the query we want to enhance. Must not be {@literal null}. */ public JSqlParserQueryEnhancer(DeclaredQuery query) { this.query = query; + this.parsedType = detectParsedType(); + } + + /** + * Detects what type of query is provided. + * + * @return the parsed type + */ + private ParsedType detectParsedType() { + try { + Statement statement = CCJSqlParserUtil.parse(this.query.getQueryString()); + + if (statement instanceof Update) { + return ParsedType.UPDATE; + } else if (statement instanceof Delete) { + return ParsedType.DELETE; + } else if (statement instanceof Select) { + return ParsedType.SELECT; + } else { + return ParsedType.SELECT; + } + + } catch (JSQLParserException e) { + throw new IllegalArgumentException("The query you provided is not a valid SQL Query!", e); + } } @Override public String applySorting(Sort sort, @Nullable String alias) { - String queryString = query.getQueryString(); Assert.hasText(queryString, "Query must not be null or empty!"); + if (this.parsedType != ParsedType.SELECT) { + return queryString; + } + if (sort.isUnsorted()) { return queryString; } @@ -120,6 +152,10 @@ private Set getSelectionAliases(PlainSelect selectBody) { */ Set getSelectionAliases() { + if (this.parsedType != ParsedType.SELECT) { + return new HashSet<>(); + } + Select selectStatement = parseSelectStatement(this.query.getQueryString()); PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); return this.getSelectionAliases(selectBody); @@ -132,6 +168,9 @@ Set getSelectionAliases() { * @return a {@literal Set} of aliases used in the query. Guaranteed to be not {@literal null}. */ private Set getJoinAliases(String query) { + if (this.parsedType != ParsedType.SELECT) { + return new HashSet<>(); + } return getJoinAliases((PlainSelect) parseSelectStatement(query).getSelectBody()); } @@ -211,6 +250,10 @@ public String detectAlias() { @Nullable private String detectAlias(String query) { + if (this.parsedType != ParsedType.SELECT) { + return null; + } + Select selectStatement = parseSelectStatement(query); PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); return detectAlias(selectBody); @@ -233,6 +276,10 @@ private static String detectAlias(PlainSelect selectBody) { @Override public String createCountQueryFor(@Nullable String countProjection) { + if (this.parsedType != ParsedType.SELECT) { + return this.query.getQueryString(); + } + Assert.hasText(this.query.getQueryString(), "OriginalQuery must not be null or empty!"); Select selectStatement = parseSelectStatement(this.query.getQueryString()); @@ -278,6 +325,10 @@ public String createCountQueryFor(@Nullable String countProjection) { @Override public String getProjection() { + if (this.parsedType != ParsedType.SELECT) { + return ""; + } + Assert.hasText(query.getQueryString(), "Query must not be null or empty!"); Select selectStatement = parseSelectStatement(query.getQueryString()); @@ -327,3 +378,15 @@ public DeclaredQuery getQuery() { return this.query; } } + +/** + * An enum to represent the top level parsed statement of the provided query. + *
    + *
  • {@code ParsedType.DELETE}: means the top level statement is {@link Delete}
  • + *
  • {@code ParsedType.UPDATE}: means the top level statement is {@link Update}
  • + *
  • {@code ParsedType.SELECT}: means the top level statement is {@link Select}
  • + *
+ */ +enum ParsedType { + DELETE, UPDATE, SELECT; +} 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 d637fe9525..4b7bb6b5f6 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 @@ -2865,6 +2865,19 @@ void existsWithSpec() { assertThat(repository.exists(hundredYearsOld)).isTrue(); } + @Test // GH-2555 + void modifyingUpdateNativeQueryWorksWithJSQLParser() { + flushTestUsers(); + + Optional byIdUser = repository.findById(firstUser.getId()); + assertThat(byIdUser).isPresent().map(User::isActive).get().isEqualTo(true); + + repository.setActiveToFalseWithModifyingNative(byIdUser.get().getId()); + + Optional afterUpdate = repository.findById(firstUser.getId()); + assertThat(afterUpdate).isPresent().map(User::isActive).get().isEqualTo(false); + } + @Test // GH-1262 void deleteWithSpec() { 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 c181425f12..5b8487cfbc 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 @@ -731,6 +731,25 @@ void countQueryUsesCorrectVariable() { assertThat(countQueryFor).isEqualTo("SELECT count(test) FROM (SELECT * FROM test) AS test"); } + @Test // GH-2555 + void modifyingQueriesAreDetectedCorrectly() { + String modifyingQuery = "update userinfo user set user.is_in_treatment = false where user.id = :userId"; + + String aliasNotConsideringQueryType = QueryUtils.detectAlias(modifyingQuery); + String projectionNotConsideringQueryType = QueryUtils.getProjection(modifyingQuery); + boolean constructorExpressionNotConsideringQueryType = QueryUtils.hasConstructorExpression(modifyingQuery); + String countQueryForNotConsiderQueryType = QueryUtils.createCountQueryFor(modifyingQuery); + + StringQuery modiQuery = new StringQuery(modifyingQuery, true); + + assertThat(modiQuery.getAlias()).isEqualToIgnoringCase(aliasNotConsideringQueryType); + assertThat(modiQuery.getProjection()).isEqualToIgnoringCase(projectionNotConsideringQueryType); + assertThat(modiQuery.hasConstructorExpression()).isEqualTo(constructorExpressionNotConsideringQueryType); + + assertThat(countQueryForNotConsiderQueryType).isEqualToIgnoringCase(modifyingQuery); + assertThat(QueryEnhancerFactory.forQuery(modiQuery).createCountQueryFor()).isEqualToIgnoringCase(modifyingQuery); + } + public static Stream detectsJoinAliasesCorrectlySource() { return Stream.of( // 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 c7ceb0e555..c913ddd541 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 @@ -637,6 +637,11 @@ List findAllAndSortByFunctionResultPositionalParameter( List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter1") String namedParameter1, @Param("namedParameter2") String namedParameter2, Sort sort); + // GH-2555 + @Modifying(clearAutomatically = true) + @Query(value = "update SD_User u set u.active = false where u.id = :userId", nativeQuery = true) + void setActiveToFalseWithModifyingNative(@Param("userId") int userId); + interface RolesAndFirstname { String getFirstname(); From cc1eb9ce2b512a3928e55bd190397b751814cd9b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 3 Jun 2022 14:44:32 -0500 Subject: [PATCH 212/821] Polishing. See #2555. --- .../query/JSqlParserQueryEnhancer.java | 31 +++++++++++-------- .../jpa/repository/UserRepositoryTests.java | 1 + .../query/QueryEnhancerUnitTests.java | 1 + 3 files changed, 20 insertions(+), 13 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 d7aad1e4cf..d8322ec8e5 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 @@ -63,16 +63,18 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { * @param query the query we want to enhance. Must not be {@literal null}. */ public JSqlParserQueryEnhancer(DeclaredQuery query) { + this.query = query; this.parsedType = detectParsedType(); } /** * Detects what type of query is provided. - * + * * @return the parsed type */ private ParsedType detectParsedType() { + try { Statement statement = CCJSqlParserUtil.parse(this.query.getQueryString()); @@ -85,7 +87,6 @@ private ParsedType detectParsedType() { } else { return ParsedType.SELECT; } - } catch (JSQLParserException e) { throw new IllegalArgumentException("The query you provided is not a valid SQL Query!", e); } @@ -93,6 +94,7 @@ private ParsedType detectParsedType() { @Override public String applySorting(Sort sort, @Nullable String alias) { + String queryString = query.getQueryString(); Assert.hasText(queryString, "Query must not be null or empty!"); @@ -168,9 +170,11 @@ Set getSelectionAliases() { * @return a {@literal Set} of aliases used in the query. Guaranteed to be not {@literal null}. */ private Set getJoinAliases(String query) { + if (this.parsedType != ParsedType.SELECT) { return new HashSet<>(); } + return getJoinAliases((PlainSelect) parseSelectStatement(query).getSelectBody()); } @@ -377,16 +381,17 @@ private boolean onlyASingleColumnProjection(List projection) { public DeclaredQuery getQuery() { return this.query; } -} -/** - * An enum to represent the top level parsed statement of the provided query. - *
    - *
  • {@code ParsedType.DELETE}: means the top level statement is {@link Delete}
  • - *
  • {@code ParsedType.UPDATE}: means the top level statement is {@link Update}
  • - *
  • {@code ParsedType.SELECT}: means the top level statement is {@link Select}
  • - *
- */ -enum ParsedType { - DELETE, UPDATE, SELECT; + /** + * An enum to represent the top level parsed statement of the provided query. + *
    + *
  • {@code ParsedType.DELETE}: means the top level statement is {@link Delete}
  • + *
  • {@code ParsedType.UPDATE}: means the top level statement is {@link Update}
  • + *
  • {@code ParsedType.SELECT}: means the top level statement is {@link Select}
  • + *
+ */ + enum ParsedType { + DELETE, UPDATE, SELECT; + } + } 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 4b7bb6b5f6..7bc48e30a6 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 @@ -2867,6 +2867,7 @@ void existsWithSpec() { @Test // GH-2555 void modifyingUpdateNativeQueryWorksWithJSQLParser() { + flushTestUsers(); Optional byIdUser = repository.findById(firstUser.getId()); 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 5b8487cfbc..3572fa403e 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 @@ -733,6 +733,7 @@ void countQueryUsesCorrectVariable() { @Test // GH-2555 void modifyingQueriesAreDetectedCorrectly() { + String modifyingQuery = "update userinfo user set user.is_in_treatment = false where user.id = :userId"; String aliasNotConsideringQueryType = QueryUtils.detectAlias(modifyingQuery); From cbfb0f7a1caaea9ab06ab0a661cf7e4a5a4f8366 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 6 Jun 2022 10:37:34 -0500 Subject: [PATCH 213/821] Alter JSR-310 converters to use java.sql.Timestamp instead of java.util.Date. Closes #2077. --- .../convert/threeten/Jsr310JpaConverters.java | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java index ad8ce3f0bb..78d7d403d8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java @@ -15,6 +15,10 @@ */ package org.springframework.data.jpa.convert.threeten; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; @@ -22,19 +26,17 @@ import java.time.ZoneId; import java.util.Date; -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.Converter; - -import org.springframework.data.convert.Jsr310Converters.DateToInstantConverter; import org.springframework.data.convert.Jsr310Converters.DateToLocalDateConverter; import org.springframework.data.convert.Jsr310Converters.DateToLocalDateTimeConverter; import org.springframework.data.convert.Jsr310Converters.DateToLocalTimeConverter; -import org.springframework.data.convert.Jsr310Converters.InstantToDateConverter; import org.springframework.data.convert.Jsr310Converters.LocalDateTimeToDateConverter; import org.springframework.data.convert.Jsr310Converters.LocalDateToDateConverter; import org.springframework.data.convert.Jsr310Converters.LocalTimeToDateConverter; import org.springframework.data.convert.Jsr310Converters.StringToZoneIdConverter; import org.springframework.data.convert.Jsr310Converters.ZoneIdToStringConverter; +import org.springframework.data.convert.ReadingConverter; +import org.springframework.data.convert.WritingConverter; +import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -46,6 +48,7 @@ * * @author Oliver Gierke * @author Kevin Peters + * @author Greg Turnquist */ public class Jsr310JpaConverters { @@ -98,18 +101,18 @@ public LocalDateTime convertToEntityAttribute(Date date) { } @Converter(autoApply = true) - public static class InstantConverter implements AttributeConverter { + public static class InstantConverter implements AttributeConverter { @Nullable @Override - public Date convertToDatabaseColumn(Instant instant) { - return instant == null ? null : InstantToDateConverter.INSTANCE.convert(instant); + public Timestamp convertToDatabaseColumn(Instant instant) { + return instant == null ? null : InstantToTimestampConverter.INSTANCE.convert(instant); } @Nullable @Override - public Instant convertToEntityAttribute(Date date) { - return date == null ? null : DateToInstantConverter.INSTANCE.convert(date); + public Instant convertToEntityAttribute(Timestamp timestamp) { + return timestamp == null ? null : TimestampToInstantConverter.INSTANCE.convert(timestamp); } } @@ -128,4 +131,28 @@ public ZoneId convertToEntityAttribute(String zoneId) { return zoneId == null ? null : StringToZoneIdConverter.INSTANCE.convert(zoneId); } } + + @ReadingConverter + enum TimestampToInstantConverter implements org.springframework.core.convert.converter.Converter { + + INSTANCE; + + @NonNull + @Override + public Instant convert(Timestamp source) { + return source.toInstant(); + } + } + + @WritingConverter + enum InstantToTimestampConverter implements org.springframework.core.convert.converter.Converter { + + INSTANCE; + + @NonNull + @Override + public Timestamp convert(Instant source) { + return Timestamp.from(source); + } + } } From 48790adebbf39a79f706095c38b78026a479bef9 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 8 Jun 2022 10:23:30 -0500 Subject: [PATCH 214/821] Align Spring Data JPA annotations with Spring Framework annotations. Remove @Inherited from Spring Data JPA annotations to align with Spring Framework ones. Closes #803. --- .../envers/repository/config/EnableEnversRepositories.java | 7 +++---- .../data/jpa/repository/config/EnableJpaAuditing.java | 7 +++---- .../data/jpa/repository/config/EnableJpaRepositories.java | 7 +++---- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java index 663fe24250..632ef5fa95 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java @@ -15,15 +15,14 @@ */ package org.springframework.data.envers.repository.config; +import jakarta.persistence.EntityManagerFactory; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import jakarta.persistence.EntityManagerFactory; - import org.springframework.beans.factory.FactoryBean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Lazy; @@ -45,6 +44,7 @@ * {@link #repositoryFactoryBeanClass} to {@link EnversRevisionRepositoryFactoryBean}. * * @author Mark Paluch + * @author Greg Turnquist * @since 2.5 * @see EnableJpaRepositories * @see AliasFor @@ -52,7 +52,6 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -@Inherited @EnableJpaRepositories public @interface EnableEnversRepositories { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java index 69df7a7cb4..b462df485f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java @@ -17,7 +17,6 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -31,8 +30,8 @@ * * @author Thomas Darimont * @author Oliver Gierke + * @author Greg Turnquist */ -@Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @@ -61,8 +60,8 @@ boolean modifyOnCreate() default true; /** - * Configures a {@link DateTimeProvider} bean name that allows customizing the {@link java.time.temporal.TemporalAccessor} to be - * used for setting creation and modification dates. + * Configures a {@link DateTimeProvider} bean name that allows customizing the + * {@link java.time.temporal.TemporalAccessor} to be used for setting creation and modification dates. * * @return */ diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java index 802b831700..fa2241dc78 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java @@ -15,15 +15,14 @@ */ package org.springframework.data.jpa.repository.config; +import jakarta.persistence.EntityManagerFactory; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import jakarta.persistence.EntityManagerFactory; - import org.springframework.beans.factory.FactoryBean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Import; @@ -41,11 +40,11 @@ * * @author Oliver Gierke * @author Thomas Darimont + * @author Greg Turnquist */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -@Inherited @Import(JpaRepositoriesRegistrar.class) public @interface EnableJpaRepositories { From b57eb85a230d3a05e45276052ef62b1249e5a0e6 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 8 Jun 2022 12:42:17 -0700 Subject: [PATCH 215/821] Remove punctuation in Exception messages. Closes #2566. --- .../support/EnversRevisionRepositoryImpl.java | 12 +++--- .../ReflectionRevisionEntityInformation.java | 2 +- .../QueryByExamplePredicateBuilder.java | 8 ++-- .../data/jpa/domain/JpaSort.java | 28 ++++++------- .../AuditingBeanFactoryPostProcessor.java | 2 +- .../support/AuditingEntityListener.java | 6 +-- .../mapping/JpaMetamodelMappingContext.java | 6 +-- .../jpa/mapping/JpaPersistentEntityImpl.java | 6 +-- .../mapping/JpaPersistentPropertyImpl.java | 2 +- .../data/jpa/provider/JpaClassUtils.java | 4 +- .../jpa/provider/PersistenceProvider.java | 4 +- .../jpa/repository/cdi/JpaRepositoryBean.java | 2 +- .../cdi/JpaRepositoryExtension.java | 8 ++-- .../config/AuditingBeanDefinitionParser.java | 4 +- .../config/JpaAuditingRegistrar.java | 10 ++--- ...JpaMetamodelMappingContextFactoryBean.java | 4 +- .../repository/query/AbstractJpaQuery.java | 12 +++--- .../query/AbstractStringBasedJpaQuery.java | 10 ++--- .../query/DefaultJpaEntityMetadata.java | 2 +- .../repository/query/EmptyDeclaredQuery.java | 2 +- .../query/ExpressionBasedStringQuery.java | 6 +-- .../query/JSqlParserQueryEnhancer.java | 8 ++-- .../data/jpa/repository/query/Jpa21Utils.java | 18 ++++---- .../jpa/repository/query/JpaEntityGraph.java | 4 +- .../jpa/repository/query/JpaParameters.java | 4 +- .../jpa/repository/query/JpaQueryCreator.java | 6 +-- .../repository/query/JpaQueryExecution.java | 12 +++--- .../query/JpaQueryLookupStrategy.java | 16 +++---- .../jpa/repository/query/JpaQueryMethod.java | 10 ++--- .../repository/query/JpaResultConverters.java | 4 +- .../data/jpa/repository/query/NamedQuery.java | 12 +++--- .../jpa/repository/query/ParameterBinder.java | 6 +-- .../query/ParameterBinderFactory.java | 14 +++---- .../query/ParameterMetadataProvider.java | 12 +++--- .../repository/query/PartTreeJpaQuery.java | 8 ++-- .../query/QueryEnhancerFactory.java | 2 +- .../query/QueryParameterSetter.java | 4 +- .../query/QueryParameterSetterFactory.java | 32 +++++++------- .../data/jpa/repository/query/QueryUtils.java | 28 ++++++------- .../jpa/repository/query/SimpleJpaQuery.java | 4 +- .../query/StoredProcedureAttributeSource.java | 12 +++--- .../query/StoredProcedureAttributes.java | 4 +- .../query/StoredProcedureJpaQuery.java | 4 +- .../jpa/repository/query/StringQuery.java | 34 +++++++-------- .../CrudMethodMetadataPostProcessor.java | 2 +- .../repository/support/DefaultJpaContext.java | 10 ++--- .../repository/support/DefaultQueryHints.java | 4 +- .../FetchableFluentQueryByExample.java | 4 +- .../FetchableFluentQueryByPredicate.java | 4 +- .../FetchableFluentQueryBySpecification.java | 4 +- .../support/JpaEntityInformation.java | 2 +- .../support/JpaEntityInformationSupport.java | 4 +- .../JpaMetamodelEntityInformation.java | 10 ++--- .../support/JpaRepositoryFactory.java | 14 +++---- .../support/JpaRepositoryFactoryBean.java | 4 +- .../repository/support/QueryHintValue.java | 4 +- .../jpa/repository/support/QueryHints.java | 2 +- .../data/jpa/repository/support/Querydsl.java | 22 +++++----- .../support/QuerydslJpaPredicateExecutor.java | 24 +++++------ .../support/QuerydslJpaRepository.java | 6 +-- .../support/QuerydslRepositorySupport.java | 12 +++--- .../support/SimpleJpaRepository.java | 42 +++++++++---------- ...hScanningPersistenceUnitPostProcessor.java | 16 +++---- .../MergingPersistenceUnitManager.java | 4 +- .../data/jpa/util/JpaMetamodel.java | 6 +-- .../jpa/domain/sample/AuditorAwareStub.java | 2 +- .../jpa/domain/sample/SampleEntityPK.java | 4 +- .../infrastructure/HibernateTestUtils.java | 2 +- ...lipseLinkNamespaceUserRepositoryTests.java | 2 +- .../cdi/CdiExtensionIntegrationTests.java | 2 +- ...aQueryRewriterWithCdiIntegrationTests.java | 2 +- .../jpa/repository/query/Jpa21UtilsTests.java | 12 +++--- .../JpaQueryLookupStrategyUnitTests.java | 4 +- .../PartTreeJpaQueryIntegrationTests.java | 4 +- .../QueryParameterSetterFactoryUnitTests.java | 4 +- .../repository/sample/UserRepositoryImpl.java | 6 +-- .../JpaRepositoryFactoryUnitTests.java | 4 +- 77 files changed, 323 insertions(+), 323 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index 838d647219..537c667830 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -77,7 +77,7 @@ public class EnversRevisionRepositoryImpl entityInformation, RevisionEntityInformation revisionEntityInformation, EntityManager entityManager) { - Assert.notNull(revisionEntityInformation, "RevisionEntityInformation must not be null!"); + Assert.notNull(revisionEntityInformation, "RevisionEntityInformation must not be null"); this.entityInformation = entityInformation; this.entityManager = entityManager; @@ -91,7 +91,7 @@ public Optional> findLastChangeRevision(ID id) { .setMaxResults(1) // .getResultList(); - Assert.state(singleResult.size() <= 1, "We expect at most one result."); + Assert.state(singleResult.size() <= 1, "We expect at most one result"); if (singleResult.isEmpty()) { return Optional.empty(); @@ -104,14 +104,14 @@ public Optional> findLastChangeRevision(ID id) { @SuppressWarnings("unchecked") public Optional> findRevision(ID id, N revisionNumber) { - Assert.notNull(id, "Identifier must not be null!"); - Assert.notNull(revisionNumber, "Revision number must not be null!"); + Assert.notNull(id, "Identifier must not be null"); + Assert.notNull(revisionNumber, "Revision number must not be null"); List singleResult = (List) createBaseQuery(id) // .add(AuditEntity.revisionNumber().eq(revisionNumber)) // .getResultList(); - Assert.state(singleResult.size() <= 1, "We expect at most one result."); + Assert.state(singleResult.size() <= 1, "We expect at most one result"); if (singleResult.isEmpty()) { return Optional.empty(); @@ -185,7 +185,7 @@ static class QueryResult { Assert.notNull(data, "Data must not be null"); Assert.isTrue( // data.length == 3, // - () -> String.format("Data must have length three, but has length %d.", data.length)); + () -> String.format("Data must have length three, but has length %d", data.length)); Assert.isTrue( // data[2] instanceof RevisionType, // () -> String.format("The third array element must be of type Revision type, but is of type %s", diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java index 65d22ff772..c441ef9542 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java @@ -40,7 +40,7 @@ public class ReflectionRevisionEntityInformation implements RevisionEntityInform */ public ReflectionRevisionEntityInformation(Class revisionEntityClass) { - Assert.notNull(revisionEntityClass, "Revision entity type must not be null!"); + Assert.notNull(revisionEntityClass, "Revision entity type must not be null"); AnnotationDetectionFieldCallback fieldCallback = new AnnotationDetectionFieldCallback(RevisionNumber.class); ReflectionUtils.doWithFields(revisionEntityClass, fieldCallback); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java index fc8732ec59..6949090e4c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java @@ -96,9 +96,9 @@ public static Predicate getPredicate(Root root, CriteriaBuilder cb, Examp public static Predicate getPredicate(Root root, CriteriaBuilder cb, Example example, EscapeCharacter escapeCharacter) { - Assert.notNull(root, "Root must not be null!"); - Assert.notNull(cb, "CriteriaBuilder must not be null!"); - Assert.notNull(example, "Example must not be null!"); + Assert.notNull(root, "Root must not be null"); + Assert.notNull(cb, "CriteriaBuilder must not be null"); + Assert.notNull(example, "Example must not be null"); ExampleMatcher matcher = example.getMatcher(); @@ -167,7 +167,7 @@ static List getPredicates(String path, CriteriaBuilder cb, Path fr PathNode node = currentNode.add(attribute.getName(), attributeValue); if (node.spansCycle()) { throw new InvalidDataAccessApiUsageException( - String.format("Path '%s' from root %s must not span a cyclic property reference!%n%s", currentPath, + String.format("Path '%s' from root %s must not span a cyclic property reference%n%s", currentPath, ClassUtils.getShortName(probeType), node)); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index e43754cb08..5adc6fab85 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -145,7 +145,7 @@ public static JpaSort of(Direction direction, Path... paths) { */ public JpaSort and(@Nullable Direction direction, Attribute... attributes) { - Assert.notNull(attributes, "Attributes must not be null!"); + Assert.notNull(attributes, "Attributes must not be null"); return and(direction, paths(attributes)); } @@ -159,7 +159,7 @@ public JpaSort and(@Nullable Direction direction, Attribute... attributes) */ public JpaSort and(@Nullable Direction direction, Path... paths) { - Assert.notNull(paths, "Paths must not be null!"); + Assert.notNull(paths, "Paths must not be null"); List existing = new ArrayList<>(); @@ -179,7 +179,7 @@ public JpaSort and(@Nullable Direction direction, Path... paths) { */ public JpaSort andUnsafe(@Nullable Direction direction, String... properties) { - Assert.notEmpty(properties, "Properties must not be empty!"); + Assert.notEmpty(properties, "Properties must not be empty"); List orders = new ArrayList<>(); @@ -202,8 +202,8 @@ public JpaSort andUnsafe(@Nullable Direction direction, String... properties) { */ private static Path[] paths(Attribute[] attributes) { - Assert.notNull(attributes, "Attributes must not be null!"); - Assert.notEmpty(attributes, "Attributes must not be empty!"); + Assert.notNull(attributes, "Attributes must not be null"); + Assert.notEmpty(attributes, "Attributes must not be empty"); Path[] paths = new Path[attributes.length]; @@ -233,7 +233,7 @@ private static List combine(List orders, @Nullable Direction direc */ public static , T, S> Path path(A attribute) { - Assert.notNull(attribute, "Attribute must not be null!"); + Assert.notNull(attribute, "Attribute must not be null"); return new Path<>(Collections.singletonList(attribute)); } @@ -245,7 +245,7 @@ public static , T, S> Path path(A attribute) { */ public static

, T, S> Path path(P attribute) { - Assert.notNull(attribute, "Attribute must not be null!"); + Assert.notNull(attribute, "Attribute must not be null"); return new Path<>(Collections.singletonList(attribute)); } @@ -268,9 +268,9 @@ public static JpaSort unsafe(String... properties) { */ public static JpaSort unsafe(Direction direction, String... properties) { - Assert.notNull(direction, "Direction must not be null!"); - Assert.notEmpty(properties, "Properties must not be empty!"); - Assert.noNullElements(properties, "Properties must not contain null values!"); + Assert.notNull(direction, "Direction must not be null"); + Assert.notEmpty(properties, "Properties must not be empty"); + Assert.noNullElements(properties, "Properties must not contain null values"); return unsafe(direction, Arrays.asList(properties)); } @@ -284,7 +284,7 @@ public static JpaSort unsafe(Direction direction, String... properties) { */ public static JpaSort unsafe(Direction direction, List properties) { - Assert.notEmpty(properties, "Properties must not be empty!"); + Assert.notEmpty(properties, "Properties must not be empty"); List orders = new ArrayList<>(properties.size()); @@ -330,7 +330,7 @@ public , U> Path dot(A attribute) { private List> add(Attribute attribute) { - Assert.notNull(attribute, "Attribute must not be null!"); + Assert.notNull(attribute, "Attribute must not be null"); List> newAttributes = new ArrayList>(attributes.size() + 1); newAttributes.addAll(attributes); @@ -415,8 +415,8 @@ public JpaOrder with(NullHandling nullHandling) { */ public Sort withUnsafe(String... properties) { - Assert.notEmpty(properties, "Properties must not be empty!"); - Assert.noNullElements(properties, "Properties must not contain null values!"); + Assert.notEmpty(properties, "Properties must not be empty"); + Assert.noNullElements(properties, "Properties must not contain null values"); List orders = new ArrayList<>(properties.length); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java index d3581f9d99..d76ca704ca 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java @@ -44,7 +44,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) getBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME, beanFactory); } catch (NoSuchBeanDefinitionException o_O) { throw new IllegalStateException( - "Invalid auditing setup! Make sure you've used @EnableJpaAuditing or correctly!", o_O); + "Invalid auditing setup; Make sure you've used @EnableJpaAuditing or correctly", o_O); } for (String beanName : getEntityManagerFactoryBeanNames(beanFactory)) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java index ca73ad9d9b..25dabceb61 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java @@ -70,7 +70,7 @@ public class AuditingEntityListener { */ public void setAuditingHandler(ObjectFactory auditingHandler) { - Assert.notNull(auditingHandler, "AuditingHandler must not be null!"); + Assert.notNull(auditingHandler, "AuditingHandler must not be null"); this.handler = auditingHandler; } @@ -83,7 +83,7 @@ public void setAuditingHandler(ObjectFactory auditingHandler) { @PrePersist public void touchForCreate(Object target) { - Assert.notNull(target, "Entity must not be null!"); + Assert.notNull(target, "Entity must not be null"); if (handler != null) { @@ -103,7 +103,7 @@ public void touchForCreate(Object target) { @PreUpdate public void touchForUpdate(Object target) { - Assert.notNull(target, "Entity must not be null!"); + Assert.notNull(target, "Entity must not be null"); if (handler != null) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index 0d129cccfc..a42e772660 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -54,8 +54,8 @@ public class JpaMetamodelMappingContext */ public JpaMetamodelMappingContext(Set models) { - Assert.notNull(models, "JPA metamodel must not be null!"); - Assert.notEmpty(models, "JPA metamodel must not be empty!"); + Assert.notNull(models, "JPA metamodel must not be null"); + Assert.notEmpty(models, "JPA metamodel must not be empty"); this.models = new Metamodels(models); this.persistenceProvider = PersistenceProvider.fromMetamodel(models.iterator().next()); @@ -137,7 +137,7 @@ public JpaMetamodel getRequiredMetamodel(TypeInformation type) { JpaMetamodel metamodel = getMetamodel(type); if (metamodel == null) { - throw new IllegalArgumentException(String.format("Required JpaMetamodel not found for %s!", type)); + throw new IllegalArgumentException(String.format("Required JpaMetamodel not found for %s", type)); } return metamodel; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index 2165ce61bf..e55d44a230 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -41,7 +41,7 @@ class JpaPersistentEntityImpl extends BasicPersistentEntity information, ProxyIdAccessor p super(information, null); - Assert.notNull(proxyIdAccessor, "ProxyIdAccessor must not be null!"); + Assert.notNull(proxyIdAccessor, "ProxyIdAccessor must not be null"); this.proxyIdAccessor = proxyIdAccessor; this.metamodel = metamodel; } @@ -113,7 +113,7 @@ private static class JpaProxyAwareIdentifierAccessor extends IdPropertyIdentifie super(entity, bean); - Assert.notNull(proxyIdAccessor, "Proxy identifier accessor must not be null!"); + Assert.notNull(proxyIdAccessor, "Proxy identifier accessor must not be null"); this.proxyIdAccessor = proxyIdAccessor; this.bean = bean; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index 46d72642ed..6ecb5270e3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -99,7 +99,7 @@ public JpaPersistentPropertyImpl(JpaMetamodel metamodel, Property property, super(property, owner, simpleTypeHolder); - Assert.notNull(metamodel, "Metamodel must not be null!"); + Assert.notNull(metamodel, "Metamodel must not be null"); this.isAssociation = Lazy.of(() -> super.isAssociation() // || ASSOCIATION_ANNOTATIONS.stream().anyMatch(this::isAnnotationPresent)); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java index dc3be2c4a7..ab68486d8e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java @@ -61,8 +61,8 @@ public static boolean isMetamodelOfType(Metamodel metamodel, String type) { private static boolean isOfType(Object source, String typeName, @Nullable ClassLoader classLoader) { - Assert.notNull(source, "Source instance must not be null!"); - Assert.hasText(typeName, "Target type name must not be null or empty!"); + Assert.notNull(source, "Source instance must not be null"); + Assert.hasText(typeName, "Target type name must not be null or empty"); try { return ClassUtils.forName(typeName, classLoader).isInstance(source); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index fa4f87f948..73a09f6313 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -200,7 +200,7 @@ private static PersistenceProvider cacheAndReturn(Class type, PersistenceProv */ public static PersistenceProvider fromEntityManager(EntityManager em) { - Assert.notNull(em, "EntityManager must not be null!"); + Assert.notNull(em, "EntityManager must not be null"); Class entityManagerType = em.getDelegate().getClass(); PersistenceProvider cachedProvider = CACHE.get(entityManagerType); @@ -229,7 +229,7 @@ public static PersistenceProvider fromEntityManager(EntityManager em) { */ public static PersistenceProvider fromMetamodel(Metamodel metamodel) { - Assert.notNull(metamodel, "Metamodel must not be null!"); + Assert.notNull(metamodel, "Metamodel must not be null"); Class metamodelType = metamodel.getClass(); PersistenceProvider cachedProvider = CACHE.get(metamodelType); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index 01412b744b..9073a843f9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -60,7 +60,7 @@ class JpaRepositoryBean extends CdiRepositoryBean { super(qualifiers, repositoryType, beanManager, detector); - Assert.notNull(entityManagerBean, "EntityManager bean must not be null!"); + Assert.notNull(entityManagerBean, "EntityManager bean must not be null"); this.entityManagerBean = entityManagerBean; this.queryRewriterProvider = new BeanManagerQueryRewriterProvider(beanManager); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index 71634d5bc2..9cdb3bba1e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -52,7 +52,7 @@ public class JpaRepositoryExtension extends CdiRepositoryExtensionSupport { private final Map, Bean> entityManagers = new HashMap<>(); public JpaRepositoryExtension() { - LOGGER.info("Activating CDI extension for Spring Data JPA repositories."); + LOGGER.info("Activating CDI extension for Spring Data JPA repositories"); } /** @@ -71,7 +71,7 @@ void processBean(@Observes ProcessBean processBean) { if (type instanceof Class && EntityManager.class.isAssignableFrom((Class) type)) { Set qualifiers = new HashSet<>(bean.getQualifiers()); if (bean.isAlternative() || !entityManagers.containsKey(qualifiers)) { - LOGGER.debug(String.format("Discovered '%s' with qualifiers %s.", EntityManager.class.getName(), qualifiers)); + LOGGER.debug(String.format("Discovered '%s' with qualifiers %s", EntityManager.class.getName(), qualifiers)); entityManagers.put(qualifiers, (Bean) bean); } } @@ -94,7 +94,7 @@ void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanMan // Create the bean representing the repository. CdiRepositoryBean repositoryBean = createRepositoryBean(repositoryType, qualifiers, beanManager); - LOGGER.info(String.format("Registering bean for '%s' with qualifiers %s.", repositoryType.getName(), qualifiers)); + LOGGER.info(String.format("Registering bean for '%s' with qualifiers %s", repositoryType.getName(), qualifiers)); // Register the bean to the extension and the container. registerBean(repositoryBean); @@ -117,7 +117,7 @@ private CdiRepositoryBean createRepositoryBean(Class repositoryType, S Bean entityManagerBean = entityManagers.get(qualifiers); if (entityManagerBean == null) { - throw new UnsatisfiedResolutionException(String.format("Unable to resolve a bean for '%s' with qualifiers %s.", + throw new UnsatisfiedResolutionException(String.format("Unable to resolve a bean for '%s' with qualifiers %s", EntityManager.class.getName(), qualifiers)); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java index 3b080f7b7a..79379625b7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java @@ -97,8 +97,8 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { if (!ClassUtils.isPresent(BEAN_CONFIGURER_ASPECT_CLASS_NAME, getClass().getClassLoader())) { parserContext.getReaderContext().error( "Could not configure Spring Data JPA auditing-feature because" - + " spring-aspects.jar is not on the classpath!\n" - + "If you want to use auditing please add spring-aspects.jar to the classpath.", element); + + " spring-aspects.jar is not on the classpath;\n" + + "If you want to use auditing please add spring-aspects.jar to the classpath", element); } RootBeanDefinition def = new RootBeanDefinition(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java index 9e536f42ef..fc0404169f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java @@ -70,8 +70,8 @@ protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingCon @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { - Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!"); - Assert.notNull(registry, "BeanDefinitionRegistry must not be null!"); + Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null"); + Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); registerBeanConfigurerAspectIfNecessary(registry); super.registerBeanDefinitions(annotationMetadata, registry); @@ -106,10 +106,10 @@ private void registerBeanConfigurerAspectIfNecessary(BeanDefinitionRegistry regi } if (!ClassUtils.isPresent(BEAN_CONFIGURER_ASPECT_CLASS_NAME, getClass().getClassLoader())) { - throw new BeanDefinitionStoreException(BEAN_CONFIGURER_ASPECT_CLASS_NAME + " not found. \n" + throw new BeanDefinitionStoreException(BEAN_CONFIGURER_ASPECT_CLASS_NAME + " not found; \n" + "Could not configure Spring Data JPA auditing-feature because" - + " spring-aspects.jar is not on the classpath!\n" - + "If you want to use auditing please add spring-aspects.jar to the classpath."); + + " spring-aspects.jar is not on the classpath;\n" + + "If you want to use auditing please add spring-aspects.jar to the classpath"); } RootBeanDefinition def = new RootBeanDefinition(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index 96fff0518d..1d3d10b0fe 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -69,7 +69,7 @@ protected JpaMetamodelMappingContext createInstance() { context.initialize(); if (LOG.isDebugEnabled()) { - LOG.debug("Finished initializing JpaMetamodelMappingContext!"); + LOG.debug("Finished initializing JpaMetamodelMappingContext"); } return context; @@ -83,7 +83,7 @@ protected JpaMetamodelMappingContext createInstance() { private Set getMetamodels() { if (beanFactory == null) { - throw new IllegalStateException("BeanFactory must not be null!"); + throw new IllegalStateException("BeanFactory must not be null"); } Collection factories = BeanFactoryUtils diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 52e9c3aa96..7fe6bafdea 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -80,8 +80,8 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { */ public AbstractJpaQuery(JpaQueryMethod method, EntityManager em) { - Assert.notNull(method, "JpaQueryMethod must not be null!"); - Assert.notNull(em, "EntityManager must not be null!"); + Assert.notNull(method, "JpaQueryMethod must not be null"); + Assert.notNull(em, "EntityManager must not be null"); this.method = method; this.em = em; @@ -198,8 +198,8 @@ protected T applyHints(T query, JpaQueryMethod method) { */ protected void applyQueryHint(T query, QueryHint hint) { - Assert.notNull(query, "Query must not be null!"); - Assert.notNull(hint, "QueryHint must not be null!"); + Assert.notNull(query, "Query must not be null"); + Assert.notNull(hint, "QueryHint must not be null"); query.setHint(hint.name(), hint.value()); } @@ -297,7 +297,7 @@ static class TupleConverter implements Converter { */ public TupleConverter(ReturnedType type) { - Assert.notNull(type, "Returned type must not be null!"); + Assert.notNull(type, "Returned type must not be null"); this.type = type; } @@ -333,7 +333,7 @@ public Object convert(Object source) { */ private static class TupleBackedMap implements Map { - private static final String UNMODIFIABLE_MESSAGE = "A TupleBackedMap cannot be modified."; + private static final String UNMODIFIABLE_MESSAGE = "A TupleBackedMap cannot be modified"; private final Tuple tuple; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 20611985b0..64e61530f6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -70,10 +70,10 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri super(method, em); - Assert.hasText(queryString, "Query string must not be null or empty!"); - Assert.notNull(evaluationContextProvider, "ExpressionEvaluationContextProvider must not be null!"); - Assert.notNull(parser, "Parser must not be null!"); - Assert.notNull(queryRewriter, "QueryRewriter must not be null!"); + Assert.hasText(queryString, "Query string must not be null or empty"); + Assert.notNull(evaluationContextProvider, "ExpressionEvaluationContextProvider must not be null"); + Assert.notNull(parser, "Parser must not be null"); + Assert.notNull(queryRewriter, "QueryRewriter must not be null"); this.evaluationContextProvider = evaluationContextProvider; this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser, @@ -87,7 +87,7 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri this.queryRewriter = queryRewriter; Assert.isTrue(method.isNativeQuery() || !query.usesJdbcStyleParameters(), - "JDBC style parameters (?) are not supported for JPA queries."); + "JDBC style parameters (?) are not supported for JPA queries"); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java index 56b1551e9d..de2d8416a7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java @@ -38,7 +38,7 @@ public class DefaultJpaEntityMetadata implements JpaEntityMetadata { */ public DefaultJpaEntityMetadata(Class domainType) { - Assert.notNull(domainType, "Domain type must not be null!"); + Assert.notNull(domainType, "Domain type must not be null"); this.domainType = domainType; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java index 725719785d..a2cf998401 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java @@ -67,7 +67,7 @@ public List getParameterBindings() { @Override public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable String countQueryProjection) { - Assert.hasText(countQuery, "CountQuery must not be empty!"); + Assert.hasText(countQuery, "CountQuery must not be empty"); return DeclaredQuery.of(countQuery, false); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index ce399d3212..8bb25aa6c6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -85,9 +85,9 @@ static ExpressionBasedStringQuery from(DeclaredQuery query, JpaEntityMetadata private static String renderQueryIfExpressionOrReturnQuery(String query, JpaEntityMetadata metadata, SpelExpressionParser parser) { - Assert.notNull(query, "query must not be null!"); - Assert.notNull(metadata, "metadata must not be null!"); - Assert.notNull(parser, "parser must not be null!"); + Assert.notNull(query, "query must not be null"); + Assert.notNull(metadata, "metadata must not be null"); + Assert.notNull(parser, "parser must not be null"); if (!containsExpression(query)) { return query; 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 d8322ec8e5..9ac2bc7466 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 @@ -96,7 +96,7 @@ private ParsedType detectParsedType() { public String applySorting(Sort sort, @Nullable String alias) { String queryString = query.getQueryString(); - Assert.hasText(queryString, "Query must not be null or empty!"); + Assert.hasText(queryString, "Query must not be null or empty"); if (this.parsedType != ParsedType.SELECT) { return queryString; @@ -284,7 +284,7 @@ public String createCountQueryFor(@Nullable String countProjection) { return this.query.getQueryString(); } - Assert.hasText(this.query.getQueryString(), "OriginalQuery must not be null or empty!"); + Assert.hasText(this.query.getQueryString(), "OriginalQuery must not be null or empty"); Select selectStatement = parseSelectStatement(this.query.getQueryString()); PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); @@ -333,7 +333,7 @@ public String getProjection() { return ""; } - Assert.hasText(query.getQueryString(), "Query must not be null or empty!"); + Assert.hasText(query.getQueryString(), "Query must not be null or empty"); Select selectStatement = parseSelectStatement(query.getQueryString()); PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); @@ -360,7 +360,7 @@ private static Select parseSelectStatement(String query) { try { return (Select) CCJSqlParserUtil.parse(query); } catch (JSQLParserException e) { - throw new IllegalArgumentException("The query you provided is not a valid SQL Query!", e); + throw new IllegalArgumentException("The query you provided is not a valid SQL Query", e); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java index b8e7a561b3..30152a169b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java @@ -96,13 +96,13 @@ public static QueryHints getFetchGraphHint(EntityManager em, @Nullable JpaEntity @Nullable private static EntityGraph tryGetFetchGraph(EntityManager em, JpaEntityGraph jpaEntityGraph, Class entityType) { - Assert.notNull(em, "EntityManager must not be null!"); - Assert.notNull(jpaEntityGraph, "EntityGraph must not be null!"); - Assert.notNull(entityType, "EntityType must not be null!"); + Assert.notNull(em, "EntityManager must not be null"); + Assert.notNull(jpaEntityGraph, "EntityGraph must not be null"); + Assert.notNull(entityType, "EntityType must not be null"); - Assert.isTrue(JPA21_AVAILABLE, "The EntityGraph-Feature requires at least a JPA 2.1 persistence provider!"); + Assert.isTrue(JPA21_AVAILABLE, "The EntityGraph-Feature requires at least a JPA 2.1 persistence provider"); Assert.isTrue(GET_ENTITY_GRAPH_METHOD != null, - "It seems that you have the JPA 2.1 API but a JPA 2.0 implementation on the classpath!"); + "It seems that you have the JPA 2.1 API but a JPA 2.0 implementation on the classpath"); try { // first check whether an entityGraph with that name is already registered. @@ -125,10 +125,10 @@ private static EntityGraph tryGetFetchGraph(EntityManager em, JpaEntityGraph private static EntityGraph createDynamicEntityGraph(EntityManager em, JpaEntityGraph jpaEntityGraph, Class entityType) { - Assert.notNull(em, "EntityManager must not be null!"); - Assert.notNull(jpaEntityGraph, "JpaEntityGraph must not be null!"); - Assert.notNull(entityType, "Entity type must not be null!"); - Assert.isTrue(jpaEntityGraph.isAdHocEntityGraph(), "The given " + jpaEntityGraph + " is not dynamic!"); + Assert.notNull(em, "EntityManager must not be null"); + Assert.notNull(jpaEntityGraph, "JpaEntityGraph must not be null"); + Assert.notNull(entityType, "Entity type must not be null"); + Assert.isTrue(jpaEntityGraph.isAdHocEntityGraph(), "The given " + jpaEntityGraph + " is not dynamic"); EntityGraph entityGraph = em.createEntityGraph(entityType); configureFetchGraphFrom(jpaEntityGraph, entityGraph); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java index e822ef9869..759a143e28 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java @@ -60,8 +60,8 @@ public JpaEntityGraph(EntityGraph entityGraph, String nameFallback) { */ public JpaEntityGraph(String name, EntityGraphType type, @Nullable String[] attributePaths) { - Assert.hasText(name, "The name of an EntityGraph must not be null or empty!"); - Assert.notNull(type, "FetchGraphType must not be null!"); + Assert.hasText(name, "The name of an EntityGraph must not be null or empty"); + Assert.notNull(type, "FetchGraphType must not be null"); this.name = name; this.type = type; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index 5a5ce1d22f..329c01335d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -85,7 +85,7 @@ protected JpaParameter(MethodParameter parameter) { if (!isDateParameter() && hasTemporalParamAnnotation()) { throw new IllegalArgumentException( - Temporal.class.getSimpleName() + " annotation is only allowed on Date parameter!"); + Temporal.class.getSimpleName() + " annotation is only allowed on Date parameter"); } } @@ -127,7 +127,7 @@ TemporalType getRequiredTemporalType() throws IllegalStateException { return temporalType; } - throw new IllegalStateException(String.format("Required temporal type not found for %s!", getType())); + throw new IllegalStateException(String.format("Required temporal type not found for %s", getType())); } private boolean hasTemporalParamAnnotation() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 8a6527b119..836cca29fe 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -225,8 +225,8 @@ private class PredicateBuilder { */ public PredicateBuilder(Part part, Root root) { - Assert.notNull(part, "Part must not be null!"); - Assert.notNull(root, "Root must not be null!"); + Assert.notNull(part, "Part must not be null"); + Assert.notNull(root, "Root must not be null"); this.part = part; this.root = root; } @@ -312,7 +312,7 @@ public Predicate build() { case IS_NOT_EMPTY: if (!property.getLeafProperty().isCollection()) { - throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties!"); + throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties"); } Expression> collectionPath = traversePath(root, property); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 2cc12a5a1a..0080670756 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -81,8 +81,8 @@ public abstract class JpaQueryExecution { @Nullable public Object execute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { - Assert.notNull(query, "AbstractJpaQuery must not be null!"); - Assert.notNull(accessor, "JpaParametersParameterAccessor must not be null!"); + Assert.notNull(query, "AbstractJpaQuery must not be null"); + Assert.notNull(accessor, "JpaParametersParameterAccessor must not be null"); Object result; @@ -211,7 +211,7 @@ static class ModifyingExecution extends JpaQueryExecution { */ public ModifyingExecution(JpaQueryMethod method, EntityManager em) { - Assert.notNull(em, "The EntityManager must not be null."); + Assert.notNull(em, "The EntityManager must not be null"); Class returnType = method.getReturnType(); @@ -219,7 +219,7 @@ public ModifyingExecution(JpaQueryMethod method, EntityManager em) { boolean isInt = ClassUtils.isAssignable(returnType, Integer.class); Assert.isTrue(isInt || isVoid, - "Modifying queries can only use void or int/Integer as return type! Offending method: " + method); + "Modifying queries can only use void or int/Integer as return type; Offending method: " + method); this.em = em; this.flush = method.getFlushAutomatically(); @@ -294,7 +294,7 @@ protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccesso */ static class ProcedureExecution extends JpaQueryExecution { - private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a @Procedure method without a surrounding transaction that keeps the connection open so that the ResultSet can actually be consumed. Make sure the consumer code uses @Transactional or any other way of declaring a (read-only) transaction."; + private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a @Procedure method without a surrounding transaction that keeps the connection open so that the ResultSet can actually be consumed; Make sure the consumer code uses @Transactional or any other way of declaring a (read-only) transaction"; @Override protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAccessor accessor) { @@ -331,7 +331,7 @@ protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAcce */ static class StreamExecution extends JpaQueryExecution { - private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction."; + private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed; Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction"; private static Method streamMethod = ReflectionUtils.findMethod(Query.class, "getResultStream"); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 4df1c9ab8c..7fcab82bd5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -80,8 +80,8 @@ private abstract static class AbstractQueryLookupStrategy implements QueryLookup public AbstractQueryLookupStrategy(EntityManager em, JpaQueryMethodFactory queryMethodFactory, QueryRewriterProvider queryRewriterProvider) { - Assert.notNull(em, "EntityManager must not be null!"); - Assert.notNull(queryMethodFactory, "JpaQueryMethodFactory must not be null!"); + Assert.notNull(em, "EntityManager must not be null"); + Assert.notNull(queryMethodFactory, "JpaQueryMethodFactory must not be null"); this.em = em; this.queryMethodFactory = queryMethodFactory; @@ -164,7 +164,7 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter quer if (method.hasAnnotatedQueryName()) { LOG.warn(String.format( - "Query method %s is annotated with both, a query and a query name. Using the declared query.", method)); + "Query method %s is annotated with both, a query and a query name; Using the declared query", method)); } return JpaQueryFactory.INSTANCE.fromMethodWithQueryString(method, em, method.getRequiredAnnotatedQuery(), @@ -238,8 +238,8 @@ public CreateIfNotFoundQueryLookupStrategy(EntityManager em, JpaQueryMethodFacto super(em, queryMethodFactory, queryRewriterProvider); - Assert.notNull(createStrategy, "CreateQueryLookupStrategy must not be null!"); - Assert.notNull(lookupStrategy, "DeclaredQueryLookupStrategy must not be null!"); + Assert.notNull(createStrategy, "CreateQueryLookupStrategy must not be null"); + Assert.notNull(lookupStrategy, "DeclaredQueryLookupStrategy must not be null"); this.createStrategy = createStrategy; this.lookupStrategy = lookupStrategy; @@ -272,8 +272,8 @@ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory @Nullable Key key, QueryMethodEvaluationContextProvider evaluationContextProvider, QueryRewriterProvider queryRewriterProvider, EscapeCharacter escape) { - Assert.notNull(em, "EntityManager must not be null!"); - Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!"); + Assert.notNull(em, "EntityManager must not be null"); + Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null"); switch (key != null ? key : Key.CREATE_IF_NOT_FOUND) { case CREATE: @@ -287,7 +287,7 @@ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory new DeclaredQueryLookupStrategy(em, queryMethodFactory, evaluationContextProvider, queryRewriterProvider), queryRewriterProvider); default: - throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key)); + throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s", key)); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index e3138c5bd7..d510577d34 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -108,8 +108,8 @@ protected JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionF super(method, metadata, factory); - Assert.notNull(method, "Method must not be null!"); - Assert.notNull(extractor, "Query extractor must not be null!"); + Assert.notNull(method, "Method must not be null"); + Assert.notNull(extractor, "Query extractor must not be null"); this.method = method; this.returnType = potentiallyUnwrapReturnTypeFor(metadata, method); @@ -137,7 +137,7 @@ protected JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionF this.entityMetadata = Lazy.of(() -> new DefaultJpaEntityMetadata<>(getDomainClass())); Assert.isTrue(!(isModifyingQuery() && getParameters().hasSpecialParameter()), - String.format("Modifying method must not contain %s!", Parameters.TYPES)); + String.format("Modifying method must not contain %s", Parameters.TYPES)); assertParameterNamesInAnnotatedQuery(); } @@ -171,7 +171,7 @@ private void assertParameterNamesInAnnotatedQuery() { || !annotatedQuery.contains(String.format(":%s", parameter.getName().get())) && !annotatedQuery.contains(String.format("#%s", parameter.getName().get()))) { throw new IllegalStateException( - String.format("Using named parameters for method %s but parameter '%s' not found in annotated query '%s'!", + String.format("Using named parameters for method %s but parameter '%s' not found in annotated query '%s'", method, parameter.getName(), annotatedQuery)); } } @@ -295,7 +295,7 @@ public String getRequiredAnnotatedQuery() throws IllegalStateException { return query; } - throw new IllegalStateException(String.format("No annotated query found for query method %s!", getName())); + throw new IllegalStateException(String.format("No annotated query found for query method %s", getName())); } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java index fb80a62d54..77dba961c9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java @@ -71,13 +71,13 @@ public byte[] convert(@Nullable Blob source) { } } catch (SQLException | IOException e) { - throw new DataRetrievalFailureException("Couldn't retrieve data from blob.", e); + throw new DataRetrievalFailureException("Couldn't retrieve data from blob", e); } finally { if (blobStream != null) { try { blobStream.close(); } catch (IOException e) { - throw new CleanupFailureDataAccessException("Couldn't close binary stream for given blob.", e); + throw new CleanupFailureDataAccessException("Couldn't close binary stream for given blob", e); } } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index e79a39a3e8..b2b8a66d8a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -40,9 +40,9 @@ final class NamedQuery extends AbstractJpaQuery { private static final String CANNOT_EXTRACT_QUERY = "Your persistence provider does not support extracting the JPQL query from a " - + "named query thus you can't use Pageable inside your query method. Make sure you " + + "named query thus you can't use Pageable inside your query method; Make sure you " + "have a JpaDialect configured at your EntityManagerFactoryBean as this affects " - + "discovering the concrete persistence provider."; + + "discovering the concrete persistence provider"; private static final Log LOG = LogFactory.getLog(NamedQuery.class); @@ -69,8 +69,8 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { Parameters parameters = method.getParameters(); if (parameters.hasSortParameter()) { - throw new IllegalStateException(String.format("Finder method %s is backed " + "by a NamedQuery and must " - + "not contain a sort parameter as we cannot modify the query! Use @Query instead!", method)); + throw new IllegalStateException(String.format("Finder method %s is backed by a NamedQuery and must " + + "not contain a sort parameter as we cannot modify the query; Use @Query instead", method)); } this.namedCountQueryIsPresent = hasNamedQuery(em, countQueryName); @@ -89,7 +89,7 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { if (parameters.hasPageableParameter()) { LOG.warn(String.format( - "Finder method %s is backed by a NamedQuery but contains a Pageable parameter! Sorting delivered via this Pageable will not be applied!", + "Finder method %s is backed by a NamedQuery but contains a Pageable parameter; Sorting delivered via this Pageable will not be applied", method)); } @@ -149,7 +149,7 @@ public static RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em RepositoryQuery query = new NamedQuery(method, em); if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Found named query %s!", queryName)); + LOG.debug(String.format("Found named query %s", queryName)); } return query; } catch (IllegalArgumentException e) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 49d129cd0f..af76bb5476 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -33,7 +33,7 @@ */ public class ParameterBinder { - static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to use provide names for method parameters. Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters."; + static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to use provide names for method parameters; Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters"; private final JpaParameters parameters; private final Iterable parameterSetters; @@ -62,8 +62,8 @@ public class ParameterBinder { public ParameterBinder(JpaParameters parameters, Iterable parameterSetters, boolean useJpaForPaging) { - Assert.notNull(parameters, "JpaParameters must not be null!"); - Assert.notNull(parameterSetters, "Parameter setters must not be null!"); + Assert.notNull(parameters, "JpaParameters must not be null"); + Assert.notNull(parameterSetters, "Parameter setters must not be null"); this.parameters = parameters; this.parameterSetters = parameterSetters; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java index a16547cea6..77ebedf3d8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java @@ -44,7 +44,7 @@ class ParameterBinderFactory { */ static ParameterBinder createBinder(JpaParameters parameters) { - Assert.notNull(parameters, "JpaParameters must not be null!"); + Assert.notNull(parameters, "JpaParameters must not be null"); QueryParameterSetterFactory setterFactory = QueryParameterSetterFactory.basic(parameters); List bindings = getBindings(parameters); @@ -63,8 +63,8 @@ static ParameterBinder createBinder(JpaParameters parameters) { */ static ParameterBinder createCriteriaBinder(JpaParameters parameters, List> metadata) { - Assert.notNull(parameters, "JpaParameters must not be null!"); - Assert.notNull(metadata, "Parameter metadata must not be null!"); + Assert.notNull(parameters, "JpaParameters must not be null"); + Assert.notNull(metadata, "Parameter metadata must not be null"); QueryParameterSetterFactory setterFactory = QueryParameterSetterFactory.forCriteriaQuery(parameters, metadata); List bindings = getBindings(parameters); @@ -87,10 +87,10 @@ static ParameterBinder createCriteriaBinder(JpaParameters parameters, List bindings = query.getParameterBindings(); QueryParameterSetterFactory expressionSetterFactory = QueryParameterSetterFactory.parsing(parser, diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index e214ffe4c6..97696f1cfe 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -96,9 +96,9 @@ public ParameterMetadataProvider(CriteriaBuilder builder, Parameters param private ParameterMetadataProvider(CriteriaBuilder builder, @Nullable Iterator bindableParameterValues, Parameters parameters, EscapeCharacter escape) { - Assert.notNull(builder, "CriteriaBuilder must not be null!"); - Assert.notNull(parameters, "Parameters must not be null!"); - Assert.notNull(escape, "EscapeCharacter must not be null!"); + Assert.notNull(builder, "CriteriaBuilder must not be null"); + Assert.notNull(parameters, "Parameters must not be null"); + Assert.notNull(escape, "EscapeCharacter must not be null"); this.builder = builder; this.parameters = parameters.getBindableParameters().iterator(); @@ -122,7 +122,7 @@ public List> getExpressions() { @SuppressWarnings("unchecked") public ParameterMetadata next(Part part) { - Assert.isTrue(parameters.hasNext(), () -> String.format("No parameter available for part %s.", part)); + Assert.isTrue(parameters.hasNext(), () -> String.format("No parameter available for part %s", part)); Parameter parameter = parameters.next(); return (ParameterMetadata) next(part, parameter.getType(), parameter); @@ -155,7 +155,7 @@ public ParameterMetadata next(Part part, Class type) { */ private ParameterMetadata next(Part part, Class type, Parameter parameter) { - Assert.notNull(type, "Type must not be null!"); + Assert.notNull(type, "Type must not be null"); /* * We treat Expression types as Object vales since the real value to be bound as a parameter is determined at query time. @@ -233,7 +233,7 @@ public boolean isIsNullParameter() { @Nullable public Object prepare(Object value) { - Assert.notNull(value, "Value must not be null!"); + Assert.notNull(value, "Value must not be null"); Class expressionType = expression.getJavaType(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 28c08f47ba..7ec72f9565 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -93,7 +93,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { } catch (Exception o_O) { throw new IllegalArgumentException( - String.format("Failed to create query for method %s! %s", method, o_O.getMessage()), o_O); + String.format("Failed to create query for method %s; %s", method, o_O.getMessage()), o_O); } } @@ -147,7 +147,7 @@ private static void throwExceptionOnArgumentMismatch(String methodName, Part par if (!parameters.getBindableParameters().hasParameterAt(index)) { throw new IllegalStateException(String.format( - "Method %s expects at least %d arguments but only found %d. This leaves an operator of type %s for property %s unbound.", + "Method %s expects at least %d arguments but only found %d; This leaves an operator of type %s for property %s unbound", methodName, index + 1, index, type.name(), property)); } @@ -163,7 +163,7 @@ private static void throwExceptionOnArgumentMismatch(String methodName, Part par private static String wrongParameterTypeMessage(String methodName, String property, Type operatorType, String expectedArgumentType, JpaParameter parameter) { - return String.format("Operator %s on %s requires a %s argument, found %s in method %s.", operatorType.name(), + return String.format("Operator %s on %s requires a %s argument, found %s in method %s", operatorType.name(), property, expectedArgumentType, parameter.getType(), methodName); } @@ -223,7 +223,7 @@ public Query createQuery(JpaParametersParameterAccessor accessor) { } if (parameterBinder == null) { - throw new IllegalStateException("ParameterBinder is null!"); + throw new IllegalStateException("ParameterBinder is null"); } TypedQuery query = createQuery(criteriaQuery); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java index 156a49aeec..8365122cdd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java @@ -68,7 +68,7 @@ private static boolean isJSqlParserInClassPath() { try { Class.forName("net.sf.jsqlparser.parser.JSqlParser", false, QueryEnhancerFactory.class.getClassLoader()); - LOG.info("JSqlParser is in classpath. If applicable JSqlParser will be used."); + LOG.info("JSqlParser is in classpath; If applicable JSqlParser will be used"); return true; } catch (ClassNotFoundException e) { return false; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index 8bc189e310..e3c40f18a6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -67,7 +67,7 @@ class NamedOrIndexedQueryParameterSetter implements QueryParameterSetter { NamedOrIndexedQueryParameterSetter(Function valueExtractor, Parameter parameter, @Nullable TemporalType temporalType) { - Assert.notNull(valueExtractor, "ValueExtractor must not be null!"); + Assert.notNull(valueExtractor, "ValueExtractor must not be null"); this.valueExtractor = valueExtractor; this.parameter = parameter; @@ -275,7 +275,7 @@ private static Class unwrapClass(Query query) { } catch (RuntimeException e) { - LogFactory.getLog(QueryMetadata.class).warn("Failed to unwrap actual class for Query proxy.", e); + LogFactory.getLog(QueryMetadata.class).warn("Failed to unwrap actual class for Query proxy", e); return queryType; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index 72986cf4b2..c27abbd2be 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -57,7 +57,7 @@ abstract class QueryParameterSetterFactory { */ static QueryParameterSetterFactory basic(JpaParameters parameters) { - Assert.notNull(parameters, "JpaParameters must not be null!"); + Assert.notNull(parameters, "JpaParameters must not be null"); return new BasicQueryParameterSetterFactory(parameters); } @@ -72,8 +72,8 @@ static QueryParameterSetterFactory basic(JpaParameters parameters) { */ static QueryParameterSetterFactory forCriteriaQuery(JpaParameters parameters, List> metadata) { - Assert.notNull(parameters, "JpaParameters must not be null!"); - Assert.notNull(metadata, "ParameterMetadata must not be null!"); + Assert.notNull(parameters, "JpaParameters must not be null"); + Assert.notNull(metadata, "ParameterMetadata must not be null"); return new CriteriaQueryParameterSetterFactory(parameters, metadata); } @@ -91,9 +91,9 @@ static QueryParameterSetterFactory forCriteriaQuery(JpaParameters parameters, Li static QueryParameterSetterFactory parsing(SpelExpressionParser parser, QueryMethodEvaluationContextProvider evaluationContextProvider, Parameters parameters) { - Assert.notNull(parser, "SpelExpressionParser must not be null!"); - Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!"); - Assert.notNull(parameters, "Parameters must not be null!"); + Assert.notNull(parser, "SpelExpressionParser must not be null"); + Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null"); + Assert.notNull(parameters, "Parameters must not be null"); return new ExpressionBasedQueryParameterSetterFactory(parser, evaluationContextProvider, parameters); } @@ -138,9 +138,9 @@ private static class ExpressionBasedQueryParameterSetterFactory extends QueryPar ExpressionBasedQueryParameterSetterFactory(SpelExpressionParser parser, QueryMethodEvaluationContextProvider evaluationContextProvider, Parameters parameters) { - Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!"); - Assert.notNull(parser, "SpelExpressionParser must not be null!"); - Assert.notNull(parameters, "Parameters must not be null!"); + Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null"); + Assert.notNull(parser, "SpelExpressionParser must not be null"); + Assert.notNull(parameters, "Parameters must not be null"); this.evaluationContextProvider = evaluationContextProvider; this.parser = parser; @@ -192,7 +192,7 @@ private static class BasicQueryParameterSetterFactory extends QueryParameterSett */ BasicQueryParameterSetterFactory(JpaParameters parameters) { - Assert.notNull(parameters, "JpaParameters must not be null!"); + Assert.notNull(parameters, "JpaParameters must not be null"); this.parameters = parameters; } @@ -200,7 +200,7 @@ private static class BasicQueryParameterSetterFactory extends QueryParameterSett @Override public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery declaredQuery) { - Assert.notNull(binding, "Binding must not be null."); + Assert.notNull(binding, "Binding must not be null"); JpaParameter parameter; @@ -214,7 +214,7 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla Assert.isTrue( // parameterIndex < bindableParameters.getNumberOfParameters(), // () -> String.format( // - "At least %s parameter(s) provided but only %s parameter(s) present in query.", // + "At least %s parameter(s) provided but only %s parameter(s) present in query", // binding.getRequiredPosition(), // bindableParameters.getNumberOfParameters() // ) // @@ -271,8 +271,8 @@ private static class CriteriaQueryParameterSetterFactory extends QueryParameterS */ CriteriaQueryParameterSetterFactory(JpaParameters parameters, List> metadata) { - Assert.notNull(parameters, "JpaParameters must not be null!"); - Assert.notNull(metadata, "Expressions must not be null!"); + Assert.notNull(parameters, "JpaParameters must not be null"); + Assert.notNull(metadata, "Expressions must not be null"); this.parameters = parameters; this.expressions = metadata; @@ -286,7 +286,7 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla Assert.isTrue( // parameterIndex < expressions.size(), // () -> String.format( // - "At least %s parameter(s) provided but only %s parameter(s) present in query.", // + "At least %s parameter(s) provided but only %s parameter(s) present in query", // binding.getRequiredPosition(), // expressions.size() // ) // @@ -372,7 +372,7 @@ private static String getName(@Nullable JpaParameter parameter, ParameterBinding } return parameter.isNamedParameter() // - ? parameter.getName().orElseThrow(() -> new IllegalArgumentException("o_O parameter needs to have a name!")) // + ? parameter.getName().orElseThrow(() -> new IllegalArgumentException("o_O parameter needs to have a name")) // : null; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 2a422e870f..bd39edc50b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -133,8 +133,8 @@ public abstract class QueryUtils { private static final Pattern FIELD_ALIAS_PATTERN; private static final String UNSAFE_PROPERTY_REFERENCE = "Sort expression '%s' must only contain property references or " - + "aliases used in the select clause. If you really want to use something other than that for sorting, please use " - + "JpaSort.unsafe(…)!"; + + "aliases used in the select clause; If you really want to use something other than that for sorting, please use " + + "JpaSort.unsafe(…)"; static { @@ -231,7 +231,7 @@ public static String getExistsQueryString(String entityName, String countQueryPl */ public static String getQueryString(String template, String entityName) { - Assert.hasText(entityName, "Entity name must not be null or empty!"); + Assert.hasText(entityName, "Entity name must not be null or empty"); return String.format(template, entityName); } @@ -257,7 +257,7 @@ public static String applySorting(String query, Sort sort) { */ public static String applySorting(String query, Sort sort, @Nullable String alias) { - Assert.hasText(query, "Query must not be null or empty!"); + Assert.hasText(query, "Query must not be null or empty"); if (sort.isUnsorted()) { return query; @@ -518,9 +518,9 @@ private static Integer findClose(final Integer open, final List closes, public static Query applyAndBind(String queryString, Iterable entities, EntityManager entityManager) { - Assert.notNull(queryString, "Querystring must not be null!"); - Assert.notNull(entities, "Iterable of entities must not be null!"); - Assert.notNull(entityManager, "EntityManager must not be null!"); + Assert.notNull(queryString, "Querystring must not be null"); + Assert.notNull(entities, "Iterable of entities must not be null"); + Assert.notNull(entityManager, "EntityManager must not be null"); Iterator iterator = entities.iterator(); @@ -581,7 +581,7 @@ public static String createCountQueryFor(String originalQuery) { @Deprecated public static String createCountQueryFor(String originalQuery, @Nullable String countProjection) { - Assert.hasText(originalQuery, "OriginalQuery must not be null or empty!"); + Assert.hasText(originalQuery, "OriginalQuery must not be null or empty"); Matcher matcher = COUNT_MATCH.matcher(originalQuery); String countQuery; @@ -621,7 +621,7 @@ public static String createCountQueryFor(String originalQuery, @Nullable String */ public static boolean hasNamedParameter(Query query) { - Assert.notNull(query, "Query must not be null!"); + Assert.notNull(query, "Query must not be null"); for (Parameter parameter : query.getParameters()) { @@ -661,8 +661,8 @@ public static List toOrders(Sort sort, From< return Collections.emptyList(); } - Assert.notNull(from, "From must not be null!"); - Assert.notNull(cb, "CriteriaBuilder must not be null!"); + Assert.notNull(from, "From must not be null"); + Assert.notNull(cb, "CriteriaBuilder must not be null"); List orders = new ArrayList<>(); @@ -682,7 +682,7 @@ public static List toOrders(Sort sort, From< */ public static boolean hasConstructorExpression(String query) { - Assert.hasText(query, "Query must not be null or empty!"); + Assert.hasText(query, "Query must not be null or empty"); return CONSTRUCTOR_EXPRESSION.matcher(query).find(); } @@ -696,7 +696,7 @@ public static boolean hasConstructorExpression(String query) { */ public static String getProjection(String query) { - Assert.hasText(query, "Query must not be null or empty!"); + Assert.hasText(query, "Query must not be null or empty"); Matcher matcher = PROJECTION_CLAUSE.matcher(query); String projection = matcher.find() ? matcher.group(1) : ""; @@ -767,7 +767,7 @@ static Expression toExpressionRecursively(From from, PropertyPath p return (Expression) join; } - PropertyPath nextProperty = Objects.requireNonNull(property.next(), "An element of the property path is null!"); + PropertyPath nextProperty = Objects.requireNonNull(property.next(), "An element of the property path is null"); // recurse with the next property return toExpressionRecursively(join, nextProperty, isForSelection, requiresOuterJoin); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index 1e765db9c3..c2e23738d5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -67,11 +67,11 @@ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryStrin super(method, em, queryString, countQueryString, queryRewriter, evaluationContextProvider, parser); - validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s!", method); + validateQuery(getQuery().getQueryString(), "Validation failed for query for method %s", method); if (method.isPageQuery()) { validateQuery(getCountQuery().getQueryString(), - String.format("Count query validation failed for method %s!", method)); + String.format("Count query validation failed for method %s", method)); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java index db63ce83c0..8d321db926 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java @@ -59,11 +59,11 @@ enum StoredProcedureAttributeSource { */ public StoredProcedureAttributes createFrom(Method method, JpaEntityMetadata entityMetadata) { - Assert.notNull(method, "Method must not be null!"); - Assert.notNull(entityMetadata, "EntityMetadata must not be null!"); + Assert.notNull(method, "Method must not be null"); + Assert.notNull(entityMetadata, "EntityMetadata must not be null"); Procedure procedure = AnnotatedElementUtils.findMergedAnnotation(method, Procedure.class); - Assert.notNull(procedure, "Method must have an @Procedure annotation!"); + Assert.notNull(procedure, "Method must have an @Procedure annotation"); NamedStoredProcedureQuery namedStoredProc = tryFindAnnotatedNamedStoredProcedureQuery(method, entityMetadata, procedure); @@ -179,9 +179,9 @@ private List extractOutputParametersFrom(NamedStoredPr private NamedStoredProcedureQuery tryFindAnnotatedNamedStoredProcedureQuery(Method method, JpaEntityMetadata entityMetadata, Procedure procedure) { - Assert.notNull(method, "Method must not be null!"); - Assert.notNull(entityMetadata, "EntityMetadata must not be null!"); - Assert.notNull(procedure, "Procedure must not be null!"); + Assert.notNull(method, "Method must not be null"); + Assert.notNull(entityMetadata, "EntityMetadata must not be null"); + Assert.notNull(procedure, "Procedure must not be null"); Class entityType = entityMetadata.getJavaType(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java index 36e981c844..40cfcd2443 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java @@ -63,8 +63,8 @@ class StoredProcedureAttributes { StoredProcedureAttributes(String procedureName, List outputProcedureParameters, boolean namedStoredProcedure) { - Assert.notNull(procedureName, "ProcedureName must not be null!"); - Assert.notNull(outputProcedureParameters, "OutputProcedureParameters must not be null!"); + Assert.notNull(procedureName, "ProcedureName must not be null"); + Assert.notNull(outputProcedureParameters, "OutputProcedureParameters must not be null"); Assert.isTrue(outputProcedureParameters.size() != 1 || outputProcedureParameters.get(0) != null, "ProcedureParameters must not have size 1 with a null value"); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index f4af59547f..e7a5b02f21 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -96,7 +96,7 @@ protected StoredProcedureQuery doCreateQuery(JpaParametersParameterAccessor acce @Override protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { - throw new UnsupportedOperationException("StoredProcedureQuery does not support count queries!"); + throw new UnsupportedOperationException("StoredProcedureQuery does not support count queries"); } /** @@ -110,7 +110,7 @@ protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor acc @Nullable Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) { - Assert.notNull(storedProcedureQuery, "StoredProcedureQuery must not be null!"); + Assert.notNull(storedProcedureQuery, "StoredProcedureQuery must not be null"); if (!procedureAttributes.hasReturnValue()) { return null; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 994a7837c4..36ec1ca172 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -68,7 +68,7 @@ class StringQuery implements DeclaredQuery { @SuppressWarnings("deprecation") StringQuery(String query, boolean isNative) { - Assert.hasText(query, "Query must not be null or empty!"); + Assert.hasText(query, "Query must not be null or empty"); this.isNative = isNative; this.bindings = new ArrayList<>(); @@ -170,8 +170,8 @@ enum ParameterBindingParser { private static final Pattern NUMBERED_STYLE_PARAM = Pattern.compile(" \\?(?=\\d)"); // ?[digit] private static final Pattern NAMED_STYLE_PARAM = Pattern.compile(" :\\w+"); // :[text] - private static final String MESSAGE = "Already found parameter binding with same index / parameter name but differing binding type! " - + "Already have: %s, found %s! If you bind a parameter multiple times make sure they use the same binding."; + private static final String MESSAGE = "Already found parameter binding with same index / parameter name but differing binding type; " + + "Already have: %s, found %s; If you bind a parameter multiple times make sure they use the same binding"; private static final int INDEXED_PARAMETER_GROUP = 4; private static final int NAMED_PARAMETER_GROUP = 6; private static final int COMPARISION_TYPE_GROUP = 1; @@ -244,7 +244,7 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St String typeSource = matcher.group(COMPARISION_TYPE_GROUP); Assert.isTrue(parameterIndexString != null || parameterName != null, - () -> String.format("We need either a name or an index! Offending query string: %s", query)); + () -> String.format("We need either a name or an index; Offending query string: %s", query)); String expression = spelExtractor.getParameter(parameterName == null ? parameterIndexString : parameterName); String replacement = null; @@ -258,7 +258,7 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St } if (usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) { - throw new IllegalArgumentException("Mixing of ? parameters and other forms like ?1 is not supported!"); + throw new IllegalArgumentException("Mixing of ? parameters and other forms like ?1 is not supported"); } switch (ParameterBindingType.of(typeSource)) { @@ -417,7 +417,7 @@ static ParameterBindingType of(String typeSource) { } } - throw new IllegalArgumentException(String.format("Unsupported parameter binding type %s!", typeSource)); + throw new IllegalArgumentException(String.format("Unsupported parameter binding type %s", typeSource)); } } } @@ -453,11 +453,11 @@ static class ParameterBinding { ParameterBinding(@Nullable String name, @Nullable Integer position, @Nullable String expression) { if (name == null) { - Assert.notNull(position, "Position must not be null!"); + Assert.notNull(position, "Position must not be null"); } if (position == null) { - Assert.notNull(name, "Name must not be null!"); + Assert.notNull(name, "Name must not be null"); } this.name = name; @@ -502,7 +502,7 @@ String getRequiredName() throws IllegalStateException { return name; } - throw new IllegalStateException(String.format("Required name for %s not available!", this)); + throw new IllegalStateException(String.format("Required name for %s not available", this)); } /** @@ -526,7 +526,7 @@ int getRequiredPosition() throws IllegalStateException { return position; } - throw new IllegalStateException(String.format("Required position for %s not available!", this)); + throw new IllegalStateException(String.format("Required position for %s not available", this)); } /** @@ -657,11 +657,11 @@ static class LikeParameterBinding extends ParameterBinding { super(name, null, expression); - Assert.hasText(name, "Name must not be null or empty!"); - Assert.notNull(type, "Type must not be null!"); + Assert.hasText(name, "Name must not be null or empty"); + Assert.notNull(type, "Type must not be null"); Assert.isTrue(SUPPORTED_TYPES.contains(type), - String.format("Type must be one of %s!", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); + String.format("Type must be one of %s", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); this.type = type; } @@ -687,11 +687,11 @@ static class LikeParameterBinding extends ParameterBinding { super(null, position, expression); - Assert.isTrue(position > 0, "Position must be greater than zero!"); - Assert.notNull(type, "Type must not be null!"); + Assert.isTrue(position > 0, "Position must be greater than zero"); + Assert.notNull(type, "Type must not be null"); Assert.isTrue(SUPPORTED_TYPES.contains(type), - String.format("Type must be one of %s!", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); + String.format("Type must be one of %s", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); this.type = type; } @@ -763,7 +763,7 @@ public String toString() { */ private static Type getLikeTypeFrom(String expression) { - Assert.hasText(expression, "Expression must not be null or empty!"); + Assert.hasText(expression, "Expression must not be null or empty"); if (expression.matches("%.*%")) { return Type.CONTAINING; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 69c9152ef5..5b286814ad 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -190,7 +190,7 @@ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { */ DefaultCrudMethodMetadata(Method method) { - Assert.notNull(method, "Method must not be null!"); + Assert.notNull(method, "Method must not be null"); this.lockModeType = findLockModeType(method); this.queryHints = findQueryHints(method, it -> true); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java index 1672f0ee41..4f0bb1dc00 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java @@ -44,8 +44,8 @@ public class DefaultJpaContext implements JpaContext { */ public DefaultJpaContext(Set entityManagers) { - Assert.notNull(entityManagers, "EntityManagers must not be null!"); - Assert.notEmpty(entityManagers, "EntityManagers must not be empty!"); + Assert.notNull(entityManagers, "EntityManagers must not be null"); + Assert.notEmpty(entityManagers, "EntityManagers must not be empty"); this.entityManagers = new LinkedMultiValueMap<>(); @@ -59,10 +59,10 @@ public DefaultJpaContext(Set entityManagers) { @Override public EntityManager getEntityManagerByManagedType(Class type) { - Assert.notNull(type, "Type must not be null!"); + Assert.notNull(type, "Type must not be null"); if (!entityManagers.containsKey(type)) { - throw new IllegalArgumentException(String.format("%s is not a managed type!", type)); + throw new IllegalArgumentException(String.format("%s is not a managed type", type)); } List candidates = this.entityManagers.get(type); @@ -72,6 +72,6 @@ public EntityManager getEntityManagerByManagedType(Class type) { } throw new IllegalArgumentException( - String.format("%s managed by more than one EntityManagers: %s!", type.getName(), candidates)); + String.format("%s managed by more than one EntityManagers: %s", type.getName(), candidates)); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java index 9d8d114516..67d19a5509 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java @@ -69,8 +69,8 @@ private DefaultQueryHints(JpaEntityInformation information, CrudMethodMeta */ public static QueryHints of(JpaEntityInformation information, CrudMethodMetadata metadata) { - Assert.notNull(information, "JpaEntityInformation must not be null!"); - Assert.notNull(metadata, "CrudMethodMetadata must not be null!"); + Assert.notNull(information, "JpaEntityInformation must not be null"); + Assert.notNull(metadata, "CrudMethodMetadata must not be null"); return new DefaultQueryHints(information, metadata, Optional.empty(), false); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index 315244a3a1..8e27ab733b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -81,7 +81,7 @@ private FetchableFluentQueryByExample(Example example, Class entityType, C @Override public FetchableFluentQuery sortBy(Sort sort) { - Assert.notNull(sort, "Sort must not be null!"); + Assert.notNull(sort, "Sort must not be null"); return new FetchableFluentQueryByExample<>(example, entityType, resultType, this.sort.and(sort), properties, finder, countOperation, existsOperation, entityManager, escapeCharacter); @@ -90,7 +90,7 @@ public FetchableFluentQuery sortBy(Sort sort) { @Override public FetchableFluentQuery as(Class resultType) { - Assert.notNull(resultType, "Projection target type must not be null!"); + Assert.notNull(resultType, "Projection target type must not be null"); if (!resultType.isInterface()) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index 20fe4b7172..9eea021af0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -84,7 +84,7 @@ private FetchableFluentQueryByPredicate(Predicate predicate, Class entityType @Override public FetchableFluentQuery sortBy(Sort sort) { - Assert.notNull(sort, "Sort must not be null!"); + Assert.notNull(sort, "Sort must not be null"); return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, this.sort.and(sort), properties, finder, pagedFinder, countOperation, existsOperation, entityManager); @@ -93,7 +93,7 @@ public FetchableFluentQuery sortBy(Sort sort) { @Override public FetchableFluentQuery as(Class resultType) { - Assert.notNull(resultType, "Projection target type must not be null!"); + Assert.notNull(resultType, "Projection target type must not be null"); if (!resultType.isInterface()) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java index f8061ec5d2..1183936e74 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java @@ -78,7 +78,7 @@ private FetchableFluentQueryBySpecification(Specification spec, Class enti @Override public FetchableFluentQuery sortBy(Sort sort) { - Assert.notNull(sort, "Sort must not be null!"); + Assert.notNull(sort, "Sort must not be null"); return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, this.sort.and(sort), properties, finder, countOperation, existsOperation, entityManager); @@ -87,7 +87,7 @@ public FetchableFluentQuery sortBy(Sort sort) { @Override public FetchableFluentQuery as(Class resultType) { - Assert.notNull(resultType, "Projection target type must not be null!"); + Assert.notNull(resultType, "Projection target type must not be null"); if (!resultType.isInterface()) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java index 0a75ea183f..71345b64d7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -54,7 +54,7 @@ public interface JpaEntityInformation extends EntityInformation, J } throw new IllegalArgumentException( - String.format("Could not obtain required identifier attribute for type %s!", getEntityName())); + String.format("Could not obtain required identifier attribute for type %s", getEntityName())); } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java index 29a6948815..a242ed224c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -55,8 +55,8 @@ public JpaEntityInformationSupport(Class domainClass) { @SuppressWarnings({ "rawtypes", "unchecked" }) public static JpaEntityInformation getEntityInformation(Class domainClass, EntityManager em) { - Assert.notNull(domainClass, "Domain class must not be null!"); - Assert.notNull(em, "EntityManager must not be null!"); + Assert.notNull(domainClass, "Domain class must not be null"); + Assert.notNull(em, "EntityManager must not be null"); Metamodel metamodel = em.getMetamodel(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index cdc386df0e..f9dfa20653 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -69,19 +69,19 @@ public JpaMetamodelEntityInformation(Class domainClass, Metamodel metamodel) super(domainClass); - Assert.notNull(metamodel, "Metamodel must not be null!"); + Assert.notNull(metamodel, "Metamodel must not be null"); this.metamodel = metamodel; ManagedType type = metamodel.managedType(domainClass); if (type == null) { - throw new IllegalArgumentException("The given domain class can not be found in the given Metamodel!"); + throw new IllegalArgumentException("The given domain class can not be found in the given Metamodel"); } this.entityName = type instanceof EntityType ? ((EntityType) type).getName() : null; if (!(type instanceof IdentifiableType)) { - throw new IllegalArgumentException("The given domain class does not contain an id attribute!"); + throw new IllegalArgumentException("The given domain class does not contain an id attribute"); } IdentifiableType identifiableType = (IdentifiableType) type; @@ -201,7 +201,7 @@ public Iterable getIdAttributeNames() { @Override public Object getCompositeIdAttributeValue(Object id, String idAttribute) { - Assert.isTrue(hasCompositeId(), "Model must have a composite Id!"); + Assert.isTrue(hasCompositeId(), "Model must have a composite Id"); return new DirectFieldAccessFallbackBeanWrapper(id).getPropertyValue(idAttribute); } @@ -409,7 +409,7 @@ private boolean isIdentifierDerivationNecessary(@Nullable Object value) { ManagedType managedType = this.metamodel.managedType(userClass); if (managedType == null) { - throw new IllegalStateException("ManagedType must not be null. We checked that it exists before."); + throw new IllegalStateException("ManagedType must not be null; We checked that it exists before."); } return managedType.getPersistenceType() == PersistenceType.ENTITY; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 46f8624c7e..12adf8f2b6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -96,7 +96,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { */ public JpaRepositoryFactory(EntityManager entityManager) { - Assert.notNull(entityManager, "EntityManager must not be null!"); + Assert.notNull(entityManager, "EntityManager must not be null"); this.entityManager = entityManager; this.extractor = PersistenceProvider.fromEntityManager(entityManager); @@ -137,7 +137,7 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException { super.setBeanFactory(beanFactory); - Assert.notNull(beanFactory, "BeanFactory must not be null!"); + Assert.notNull(beanFactory, "BeanFactory must not be null"); setQueryRewriterProvider(new BeanFactoryQueryRewriterProvider(beanFactory)); } @@ -149,7 +149,7 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException { */ public void setEntityPathResolver(EntityPathResolver entityPathResolver) { - Assert.notNull(entityPathResolver, "EntityPathResolver must not be null!"); + Assert.notNull(entityPathResolver, "EntityPathResolver must not be null"); this.entityPathResolver = entityPathResolver; } @@ -170,7 +170,7 @@ public void setEscapeCharacter(EscapeCharacter escapeCharacter) { */ public void setQueryMethodFactory(JpaQueryMethodFactory queryMethodFactory) { - Assert.notNull(queryMethodFactory, "QueryMethodFactory must not be null!"); + Assert.notNull(queryMethodFactory, "QueryMethodFactory must not be null"); this.queryMethodFactory = queryMethodFactory; } @@ -184,7 +184,7 @@ public void setQueryMethodFactory(JpaQueryMethodFactory queryMethodFactory) { */ public void setQueryRewriterProvider(QueryRewriterProvider queryRewriterProvider) { - Assert.notNull(queryRewriterProvider, "QueryRewriterProvider must not be null!"); + Assert.notNull(queryRewriterProvider, "QueryRewriterProvider must not be null"); this.queryRewriterProvider = queryRewriterProvider; } @@ -312,7 +312,7 @@ private static boolean isTransactionNeeded(Class repositoryClass) { */ private static class EclipseLinkProjectionQueryCreationListener implements QueryCreationListener { - private static final String ECLIPSELINK_PROJECTIONS = "Usage of Spring Data projections detected on persistence provider EclipseLink. Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs:"; + private static final String ECLIPSELINK_PROJECTIONS = "Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs:"; private static final Log log = LogFactory.getLog(EclipseLinkProjectionQueryCreationListener.class); @@ -327,7 +327,7 @@ private static class EclipseLinkProjectionQueryCreationListener implements Query */ public EclipseLinkProjectionQueryCreationListener(EntityManager em) { - Assert.notNull(em, "EntityManager must not be null!"); + Assert.notNull(em, "EntityManager must not be null"); this.metamodel = JpaMetamodel.of(em.getMetamodel()); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index f093e1247f..f866ef06b1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -103,7 +103,7 @@ public void setQueryMethodFactory(@Nullable JpaQueryMethodFactory factory) { @Override protected RepositoryFactorySupport doCreateRepositoryFactory() { - Assert.state(entityManager != null, "EntityManager must not be null!"); + Assert.state(entityManager != null, "EntityManager must not be null"); return createRepositoryFactory(entityManager); } @@ -127,7 +127,7 @@ protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityM @Override public void afterPropertiesSet() { - Assert.state(entityManager != null, "EntityManager must not be null!"); + Assert.state(entityManager != null, "EntityManager must not be null"); super.afterPropertiesSet(); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java index 6dcd0da94b..7052de795d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java @@ -32,8 +32,8 @@ public class QueryHintValue { public QueryHintValue(String name, Object value) { - Assert.notNull(name, "Name must not be null."); - Assert.notNull(value, "Value must not be null."); + Assert.notNull(name, "Name must not be null"); + Assert.notNull(value, "Value must not be null"); this.name = name; this.value = value; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java index 9952790e5e..8f4ea196b8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java @@ -42,7 +42,7 @@ public interface QueryHints { */ static QueryHints from(QueryHints... sources) { - Assert.notNull(sources, "Sources must not be null!"); + Assert.notNull(sources, "Sources must not be null"); MutableQueryHints result = new MutableQueryHints(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 772b8ec5c3..cef0b06d17 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -63,8 +63,8 @@ public class Querydsl { */ public Querydsl(EntityManager em, PathBuilder builder) { - Assert.notNull(em, "EntityManager must not be null!"); - Assert.notNull(builder, "PathBuilder must not be null!"); + Assert.notNull(em, "EntityManager must not be null"); + Assert.notNull(builder, "PathBuilder must not be null"); this.em = em; this.provider = PersistenceProvider.fromEntityManager(em); @@ -97,7 +97,7 @@ public AbstractJPAQuery> createQuery() { */ public AbstractJPAQuery> createQuery(EntityPath... paths) { - Assert.notNull(paths, "Paths must not be null!"); + Assert.notNull(paths, "Paths must not be null"); return createQuery().from(paths); } @@ -111,8 +111,8 @@ public AbstractJPAQuery> createQuery(EntityPath... p */ public JPQLQuery applyPagination(Pageable pageable, JPQLQuery query) { - Assert.notNull(pageable, "Pageable must not be null!"); - Assert.notNull(query, "JPQLQuery must not be null!"); + Assert.notNull(pageable, "Pageable must not be null"); + Assert.notNull(query, "JPQLQuery must not be null"); if (pageable.isUnpaged()) { return query; @@ -133,8 +133,8 @@ public JPQLQuery applyPagination(Pageable pageable, JPQLQuery query) { */ public JPQLQuery applySorting(Sort sort, JPQLQuery query) { - Assert.notNull(sort, "Sort must not be null!"); - Assert.notNull(query, "Query must not be null!"); + Assert.notNull(sort, "Sort must not be null"); + Assert.notNull(query, "Query must not be null"); if (sort.isUnsorted()) { return query; @@ -171,8 +171,8 @@ private JPQLQuery addOrderByFrom(QSort qsort, JPQLQuery query) { */ private JPQLQuery addOrderByFrom(Sort sort, JPQLQuery query) { - Assert.notNull(sort, "Sort must not be null!"); - Assert.notNull(query, "Query must not be null!"); + Assert.notNull(sort, "Sort must not be null"); + Assert.notNull(query, "Query must not be null"); for (Order order : sort) { query.orderBy(toOrderSpecifier(order)); @@ -205,7 +205,7 @@ private OrderSpecifier toOrderSpecifier(Order order) { */ private NullHandling toQueryDslNullHandling(org.springframework.data.domain.Sort.NullHandling nullHandling) { - Assert.notNull(nullHandling, "NullHandling must not be null!"); + Assert.notNull(nullHandling, "NullHandling must not be null"); switch (nullHandling) { @@ -229,7 +229,7 @@ private NullHandling toQueryDslNullHandling(org.springframework.data.domain.Sort */ private Expression buildOrderPropertyPathFrom(Order order) { - Assert.notNull(order, "Order must not be null!"); + Assert.notNull(order, "Order must not be null"); PropertyPath path = PropertyPath.from(order.getProperty(), builder.getType()); Expression sortPropertyExpression = builder; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index de10c83bc2..c4f4b07b99 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -88,7 +88,7 @@ public QuerydslJpaPredicateExecutor(JpaEntityInformation entityInformation @Override public Optional findOne(Predicate predicate) { - Assert.notNull(predicate, "Predicate must not be null!"); + Assert.notNull(predicate, "Predicate must not be null"); try { return Optional.ofNullable(createQuery(predicate).select(path).fetchOne()); @@ -100,7 +100,7 @@ public Optional findOne(Predicate predicate) { @Override public List findAll(Predicate predicate) { - Assert.notNull(predicate, "Predicate must not be null!"); + Assert.notNull(predicate, "Predicate must not be null"); return createQuery(predicate).select(path).fetch(); } @@ -108,8 +108,8 @@ public List findAll(Predicate predicate) { @Override public List findAll(Predicate predicate, OrderSpecifier... orders) { - Assert.notNull(predicate, "Predicate must not be null!"); - Assert.notNull(orders, "Order specifiers must not be null!"); + Assert.notNull(predicate, "Predicate must not be null"); + Assert.notNull(orders, "Order specifiers must not be null"); return executeSorted(createQuery(predicate).select(path), orders); } @@ -117,8 +117,8 @@ public List findAll(Predicate predicate, OrderSpecifier... orders) { @Override public List findAll(Predicate predicate, Sort sort) { - Assert.notNull(predicate, "Predicate must not be null!"); - Assert.notNull(sort, "Sort must not be null!"); + Assert.notNull(predicate, "Predicate must not be null"); + Assert.notNull(sort, "Sort must not be null"); return executeSorted(createQuery(predicate).select(path), sort); } @@ -126,7 +126,7 @@ public List findAll(Predicate predicate, Sort sort) { @Override public List findAll(OrderSpecifier... orders) { - Assert.notNull(orders, "Order specifiers must not be null!"); + Assert.notNull(orders, "Order specifiers must not be null"); return executeSorted(createQuery(new Predicate[0]).select(path), orders); } @@ -134,8 +134,8 @@ public List findAll(OrderSpecifier... orders) { @Override public Page findAll(Predicate predicate, Pageable pageable) { - Assert.notNull(predicate, "Predicate must not be null!"); - Assert.notNull(pageable, "Pageable must not be null!"); + Assert.notNull(predicate, "Predicate must not be null"); + Assert.notNull(pageable, "Pageable must not be null"); final JPQLQuery countQuery = createCountQuery(predicate); JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicate).select(path)); @@ -147,8 +147,8 @@ public Page findAll(Predicate predicate, Pageable pageable) { @Override public R findBy(Predicate predicate, Function, R> queryFunction) { - Assert.notNull(predicate, "Predicate must not be null!"); - Assert.notNull(queryFunction, "Query function must not be null!"); + Assert.notNull(predicate, "Predicate must not be null"); + Assert.notNull(queryFunction, "Query function must not be null"); Function> finder = sort -> { AbstractJPAQuery select = (AbstractJPAQuery) createQuery(predicate).select(path); @@ -202,7 +202,7 @@ public boolean exists(Predicate predicate) { */ protected AbstractJPAQuery createQuery(Predicate... predicate) { - Assert.notNull(predicate, "Predicate must not be null!"); + Assert.notNull(predicate, "Predicate must not be null"); AbstractJPAQuery query = doCreateQuery(getQueryHints().withFetchGraphs(entityManager), predicate); CrudMethodMetadata metadata = getRepositoryMethodMetadata(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index 9f093211dc..3ef8c5db45 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -120,7 +120,7 @@ public List findAll(Predicate predicate, OrderSpecifier... orders) { @Override public List findAll(Predicate predicate, Sort sort) { - Assert.notNull(sort, "Sort must not be null!"); + Assert.notNull(sort, "Sort must not be null"); return executeSorted(createQuery(predicate).select(path), sort); } @@ -128,7 +128,7 @@ public List findAll(Predicate predicate, Sort sort) { @Override public List findAll(OrderSpecifier... orders) { - Assert.notNull(orders, "Order specifiers must not be null!"); + Assert.notNull(orders, "Order specifiers must not be null"); return executeSorted(createQuery(new Predicate[0]).select(path), orders); } @@ -136,7 +136,7 @@ public List findAll(OrderSpecifier... orders) { @Override public Page findAll(Predicate predicate, Pageable pageable) { - Assert.notNull(pageable, "Pageable must not be null!"); + Assert.notNull(pageable, "Pageable must not be null"); final JPQLQuery countQuery = createCountQuery(predicate); JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicate).select(path)); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java index f4f54c7709..89a49edab9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java @@ -54,7 +54,7 @@ public abstract class QuerydslRepositorySupport { */ public QuerydslRepositorySupport(Class domainClass) { - Assert.notNull(domainClass, "Domain class must not be null!"); + Assert.notNull(domainClass, "Domain class must not be null"); this.builder = new PathBuilderFactory().create(domainClass); } @@ -66,7 +66,7 @@ public QuerydslRepositorySupport(Class domainClass) { @Autowired public void setEntityManager(EntityManager entityManager) { - Assert.notNull(entityManager, "EntityManager must not be null!"); + Assert.notNull(entityManager, "EntityManager must not be null"); this.querydsl = new Querydsl(entityManager, builder); this.entityManager = entityManager; } @@ -76,8 +76,8 @@ public void setEntityManager(EntityManager entityManager) { */ @PostConstruct public void validate() { - Assert.notNull(entityManager, "EntityManager must not be null!"); - Assert.notNull(querydsl, "Querydsl must not be null!"); + Assert.notNull(entityManager, "EntityManager must not be null"); + Assert.notNull(querydsl, "Querydsl must not be null"); } /** @@ -154,7 +154,7 @@ protected Querydsl getQuerydsl() { private Querydsl getRequiredQuerydsl() { if (querydsl == null) { - throw new IllegalStateException("Querydsl is null!"); + throw new IllegalStateException("Querydsl is null"); } return querydsl; @@ -163,7 +163,7 @@ private Querydsl getRequiredQuerydsl() { private EntityManager getRequiredEntityManager() { if (entityManager == null) { - throw new IllegalStateException("EntityManager is null!"); + throw new IllegalStateException("EntityManager is null"); } return entityManager; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 9f4552d96f..f968c86ff3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -91,7 +91,7 @@ @Transactional(readOnly = true) public class SimpleJpaRepository implements JpaRepositoryImplementation { - private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!"; + private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null"; private final JpaEntityInformation entityInformation; private final EntityManager em; @@ -108,8 +108,8 @@ public class SimpleJpaRepository implements JpaRepositoryImplementation entityInformation, EntityManager entityManager) { - Assert.notNull(entityInformation, "JpaEntityInformation must not be null!"); - Assert.notNull(entityManager, "EntityManager must not be null!"); + Assert.notNull(entityInformation, "JpaEntityInformation must not be null"); + Assert.notNull(entityManager, "EntityManager must not be null"); this.entityInformation = entityInformation; this.em = entityManager; @@ -168,7 +168,7 @@ public void deleteById(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException( - String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1))); + String.format("No %s entity with id %s exists", entityInformation.getJavaType(), id), 1))); } @Override @@ -176,7 +176,7 @@ public void deleteById(ID id) { @SuppressWarnings("unchecked") public void delete(T entity) { - Assert.notNull(entity, "Entity must not be null!"); + Assert.notNull(entity, "Entity must not be null"); if (entityInformation.isNew(entity)) { return; @@ -198,7 +198,7 @@ public void delete(T entity) { @Transactional public void deleteAllById(Iterable ids) { - Assert.notNull(ids, "Ids must not be null!"); + Assert.notNull(ids, "Ids must not be null"); for (ID id : ids) { deleteById(id); @@ -209,7 +209,7 @@ public void deleteAllById(Iterable ids) { @Transactional public void deleteAllByIdInBatch(Iterable ids) { - Assert.notNull(ids, "Ids must not be null!"); + Assert.notNull(ids, "Ids must not be null"); if (!ids.iterator().hasNext()) { return; @@ -246,7 +246,7 @@ public void deleteAllByIdInBatch(Iterable ids) { @Transactional public void deleteAll(Iterable entities) { - Assert.notNull(entities, "Entities must not be null!"); + Assert.notNull(entities, "Entities must not be null"); for (T entity : entities) { delete(entity); @@ -257,7 +257,7 @@ public void deleteAll(Iterable entities) { @Transactional public void deleteAllInBatch(Iterable entities) { - Assert.notNull(entities, "Entities must not be null!"); + Assert.notNull(entities, "Entities must not be null"); if (!entities.iterator().hasNext()) { return; @@ -387,7 +387,7 @@ public List findAll() { @Override public List findAllById(Iterable ids) { - Assert.notNull(ids, "Ids must not be null!"); + Assert.notNull(ids, "Ids must not be null"); if (!ids.iterator().hasNext()) { return Collections.emptyList(); @@ -533,8 +533,8 @@ public Page findAll(Example example, Pageable pageable) { @Override public R findBy(Example example, Function, R> queryFunction) { - Assert.notNull(example, "Sample must not be null!"); - Assert.notNull(queryFunction, "Query function must not be null!"); + Assert.notNull(example, "Sample must not be null"); + Assert.notNull(queryFunction, "Query function must not be null"); Function> finder = sort -> { @@ -553,8 +553,8 @@ public R findBy(Example example, Function R findBy(Specification spec, Function, R> queryFunction) { - Assert.notNull(spec, "Specification must not be null!"); - Assert.notNull(queryFunction, "Query function must not be null!"); + Assert.notNull(spec, "Specification must not be null"); + Assert.notNull(queryFunction, "Query function must not be null"); Function> finder = sort -> { return getQuery(spec, getDomainClass(), sort); @@ -580,7 +580,7 @@ public long count(@Nullable Specification spec) { @Override public S save(S entity) { - Assert.notNull(entity, "Entity must not be null."); + Assert.notNull(entity, "Entity must not be null"); if (entityInformation.isNew(entity)) { em.persist(entity); @@ -604,7 +604,7 @@ public S saveAndFlush(S entity) { @Override public List saveAll(Iterable entities) { - Assert.notNull(entities, "Entities must not be null!"); + Assert.notNull(entities, "Entities must not be null"); List result = new ArrayList<>(); @@ -770,8 +770,8 @@ protected TypedQuery getCountQuery(@Nullable Specification Root applySpecificationToCriteria(@Nullable Specification spec, Class domainClass, CriteriaQuery query) { - Assert.notNull(domainClass, "Domain class must not be null!"); - Assert.notNull(query, "CriteriaQuery must not be null!"); + Assert.notNull(domainClass, "Domain class must not be null"); + Assert.notNull(query, "CriteriaQuery must not be null"); Root root = query.from(domainClass); @@ -829,7 +829,7 @@ private void applyQueryHintsForCount(Query query) { */ private static long executeCountQuery(TypedQuery query) { - Assert.notNull(query, "TypedQuery must not be null!"); + Assert.notNull(query, "TypedQuery must not be null"); List totals = query.getResultList(); long total = 0L; @@ -898,8 +898,8 @@ private static class ExampleSpecification implements Specification { */ ExampleSpecification(Example example, EscapeCharacter escapeCharacter) { - Assert.notNull(example, "Example must not be null!"); - Assert.notNull(escapeCharacter, "EscapeCharacter must not be null!"); + Assert.notNull(example, "Example must not be null"); + Assert.notNull(escapeCharacter, "EscapeCharacter must not be null"); this.example = example; this.escapeCharacter = escapeCharacter; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java index 3b85ad6535..77b91782d2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java @@ -74,7 +74,7 @@ public class ClasspathScanningPersistenceUnitPostProcessor */ public ClasspathScanningPersistenceUnitPostProcessor(String basePackage) { - Assert.hasText(basePackage, "Base package must not be null or empty!"); + Assert.hasText(basePackage, "Base package must not be null or empty"); this.basePackage = basePackage; } @@ -87,7 +87,7 @@ public ClasspathScanningPersistenceUnitPostProcessor(String basePackage) { */ public void setMappingFileNamePattern(String mappingFilePattern) { - Assert.hasText(mappingFilePattern, "Mapping file pattern must not be null or empty!"); + Assert.hasText(mappingFilePattern, "Mapping file pattern must not be null or empty"); this.mappingFileNamePattern = mappingFilePattern; } @@ -95,7 +95,7 @@ public void setMappingFileNamePattern(String mappingFilePattern) { @Override public void setResourceLoader(ResourceLoader resourceLoader) { - Assert.notNull(resourceLoader, "ResourceLoader must not be null!"); + Assert.notNull(resourceLoader, "ResourceLoader must not be null"); this.mappingFileResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); this.resourceLoader = resourceLoader; @@ -104,7 +104,7 @@ public void setResourceLoader(ResourceLoader resourceLoader) { @Override public void setEnvironment(Environment environment) { - Assert.notNull(environment, "Environment must not be null!"); + Assert.notNull(environment, "Environment must not be null"); this.environment = environment; } @@ -121,7 +121,7 @@ public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { for (BeanDefinition definition : provider.findCandidateComponents(basePackage)) { - LOG.debug(String.format("Registering classpath-scanned entity %s in persistence unit info!", definition.getBeanClassName())); + LOG.debug(String.format("Registering classpath-scanned entity %s in persistence unit info", definition.getBeanClassName())); if (definition.getBeanClassName() != null) { pui.addManagedClassName(definition.getBeanClassName()); @@ -130,7 +130,7 @@ public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) { for (String location : scanForMappingFileLocations()) { - LOG.debug(String.format("Registering classpath-scanned entity mapping file %s in persistence unit info!", location)); + LOG.debug(String.format("Registering classpath-scanned entity mapping file %s in persistence unit info", location)); pui.addMappingFileName(location); } @@ -165,7 +165,7 @@ private Set scanForMappingFileLocations() { try { scannedResources = mappingFileResolver.getResources(path); } catch (IOException e) { - throw new IllegalStateException(String.format("Cannot load mapping files from path %s!", path), e); + throw new IllegalStateException(String.format("Cannot load mapping files from path %s", path), e); } Set mappingFileUris = new HashSet<>(); @@ -179,7 +179,7 @@ private Set scanForMappingFileLocations() { mappingFileUris.add(resourcePathInClasspath); } catch (IOException e) { - throw new IllegalStateException(String.format("Couldn't get URI for %s!", resource), e); + throw new IllegalStateException(String.format("Couldn't get URI for %s", resource), e); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index c729ba812d..40d8224aa3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -62,7 +62,7 @@ void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui, PersistenceU if (!pui.getJarFileUrls().contains(url)) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Adding JAR file URL %s to persistence unit %s.", url, persistenceUnitName)); + LOG.debug(String.format("Adding JAR file URL %s to persistence unit %s", url, persistenceUnitName)); } pui.addJarFileUrl(url); } @@ -82,7 +82,7 @@ void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui, PersistenceU if (!pui.getMappingFileNames().contains(mappingFileName)) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Adding mapping file %s to persistence unit %s.", mappingFileName, persistenceUnitName)); + LOG.debug(String.format("Adding mapping file %s to persistence unit %s", mappingFileName, persistenceUnitName)); } pui.addMappingFileName(mappingFileName); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java index 963e48d0a3..e34c940ebf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java @@ -59,7 +59,7 @@ public class JpaMetamodel { */ private JpaMetamodel(Metamodel metamodel) { - Assert.notNull(metamodel, "Metamodel must not be null!"); + Assert.notNull(metamodel, "Metamodel must not be null"); this.metamodel = metamodel; @@ -87,7 +87,7 @@ public static JpaMetamodel of(Metamodel metamodel) { */ public boolean isJpaManaged(Class type) { - Assert.notNull(type, "Type must not be null!"); + Assert.notNull(type, "Type must not be null"); return managedTypes.get().contains(type); } @@ -120,7 +120,7 @@ public boolean isSingleIdAttribute(Class entity, String name, Class attrib */ public boolean isMappedType(Class entity) { - Assert.notNull(entity, "Type must not be null!"); + Assert.notNull(entity, "Type must not be null"); if (!isJpaManaged(entity)) { return false; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java index b9f331f7ca..63570f18a4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java @@ -36,7 +36,7 @@ public class AuditorAwareStub implements AuditorAware { public AuditorAwareStub(AuditableUserRepository repository) { - Assert.notNull(repository, "AuditableUserRepository must not be null!"); + Assert.notNull(repository, "AuditableUserRepository must not be null"); this.repository = repository; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java index 6b100acc03..e3cacb1243 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java @@ -40,8 +40,8 @@ public SampleEntityPK() { public SampleEntityPK(String first, String second) { - Assert.notNull(first, "First must not be null!"); - Assert.notNull(second, "Second must not be null!"); + Assert.notNull(first, "First must not be null"); + Assert.notNull(second, "Second must not be null"); this.first = first; this.second = second; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java index 29ffe8ccb6..5e4cfb9159 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java @@ -55,6 +55,6 @@ public static PersistenceProvider getPersistenceProvider() { } } - throw new IllegalStateException("Could not obtain Hibernate PersistenceProvider!"); + throw new IllegalStateException("Could not obtain Hibernate PersistenceProvider"); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java index 2dbc99d010..fa58802e68 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java @@ -66,7 +66,7 @@ void queryProvidesCorrectNumberOfParametersForNativeQuery() { Query query = em.createNativeQuery("select 1 from User where firstname=? and lastname=?"); assertThat(query.getParameters()).describedAs( - "Due to a bug eclipse has size 0. If this is no longer the case the special code path triggered in NamedOrIndexedQueryParameterSetter.registerExcessParameters can be removed") + "Due to a bug eclipse has size 0; If this is no longer the case the special code path triggered in NamedOrIndexedQueryParameterSetter.registerExcessParameters can be removed") .hasSize(0); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java index 55f1a30215..574682081c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java @@ -51,7 +51,7 @@ static void setUpCdi() { .addPackages(PersonRepository.class) // .initialize(); - LOGGER.debug("CDI container bootstrapped!"); + LOGGER.debug("CDI container bootstrapped"); } @AfterAll diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java index 3077542d71..596b46d90c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java @@ -67,7 +67,7 @@ static void setUpCdi() { .addPackages(UserRepositoryWithRewriter.class) // .initialize(); - LOGGER.debug("CDI container bootstrapped!"); + LOGGER.debug("CDI container bootstrapped"); } @AfterAll diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java index b50bd96da5..e2192de93d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java @@ -255,7 +255,7 @@ AttributeNodeAssert terminatesGraphWith(String... nodeNames) { Assertions.assertThat(attributeNode.getSubgraphs()) // .describedAs( - String.format("Leaf properties %s could not be found. The node does not have any subgraphs.", nodes)) // + String.format("Leaf properties %s could not be found; The node does not have any subgraphs", nodes)) // .isNotNull() // .isNotEmpty(); @@ -267,13 +267,13 @@ AttributeNodeAssert terminatesGraphWith(String... nodeNames) { AttributeNode node = findNode(nodeName, graph.getAttributeNodes()); String notInSubgraph = String.format( - "AttributeNode '%s' could not be found in subgraph for '%s'. Know nodes are: %s.", nodeName, + "AttributeNode '%s' could not be found in subgraph for '%s'; Know nodes are: %s", nodeName, attributeNode.getAttributeName(), extractExistingAttributeNames(graph)); softly.assertThat(node).describedAs(notInSubgraph).isNotNull(); String notLeaf = String.format( - "AttributeNode %s of subgraph %s is not a leaf property but has %d SubGraph(s).", nodeName, + "AttributeNode %s of subgraph %s is not a leaf property but has %d SubGraph(s)", nodeName, attributeNode.getAttributeName(), node.getSubgraphs().size()); softly.assertThat(node.getSubgraphs()) // @@ -291,7 +291,7 @@ AttributeNodeAssert hasSubgraphs(String... subgraphNames) { Assertions.assertThat(attributeNode.getSubgraphs()) // .describedAs( - String.format("Subgraphs %s could not be found. The node does not have any subgraphs.", subgraphs)) // + String.format("Subgraphs %s could not be found; The node does not have any subgraphs", subgraphs)) // .isNotNull() // .isNotEmpty(); @@ -303,13 +303,13 @@ AttributeNodeAssert hasSubgraphs(String... subgraphNames) { AttributeNode node = findNode(subgraphName, graph.getAttributeNodes()); - String notFound = String.format("Subgraph '%s' could not be found in SubGraph for '%s'. Known nodes are: %s.", + String notFound = String.format("Subgraph '%s' could not be found in SubGraph for '%s'; Known nodes are: %s", subgraphName, attributeNode.getAttributeName(), extractExistingAttributeNames(graph)); softly.assertThat(node) // .describedAs(notFound) // .isNotNull(); - String notSubGraph = String.format("'%s' of SubGraph '%s' is not a SubGraph.", subgraphName, + String notSubGraph = String.format("'%s' of SubGraph '%s' is not a SubGraph", subgraphName, attributeNode.getAttributeName()); softly.assertThat(node.getSubgraphs()) // diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index 745d6cac5f..d95ca23060 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -181,7 +181,7 @@ void namedQueryWithSortShouldThrowIllegalStateException() throws NoSuchMethodExc assertThatIllegalStateException() .isThrownBy(() -> strategy.resolveQuery(method, metadata, projectionFactory, namedQueries)) .withMessageContaining( - "is backed by a NamedQuery and must not contain a sort parameter as we cannot modify the query! Use @Query instead!"); + "is backed by a NamedQuery and must not contain a sort parameter as we cannot modify the query; Use @Query instead"); } @Test // GH-2018 @@ -211,7 +211,7 @@ void customQueryWithQuestionMarksShouldWork() throws NoSuchMethodException { RepositoryMetadata jdbcStyleMetadata = new DefaultRepositoryMetadata(UserRepository.class); strategy.resolveQuery(jdbcStyleMethod, jdbcStyleMetadata, projectionFactory, namedQueries); - }).withMessageContaining("JDBC style parameters (?) are not supported for JPA queries."); + }).withMessageContaining("JDBC style parameters (?) are not supported for JPA queries"); Method jpaStyleMethod = UserRepository.class.getMethod("customQueryWithQuestionMarksAndNumberedStyleParam", String.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index e79fa55c61..e0177b2133 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -254,11 +254,11 @@ private static T getValue(Object source, String path) { while (split.hasNext()) { - Assert.notNull(result, "result must not be null."); + Assert.notNull(result, "result must not be null"); result = getField(result, split.next()); } - Assert.notNull(result, "result must not be null."); + Assert.notNull(result, "result must not be null"); return (T) result; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java index ee126b0396..832dbc0ffd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java @@ -79,7 +79,7 @@ void exceptionWhenCriteriaQueryContainsInsufficientAmountOfParameters() { assertThatExceptionOfType(IllegalArgumentException.class) // .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith :NamedParameter", false))) // - .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query."); + .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query"); } @Test // DATAJPA-1281 @@ -93,6 +93,6 @@ void exceptionWhenBasicQueryContainsInsufficientAmountOfParameters() { assertThatExceptionOfType(IllegalArgumentException.class) // .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith ?1", false))) // - .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query."); + .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query"); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java index 0110d4560f..4757e2a543 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java @@ -33,16 +33,16 @@ public class UserRepositoryImpl implements UserRepositoryCustom { @Autowired public UserRepositoryImpl(JpaContext context) { - Assert.notNull(context, "JpaContext must not be null!"); + Assert.notNull(context, "JpaContext must not be null"); } @Override public void someCustomMethod(User u) { - LOG.debug("Some custom method was invoked!"); + LOG.debug("Some custom method was invoked"); } @Override public void findByOverrridingMethod() { - LOG.debug("A method overriding a finder was invoked!"); + LOG.debug("A method overriding a finder was invoked"); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index f922babaf9..c632917f72 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -210,13 +210,13 @@ private class SampleCustomRepositoryImpl implements SampleCustomRepository { @Override public void throwingRuntimeException() { - throw new IllegalArgumentException("You lose!"); + throw new IllegalArgumentException("You lose"); } @Override public void throwingCheckedException() throws IOException { - throw new IOException("You lose!"); + throw new IOException("You lose"); } }; } From f2e7bddb5aa4610f8a5fcf233e44df66ca2011cb Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 13 Jun 2022 15:27:33 -0500 Subject: [PATCH 216/821] Ensure that single-parameter DTOs work with JPQL queries. Closes #1869. --- .../data/jpa/repository/query/QueryUtils.java | 9 ++++--- .../repository/UserRepositoryFinderTests.java | 1 - .../query/DefaultQueryUtilsUnitTests.java | 27 +++++++++++++++++-- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index bd39edc50b..94c080dec6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -114,8 +114,8 @@ public abstract class QueryUtils { private static final String EQUALS_CONDITION_STRING = "%s.%s = :%s"; private static final Pattern ORDER_BY = Pattern.compile("(order\\s+by\\s+)", CASE_INSENSITIVE); - private static final Pattern ORDER_BY_IN_WINDOW_OR_SUBSELECT = Pattern.compile("\\([\\s\\S]*order\\s+by\\s[\\s\\S]*\\)", - CASE_INSENSITIVE); + private static final Pattern ORDER_BY_IN_WINDOW_OR_SUBSELECT = Pattern + .compile("\\([\\s\\S]*order\\s+by\\s[\\s\\S]*\\)", CASE_INSENSITIVE); private static final Pattern NAMED_PARAMETER = Pattern.compile(COLON_NO_DOUBLE_COLON + IDENTIFIER + "|#" + IDENTIFIER, CASE_INSENSITIVE); @@ -590,8 +590,9 @@ public static String createCountQueryFor(String originalQuery, @Nullable String String variable = matcher.matches() ? matcher.group(VARIABLE_NAME_GROUP_INDEX) : null; boolean useVariable = StringUtils.hasText(variable) // - && !variable.startsWith(" new") // - && !variable.startsWith("count(") // + && !variable.startsWith("new") // select [new com.example.User... + && !variable.startsWith(" new") // select distinct[ new com.example.User... + && !variable.startsWith("count(") // select [count(... && !variable.contains(","); String complexCountValue = matcher.matches() && StringUtils.hasText(matcher.group(COMPLEX_COUNT_FIRST_INDEX)) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index ff2c484004..6d89b1def3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -25,7 +25,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Page; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java index ec08e3ea9d..828f8a835b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java @@ -84,6 +84,29 @@ void createsCountQueryForJoins() { "select count(distinct u) from User u left outer join u.roles r WHERE r = ?"); } + @Test // GH-1869 + void createsCountQueryForJoinsWithTwoArgs() { + + assertCountQuery("select distinct new User(u.name, u.age) from User u left outer join u.roles r WHERE r = ?", + "select count(distinct u) from User u left outer join u.roles r WHERE r = ?"); + } + + @Test // GH-1869 + void createsCountQueryForDtoWithOneArg() { + + assertCountQuery( + "SELECT new org.springframework.data.jpa.repository.sample.FirstNameDto(u.firstname) from User u where u.firstname = ?", + "select count(u) from User u where u.firstname = ?"); + } + + @Test // GH-1869 + void createsCountQueryForDtoWithTwoArgs() { + + assertCountQuery( + "SELECT new org.springframework.data.jpa.repository.sample.NameOnlyDto(u.firstname, u.lastname) from User u where u.firstname = ?", + "select count(u) from User u where u.firstname = ?"); + } + @Test void createsCountQueryForQueriesWithSubSelects() { @@ -400,8 +423,8 @@ void doesNotContainStaticClauseInExistsQuery() { @Test // DATAJPA-1363 void discoversAliasWithComplexFunction() { - assertThat(QueryUtils - .getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) // + assertThat( + QueryUtils.getFunctionAliases("select new MyDto(sum(case when myEntity.prop3=0 then 1 else 0 end) as myAlias")) // .contains("myAlias"); } From cb6c74f89d534770f20bc56a15e3a169609a5d2a Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 9 Jun 2022 16:44:38 -0500 Subject: [PATCH 217/821] Simplify accessing the ID of an entity. Delegate ID access to the JPA provider. Closes #1854. --- .../support/JpaEntityInformationSupport.java | 7 +- .../JpaMetamodelEntityInformation.java | 166 +++--------------- .../JpaPersistableEntityInformation.java | 9 +- ...odelEntityInformationIntegrationTests.java | 58 +++--- ...odelEntityInformationIntegrationTests.java | 36 ++++ .../JpaEntityInformationSupportUnitTests.java | 7 + ...odelEntityInformationIntegrationTests.java | 110 +++++++----- ...paMetamodelEntityInformationUnitTests.java | 24 ++- ...PersistableEntityInformationUnitTests.java | 15 +- ...odelEntityInformationIntegrationTests.java | 16 +- ...QuerydslJpaPredicateExecutorUnitTests.java | 9 +- .../support/QuerydslJpaRepositoryTests.java | 10 +- .../support/SimpleJpaRepositoryUnitTests.java | 10 ++ .../test/resources/META-INF/persistence.xml | 45 +++++ .../src/test/resources/eclipselink.xml | 4 +- 15 files changed, 277 insertions(+), 249 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java index a242ed224c..9c94edbc44 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.repository.support; import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.metamodel.Metamodel; import org.springframework.data.domain.Persistable; @@ -29,6 +30,7 @@ * * @author Oliver Gierke * @author Mark Paluch + * @author Greg Turnquist */ public abstract class JpaEntityInformationSupport extends AbstractEntityInformation implements JpaEntityInformation { @@ -59,11 +61,12 @@ public JpaEntityInformationSupport(Class domainClass) { Assert.notNull(em, "EntityManager must not be null"); Metamodel metamodel = em.getMetamodel(); + PersistenceUnitUtil persistenceUnitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil(); if (Persistable.class.isAssignableFrom(domainClass)) { - return new JpaPersistableEntityInformation(domainClass, metamodel); + return new JpaPersistableEntityInformation(domainClass, metamodel, persistenceUnitUtil); } else { - return new JpaMetamodelEntityInformation(domainClass, metamodel); + return new JpaMetamodelEntityInformation(domainClass, metamodel, persistenceUnitUtil); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index f9dfa20653..7689bf2d6b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -15,14 +15,8 @@ */ package org.springframework.data.jpa.repository.support; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.Set; - import jakarta.persistence.IdClass; +import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.metamodel.Attribute; import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.IdentifiableType; @@ -30,15 +24,19 @@ import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.Type; -import jakarta.persistence.metamodel.Type.PersistenceType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.util.JpaMetamodel; import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper; -import org.springframework.data.util.ProxyUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -51,6 +49,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Jens Schauder + * @author Greg Turnquist */ public class JpaMetamodelEntityInformation extends JpaEntityInformationSupport { @@ -58,14 +57,17 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu private final Optional> versionAttribute; private final Metamodel metamodel; private final @Nullable String entityName; + private final PersistenceUnitUtil persistenceUnitUtil; /** * Creates a new {@link JpaMetamodelEntityInformation} for the given domain class and {@link Metamodel}. - * + * * @param domainClass must not be {@literal null}. * @param metamodel must not be {@literal null}. + * @param persistenceUnitUtil must not be {@literal null}. */ - public JpaMetamodelEntityInformation(Class domainClass, Metamodel metamodel) { + public JpaMetamodelEntityInformation(Class domainClass, Metamodel metamodel, + PersistenceUnitUtil persistenceUnitUtil) { super(domainClass); @@ -88,6 +90,9 @@ public JpaMetamodelEntityInformation(Class domainClass, Metamodel metamodel) this.idMetadata = new IdMetadata<>(identifiableType, PersistenceProvider.fromMetamodel(metamodel)); this.versionAttribute = findVersionAttribute(identifiableType, metamodel); + + Assert.notNull(persistenceUnitUtil, "PersistenceUnitUtil must not be null"); + this.persistenceUnitUtil = persistenceUnitUtil; } @Override @@ -147,27 +152,25 @@ public ID getId(T entity) { return (ID) persistenceProvider.getIdentifierFrom(entity); } - // if not a proxy use Spring mechanics to access the id. - BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity); - + // If it's a simple type, then immediately delegate to the provider if (idMetadata.hasSimpleId()) { - return (ID) entityWrapper.getPropertyValue(idMetadata.getSimpleIdAttribute().getName()); + return (ID) persistenceUnitUtil.getIdentifier(entity); } - BeanWrapper idWrapper = new IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(idMetadata.getType(), metamodel); + // otherwise, check if the complex id type has any partially filled fields + BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity); boolean partialIdValueFound = false; for (SingularAttribute attribute : idMetadata) { + Object propertyValue = entityWrapper.getPropertyValue(attribute.getName()); if (propertyValue != null) { partialIdValueFound = true; } - - idWrapper.setPropertyValue(attribute.getName(), propertyValue); } - return partialIdValueFound ? (ID) idWrapper.getWrappedInstance() : null; + return partialIdValueFound ? (ID) persistenceUnitUtil.getIdentifier(entity) : null; } @Override @@ -209,7 +212,7 @@ public Object getCompositeIdAttributeValue(Object id, String idAttribute) { @Override public boolean isNew(T entity) { - if (!versionAttribute.isPresent() + if (versionAttribute.isEmpty() || versionAttribute.map(Attribute::getJavaType).map(Class::isPrimitive).orElse(false)) { return super.isNew(entity); } @@ -237,9 +240,9 @@ private static class IdMetadata implements Iterable>) (source.hasSingleIdAttribute() + this.attributes = source.hasSingleIdAttribute() ? Collections.singleton(source.getId(source.getIdType().getJavaType())) - : source.getIdClassAttributes()); + : source.getIdClassAttributes(); } boolean hasSimpleId() { @@ -298,121 +301,4 @@ private static Class lookupIdClass(IdentifiableType type) { return attributes.iterator(); } } - - /** - * Custom extension of {@link DirectFieldAccessFallbackBeanWrapper} that allows to derive the identifier if composite - * keys with complex key attribute types (e.g. types that are annotated with {@code @Entity} themselves) are used. - * - * @author Thomas Darimont - */ - private static class IdentifierDerivingDirectFieldAccessFallbackBeanWrapper - extends DirectFieldAccessFallbackBeanWrapper { - - private final Metamodel metamodel; - private final JpaMetamodel jpaMetamodel; - - IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(Class type, Metamodel metamodel) { - super(type); - this.metamodel = metamodel; - this.jpaMetamodel = JpaMetamodel.of(metamodel); - } - - /** - * In addition to the functionality described in {@link BeanWrapperImpl} it is checked whether we have a nested - * entity that is part of the id key. If this is the case, we need to derive the identifier of the nested entity. - */ - @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void setPropertyValue(String propertyName, @Nullable Object value) { - - if (!isIdentifierDerivationNecessary(value)) { - super.setPropertyValue(propertyName, value); - return; - } - - // Derive the identifier from the nested entity that is part of the composite key. - JpaMetamodelEntityInformation nestedEntityInformation = new JpaMetamodelEntityInformation( - ProxyUtils.getUserClass(value), this.metamodel); - - if (!nestedEntityInformation.getJavaType().isAnnotationPresent(IdClass.class)) { - - Object nestedIdPropertyValue = new DirectFieldAccessFallbackBeanWrapper(value) - .getPropertyValue(nestedEntityInformation.getRequiredIdAttribute().getName()); - super.setPropertyValue(propertyName, nestedIdPropertyValue); - return; - } - - // We have an IdClass property, we need to inspect the current value in order to map potentially multiple id - // properties correctly. - - BeanWrapper sourceIdValueWrapper = new DirectFieldAccessFallbackBeanWrapper(value); - BeanWrapper targetIdClassTypeWrapper = new BeanWrapperImpl(nestedEntityInformation.getIdType()); - - for (String idAttributeName : (Iterable) nestedEntityInformation.getIdAttributeNames()) { - targetIdClassTypeWrapper.setPropertyValue(idAttributeName, - extractActualIdPropertyValue(sourceIdValueWrapper, idAttributeName)); - } - - super.setPropertyValue(propertyName, targetIdClassTypeWrapper.getWrappedInstance()); - } - - @Nullable - private Object extractActualIdPropertyValue(BeanWrapper sourceIdValueWrapper, String idAttributeName) { - - Object idPropertyValue = sourceIdValueWrapper.getPropertyValue(idAttributeName); - - if (idPropertyValue != null) { - - Class idPropertyValueType = idPropertyValue.getClass(); - - if (!jpaMetamodel.isJpaManaged(idPropertyValueType)) { - return idPropertyValue; - } - - return new DirectFieldAccessFallbackBeanWrapper(idPropertyValue) - .getPropertyValue(tryFindSingularIdAttributeNameOrUseFallback(idPropertyValueType, idAttributeName)); - } - - return null; - } - - private String tryFindSingularIdAttributeNameOrUseFallback(Class idPropertyValueType, - String fallbackIdTypePropertyName) { - - ManagedType idPropertyType = metamodel.managedType(idPropertyValueType); - for (SingularAttribute sa : idPropertyType.getSingularAttributes()) { - if (sa.isId()) { - return sa.getName(); - } - } - - return fallbackIdTypePropertyName; - } - - /** - * @param value - * @return {@literal true} if the given value is not {@literal null} and a mapped persistable entity otherwise - * {@literal false} - */ - private boolean isIdentifierDerivationNecessary(@Nullable Object value) { - - if (value == null) { - return false; - } - - Class userClass = ProxyUtils.getUserClass(value); - - if (!this.jpaMetamodel.isJpaManaged(userClass)) { - return false; - } - - ManagedType managedType = this.metamodel.managedType(userClass); - - if (managedType == null) { - throw new IllegalStateException("ManagedType must not be null; We checked that it exists before."); - } - - return managedType.getPersistenceType() == PersistenceType.ENTITY; - } - } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java index cf9bf7519f..1672d5e53f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jpa.repository.support; +import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.metamodel.Metamodel; import org.springframework.data.domain.Persistable; @@ -32,12 +33,14 @@ public class JpaPersistableEntityInformation, ID> /** * Creates a new {@link JpaPersistableEntityInformation} for the given domain class and {@link Metamodel}. - * + * * @param domainClass must not be {@literal null}. * @param metamodel must not be {@literal null}. + * @param persistenceUnitUtil must not be {@literal null}. */ - public JpaPersistableEntityInformation(Class domainClass, Metamodel metamodel) { - super(domainClass, metamodel); + public JpaPersistableEntityInformation(Class domainClass, Metamodel metamodel, + PersistenceUnitUtil persistenceUnitUtil) { + super(domainClass, metamodel, persistenceUnitUtil); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java index a7669f7d0d..3c0a2bf828 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java @@ -16,31 +16,40 @@ package org.springframework.data.jpa.repository.support; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.*; import java.io.Serializable; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; /** * EclipseLink execution for {@link JpaMetamodelEntityInformationIntegrationTests}. * * @author Oliver Gierke * @author Jens Schauder + * @author Greg Turnquist */ -@ContextConfiguration("classpath:eclipselink.xml") -class EclipseLinkJpaMetamodelEntityInformationIntegrationTests - extends JpaMetamodelEntityInformationIntegrationTests { +@ExtendWith(SpringExtension.class) +@ContextConfiguration({ "classpath:infrastructure.xml", "classpath:eclipselink.xml" }) +class EclipseLinkJpaMetamodelEntityInformationIntegrationTests extends JpaMetamodelEntityInformationIntegrationTests { + + @Override + String getMetadadataPersistenceUnitName() { + return "metadata_el"; + } /** - * Re-activate test. Change to check for {@link String} as OpenJpa defaults {@link Serializable}s to {@link String}. + * Change to check for {@link String} as EclipseLink defaults {@link Serializable}s to {@link String}. */ - @Test - void reactivatedDetectsIdTypeForMappedSuperclass() { - JpaEntityInformation information = JpaEntityInformationSupport.getEntityInformation(AbstractPersistable.class, - em); + @Override + void detectsIdTypeForMappedSuperclass() { + + JpaEntityInformation information = getEntityInformation(AbstractPersistable.class, em); assertThat(information.getIdType()).isEqualTo(String.class); } @@ -58,30 +67,6 @@ void findsIdClassOnMappedSuperclass() {} @Disabled void detectsNewStateForEntityWithPrimitiveId() {} - @Override - @Disabled - void considersEntityWithUnsetCompundIdNew() {} - - /** - * Re-activate test for DATAJPA-820. - */ - @Test - @Override - void detectsVersionPropertyOnMappedSuperClass() { - super.detectsVersionPropertyOnMappedSuperClass(); - } - - /** - * This test fails due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=531528 IdentifiableType.hasSingleIdAttribute() - * returns true when IdClass references an inner class. This bug is supposedly fixed, but the test still fails. - */ - @Disabled - @Test - @Override - void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() { - super.correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType(); - } - /** * This test fails due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=531528 IdentifiableType.hasSingleIdAttribute() * returns true when IdClass references an inner class. This bug is supposedly fixed, but the test still fails. @@ -89,12 +74,9 @@ void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType( @Disabled @Test @Override - void proxiedIdClassElement() { - super.proxiedIdClassElement(); - } + void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() {} @Override - String getMetadadataPersitenceUnitName() { - return "metadata_el"; - } + @Disabled + void prefersPrivateGetterOverFieldAccess() {} } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java new file mode 100644 index 0000000000..4a12e8463e --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2011-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.support; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Hibernate execution for {@link JpaMetamodelEntityInformationIntegrationTests}. + * + * @author Greg Turnquist + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration("classpath:infrastructure.xml") +public class HibernateJpaMetamodelEntityInformationIntegrationTests + extends JpaMetamodelEntityInformationIntegrationTests { + + @Override + String getMetadadataPersistenceUnitName() { + return "metadata-id-handling"; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java index 320af7d181..40806e1208 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java @@ -23,6 +23,8 @@ import jakarta.persistence.Entity; import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; @@ -45,6 +47,8 @@ public class JpaEntityInformationSupportUnitTests { @Mock EntityManager em; @Mock Metamodel metaModel; + @Mock EntityManagerFactory entityManagerFactory; + @Mock PersistenceUnitUtil persistenceUnitUtil; @Test void usesSimpleClassNameIfNoEntityNameGiven() { @@ -60,6 +64,9 @@ void usesSimpleClassNameIfNoEntityNameGiven() { void rejectsClassNotBeingFoundInMetamodel() { when(em.getMetamodel()).thenReturn(metaModel); + when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory); + when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnitUtil); + assertThatIllegalArgumentException() .isThrownBy(() -> JpaEntityInformationSupport.getEntityInformation(User.class, em)); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java index b4391d39c5..14bab1fd1d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java @@ -18,24 +18,19 @@ import static org.assertj.core.api.Assertions.*; import static org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.*; +import jakarta.persistence.*; import lombok.Data; import java.io.Serializable; import java.sql.Timestamp; import java.util.Date; - -import jakarta.persistence.*; -import jakarta.persistence.metamodel.Metamodel; +import java.util.UUID; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.sample.*; import org.springframework.data.repository.core.EntityInformation; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; /** @@ -45,13 +40,14 @@ * @author Thomas Darimont * @author Christoph Strobl * @author Jens Schauder + * @author Greg Turnquist */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration({ "classpath:infrastructure.xml" }) -public class JpaMetamodelEntityInformationIntegrationTests { +public abstract class JpaMetamodelEntityInformationIntegrationTests { @PersistenceContext EntityManager em; + abstract String getMetadadataPersistenceUnitName(); + @Test void detectsIdTypeForEntity() { @@ -59,14 +55,7 @@ void detectsIdTypeForEntity() { assertThat(information.getIdType()).isAssignableFrom(Integer.class); } - /** - * Ignored for Hibernate as it does not implement {@link Metamodel#managedType(Class)} correctly (does not consider - * {@link MappedSuperclass}es correctly). - * - * @see HHH-6896 - */ @Test // DATAJPA-141 - @Disabled void detectsIdTypeForMappedSuperclass() { JpaEntityInformation information = getEntityInformation(AbstractPersistable.class, em); @@ -147,7 +136,7 @@ void returnsPartialEmptyDerivedIdOfEntityWithIdClassCorrectly() { void favoursVersionAnnotationIfPresent() { EntityInformation information = new JpaMetamodelEntityInformation<>(VersionedUser.class, - em.getMetamodel()); + em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil()); VersionedUser entity = new VersionedUser(); assertThat(information.isNew(entity)).isTrue(); @@ -162,11 +151,11 @@ void favoursVersionAnnotationIfPresent() { @Test // DATAJPA-348 void findsIdClassOnMappedSuperclass() { - EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersitenceUnitName()); + EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersistenceUnitName()); EntityManager em = emf.createEntityManager(); EntityInformation information = new JpaMetamodelEntityInformation<>(Sample.class, - em.getMetamodel()); + em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil()); assertThat(information.getIdType()).isEqualTo(BaseIdClass.class); } @@ -175,7 +164,7 @@ void findsIdClassOnMappedSuperclass() { void detectsNewStateForEntityWithPrimitiveId() { EntityInformation information = new JpaMetamodelEntityInformation<>( - SampleWithPrimitiveId.class, em.getMetamodel()); + SampleWithPrimitiveId.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil()); SampleWithPrimitiveId sample = new SampleWithPrimitiveId(); assertThat(information.isNew(sample)).isTrue(); @@ -187,7 +176,8 @@ void detectsNewStateForEntityWithPrimitiveId() { @Test // DATAJPA-509 void jpaMetamodelEntityInformationShouldRespectExplicitlyConfiguredEntityNameFromOrmXml() { - JpaEntityInformation info = new JpaMetamodelEntityInformation<>(Role.class, em.getMetamodel()); + JpaEntityInformation info = new JpaMetamodelEntityInformation<>(Role.class, em.getMetamodel(), + em.getEntityManagerFactory().getPersistenceUnitUtil()); assertThat(info.getEntityName()).isEqualTo("ROLE"); } @@ -196,7 +186,7 @@ void jpaMetamodelEntityInformationShouldRespectExplicitlyConfiguredEntityNameFro void considersEntityWithPrimitiveVersionPropertySetToDefaultNew() { EntityInformation information = new JpaMetamodelEntityInformation<>( - PrimitiveVersionProperty.class, em.getMetamodel()); + PrimitiveVersionProperty.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil()); assertThat(information.isNew(new PrimitiveVersionProperty())).isTrue(); } @@ -205,7 +195,7 @@ void considersEntityWithPrimitiveVersionPropertySetToDefaultNew() { void considersEntityAsNotNewWhenHavingIdSetAndUsingPrimitiveTypeForVersionProperty() { EntityInformation information = new JpaMetamodelEntityInformation<>( - PrimitiveVersionProperty.class, em.getMetamodel()); + PrimitiveVersionProperty.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil()); PrimitiveVersionProperty pvp = new PrimitiveVersionProperty(); pvp.id = 100L; @@ -217,7 +207,7 @@ void considersEntityAsNotNewWhenHavingIdSetAndUsingPrimitiveTypeForVersionProper void fallsBackToIdInspectionForAPrimitiveVersionProperty() { EntityInformation information = new JpaMetamodelEntityInformation<>( - PrimitiveVersionProperty.class, em.getMetamodel()); + PrimitiveVersionProperty.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil()); PrimitiveVersionProperty pvp = new PrimitiveVersionProperty(); pvp.version = 1L; @@ -229,7 +219,8 @@ void fallsBackToIdInspectionForAPrimitiveVersionProperty() { } @Test // DATAJPA-582 - void considersEntityWithUnsetCompundIdNew() { + // @Disabled + void considersEntityWithUnsetCompoundIdNew() { EntityInformation information = getEntityInformation(SampleWithIdClass.class, em); @@ -260,11 +251,7 @@ void considersEntityWithNonPrimitiveNonNullIdTypeNotNew() { assertThat(information.isNew(user)).isFalse(); } - /** - * Ignored as Hibernate < 4.3 doesn't expose the version property properly if it's declared on the superclass. - */ @Test // DATAJPA-820 - @Disabled void detectsVersionPropertyOnMappedSuperClass() { EntityInformation information = getEntityInformation(ConcreteType1.class, em); @@ -275,7 +262,7 @@ void detectsVersionPropertyOnMappedSuperClass() { @Test // DATAJPA-1105 void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() { - EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersitenceUnitName()); + EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersistenceUnitName()); EntityManager em = emf.createEntityManager(); JpaEntityInformation information = getEntityInformation(EntityWithNestedIdClass.class, @@ -293,6 +280,7 @@ void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType( } @Test // DATAJPA-1416 + @Disabled void proxiedIdClassElement() { JpaEntityInformation information = getEntityInformation( @@ -315,13 +303,13 @@ void proxiedIdClassElement() { } @Test // DATAJPA-1576 - @Disabled void prefersPrivateGetterOverFieldAccess() { - EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersitenceUnitName()); + EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersistenceUnitName()); EntityManager em = emf.createEntityManager(); - JpaEntityInformation information = getEntityInformation(EntityWithPrivateIdGetter.class, em); + JpaEntityInformation information = getEntityInformation( + EntityWithPrivateIdGetter.class, em); EntityWithPrivateIdGetter entity = new EntityWithPrivateIdGetter(); @@ -330,10 +318,6 @@ void prefersPrivateGetterOverFieldAccess() { assertThat(id).isEqualTo(42L); } - String getMetadadataPersitenceUnitName() { - return "metadata"; - } - @SuppressWarnings("serial") private static class BaseIdClass implements Serializable { @@ -362,7 +346,8 @@ public static class Sample extends Identifiable { public static class EntityWithNestedIdClass { @Id Long id; - @Id @ManyToOne private EntityWithIdClass reference; + @Id + @ManyToOne private EntityWithIdClass reference; } @Entity @@ -389,7 +374,7 @@ public static class EntityWithNestedIdClassPK implements Serializable { } @Entity - public static class EntityWithPrivateIdGetter implements Serializable{ + public static class EntityWithPrivateIdGetter implements Serializable { private long id = 0; @@ -402,4 +387,49 @@ public void setId(long id) { this.id = id; } } + + @Entity + public static class ExampleEntityWithStringId { + + private UUID clientId; + + public UUID getId() { + return this.clientId; + } + + public void setId(UUID clientId) { + this.clientId = clientId; + } + + public void setClientId(String clientId) { + this.clientId = UUID.fromString(clientId); + } + + @Id + public String getClientId() { + return clientId == null ? null : clientId.toString(); + } + } + + @Entity + public static class ExampleEntityWithUUIDId { + + @Id private UUID clientId; + + public UUID getId() { + return this.clientId; + } + + public void setId(UUID clientId) { + this.clientId = clientId; + } + + public void setClientId(String clientId) { + this.clientId = UUID.fromString(clientId); + } + + public String getClientId() { + return clientId == null ? null : clientId.toString(); + } + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java index 9b509bf5a8..a3730c19f8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java @@ -19,15 +19,18 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.metamodel.IdentifiableType; import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.Type; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -48,17 +51,25 @@ @MockitoSettings(strictness = Strictness.LENIENT) class JpaMetamodelEntityInformationUnitTests { + @Mock EntityManager em; + @Mock EntityManagerFactory entityManagerFactory; + @Mock PersistenceUnitUtil persistenceUnit; @Mock Metamodel metamodel; @Mock IdentifiableType type; @Mock SingularAttribute first, second; - @Mock @SuppressWarnings("rawtypes") Type idType; + @Mock + @SuppressWarnings("rawtypes") Type idType; @BeforeEach @SuppressWarnings("unchecked") void setUp() { + when(em.getMetamodel()).thenReturn(metamodel); + when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory); + when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnit); + when(first.getName()).thenReturn("first"); when(second.getName()).thenReturn("second"); Set> attributes = new HashSet<>(asList(first, second)); @@ -76,12 +87,13 @@ void setUp() { void doesNotCreateIdIfAllPartialAttributesAreNull() { JpaMetamodelEntityInformation information = new JpaMetamodelEntityInformation<>( - PersistableWithIdClass.class, metamodel); + PersistableWithIdClass.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil()); PersistableWithIdClass entity = new PersistableWithIdClass(null, null); assertThat(information.getId(entity)).isNull(); entity = new PersistableWithIdClass(2L, null); + when(persistenceUnit.getIdentifier(entity)).thenReturn(2L); assertThat(information.getId(entity)).isNotNull(); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java index 13d7e78abc..298b310bb0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java @@ -18,6 +18,9 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.Type; @@ -42,16 +45,23 @@ @MockitoSettings(strictness = Strictness.LENIENT) class JpaPersistableEntityInformationUnitTests { + @Mock EntityManager em; + @Mock EntityManagerFactory entityManagerFactory; @Mock Metamodel metamodel; + @Mock PersistenceUnitUtil persistenceUnitUtil; @Mock EntityType type; - @Mock @SuppressWarnings("rawtypes") Type idType; + @Mock + @SuppressWarnings("rawtypes") Type idType; @BeforeEach @SuppressWarnings("unchecked") void setUp() { + when(em.getMetamodel()).thenReturn(metamodel); + when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory); + when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnitUtil); when(metamodel.managedType(Object.class)).thenThrow(IllegalArgumentException.class); when(metamodel.managedType(Foo.class)).thenReturn(type); when(type.getIdType()).thenReturn(idType); @@ -60,7 +70,8 @@ void setUp() { @Test void usesPersistableMethodsForIsNewAndGetId() { - EntityInformation entityInformation = new JpaPersistableEntityInformation<>(Foo.class, metamodel); + EntityInformation entityInformation = new JpaPersistableEntityInformation<>(Foo.class, em.getMetamodel(), + em.getEntityManagerFactory().getPersistenceUnitUtil()); Foo foo = new Foo(); assertThat(entityInformation.isNew(foo)).isFalse(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java index e2c09f6098..76da1e834a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java @@ -17,16 +17,25 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; /** * OpenJpa execution for {@link JpaMetamodelEntityInformationIntegrationTests}. * * @author Oliver Gierke + * @author Greg Turnquist */ -@ContextConfiguration("classpath:openjpa.xml") +@ExtendWith(SpringExtension.class) +@ContextConfiguration({ "classpath:infrastructure.xml", "classpath:openjpa.xml" }) class OpenJpaMetamodelEntityInformationIntegrationTests extends JpaMetamodelEntityInformationIntegrationTests { + @Override + String getMetadadataPersistenceUnitName() { + return "metadata_oj"; + } + /** * Re-activate test. */ @@ -50,9 +59,4 @@ void findsIdClassOnMappedSuperclass() {} void detectsVersionPropertyOnMappedSuperClass() { super.detectsVersionPropertyOnMappedSuperClass(); } - - @Override - String getMetadadataPersitenceUnitName() { - return "metadata_oj"; - } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index 59b5d58870..ca9b6d4976 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -17,6 +17,8 @@ import static org.assertj.core.api.Assertions.*; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import lombok.Data; import java.sql.Date; @@ -25,9 +27,6 @@ import java.util.Set; import java.util.stream.Stream; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; - import org.hibernate.LazyInitializationException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -83,8 +82,8 @@ class QuerydslJpaPredicateExecutorUnitTests { @BeforeEach void setUp() { - JpaEntityInformation information = new JpaMetamodelEntityInformation<>(User.class, - em.getMetamodel()); + JpaEntityInformation information = new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel(), + em.getEntityManagerFactory().getPersistenceUnitUtil()); SimpleJpaRepository repository = new SimpleJpaRepository<>(information, em); dave = repository.save(new User("Dave", "Matthews", "dave@matthews.com")); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java index 9d21f3e38f..e09cab8fc3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java @@ -17,13 +17,13 @@ import static org.assertj.core.api.Assertions.*; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + import java.sql.Date; import java.time.LocalDate; import java.util.List; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -76,8 +76,8 @@ class QuerydslJpaRepositoryTests { @BeforeEach void setUp() { - JpaEntityInformation information = new JpaMetamodelEntityInformation<>(User.class, - em.getMetamodel()); + JpaEntityInformation information = new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel(), + em.getEntityManagerFactory().getPersistenceUnitUtil()); repository = new QuerydslJpaRepository<>(information, em); dave = repository.save(new User("Dave", "Matthews", "dave@matthews.com")); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index ebbc549041..66291519aa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -22,6 +22,8 @@ import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; @@ -57,6 +59,8 @@ class SimpleJpaRepositoryUnitTests { private SimpleJpaRepository repo; @Mock EntityManager em; + @Mock EntityManagerFactory entityManagerFactory; + @Mock PersistenceUnitUtil persistenceUnitUtil; @Mock CriteriaBuilder builder; @Mock CriteriaQuery criteriaQuery; @Mock CriteriaQuery countCriteriaQuery; @@ -172,6 +176,9 @@ void doNothingWhenNewInstanceGetsDeleted() { User newUser = new User(); newUser.setId(null); + when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory); + when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnitUtil); + repo.delete(newUser); verify(em, never()).find(any(Class.class), any(Object.class)); @@ -186,6 +193,9 @@ void doNothingWhenNonExistentInstanceGetsDeleted() { newUser.setId(23); when(information.isNew(newUser)).thenReturn(false); + when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory); + when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnitUtil); + when(persistenceUnitUtil.getIdentifier(any())).thenReturn(23); when(em.find(User.class, 23)).thenReturn(null); repo.delete(newUser); diff --git a/spring-data-jpa/src/test/resources/META-INF/persistence.xml b/spring-data-jpa/src/test/resources/META-INF/persistence.xml index 9239b61d7d..7e55f6e600 100644 --- a/spring-data-jpa/src/test/resources/META-INF/persistence.xml +++ b/spring-data-jpa/src/test/resources/META-INF/persistence.xml @@ -52,6 +52,10 @@ org.springframework.data.jpa.domain.sample.Dummy org.springframework.data.jpa.domain.sample.SampleWithIdClassIncludingEntity org.springframework.data.jpa.domain.sample.SampleWithIdClassIncludingEntity$OtherEntity + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithNestedIdClass + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithIdClass + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter true @@ -108,6 +112,8 @@ org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithNestedIdClass org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithIdClass org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithStringId + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId true @@ -123,6 +129,9 @@ org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithNestedIdClass org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithIdClass + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithStringId + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId org.springframework.data.jpa.domain.sample.Dummy true @@ -152,5 +161,41 @@ + + org.hibernate.jpa.HibernatePersistenceProvider + org.springframework.data.jpa.domain.sample.CustomAbstractPersistable + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender + org.springframework.data.jpa.domain.sample.MailUser + org.springframework.data.jpa.domain.sample.User + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithNestedIdClass + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithIdClass + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithStringId + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId + true + + + + + + org.eclipse.persistence.jpa.PersistenceProvider + org.springframework.data.jpa.domain.sample.CustomAbstractPersistable + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender + org.springframework.data.jpa.domain.sample.MailUser + org.springframework.data.jpa.domain.sample.User + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithNestedIdClass + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithIdClass + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithStringId + org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId + true + + + + diff --git a/spring-data-jpa/src/test/resources/eclipselink.xml b/spring-data-jpa/src/test/resources/eclipselink.xml index 069583385c..1eec853095 100644 --- a/spring-data-jpa/src/test/resources/eclipselink.xml +++ b/spring-data-jpa/src/test/resources/eclipselink.xml @@ -6,8 +6,8 @@ http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> - - + + org.hsqldb.jdbcDriver jdbc:hsqldb:mem:hades From 63919b5a7bdd2eaa5ac2bbc4127a21b1fae01707 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 15 Jun 2022 13:50:11 -0500 Subject: [PATCH 218/821] Drop links to changelog.txt from README. Closes #2569. --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 30b92719c7..22175f8fc6 100644 --- a/README.adoc +++ b/README.adoc @@ -133,7 +133,7 @@ Having trouble with Spring Data? We’d love to help! https://docs.spring.io/spring-data/jpa/docs/current/reference/html/[reference documentation], and https://docs.spring.io/spring-data/jpa/docs/current/api/[Javadocs]. * Learn the Spring basics – Spring Data builds on Spring Framework, check the https://spring.io[spring.io] web-site for a wealth of reference documentation. If you are just starting out with Spring, try one of the https://spring.io/guides[guides]. -* If you are upgrading, check out the https://docs.spring.io/spring-data/jpa/docs/current/changelog.txt[changelog] for "`new and noteworthy`" features. +* If you are upgrading, check out the https://github.com/spring-projects/spring-data-jpa/releases[Spring Data JPA release notes] and scroll down to the one you're considering. See the details there. (Also check out the https://github.com/spring-projects/spring-data-jpa/releases/latest[latest stable release]) * Ask a question - we monitor https://stackoverflow.com[stackoverflow.com] for questions tagged with https://stackoverflow.com/tags/spring-data[`spring-data-jpa`]. You can also chat with the community on https://gitter.im/spring-projects/spring-data[Gitter]. * Report bugs with Spring Data JPA in the https://github.com/spring-projects/spring-data-jpa/issues[GitHub issue tracker]. From c4a6d382edc98fd82cd0de33b7fefea5a8f79d7e Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Tue, 28 Jun 2022 15:21:33 +0200 Subject: [PATCH 219/821] Adds support for more SelectBody types in JSqlParserQueryEhancer. We now support `ValuesStatement` and `SetOperationList`. This allows native queries to use `union`, `except`, and `with` statements in native SQL queries. Closes #2578. --- .../query/JSqlParserQueryEnhancer.java | 97 +++++++++++- .../jpa/repository/UserRepositoryTests.java | 66 ++++++++ .../query/QueryEnhancerUnitTests.java | 144 +++++++++++++++++- .../jpa/repository/sample/UserRepository.java | 34 +++++ 4 files changed, 333 insertions(+), 8 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 9ac2bc7466..1c6c42ae80 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 @@ -29,9 +29,13 @@ 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.update.Update; +import net.sf.jsqlparser.statement.values.ValuesStatement; import java.util.ArrayList; import java.util.Collections; @@ -107,6 +111,13 @@ public String applySorting(Sort sort, @Nullable String alias) { } Select selectStatement = parseSelectStatement(queryString); + + if (selectStatement.getSelectBody()instanceof SetOperationList setOperationList) { + return applySortingToSetOperationList(setOperationList, sort); + } else if (!(selectStatement.getSelectBody() instanceof PlainSelect)) { + return queryString; + } + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); final Set joinAliases = getJoinAliases(selectBody); @@ -115,7 +126,7 @@ public String applySorting(Sort sort, @Nullable String alias) { List orderByElements = sort.stream() // .map(order -> getOrderClause(joinAliases, selectionAliases, alias, order)) // - .collect(Collectors.toList()); + .toList(); if (CollectionUtils.isEmpty(selectBody.getOrderByElements())) { selectBody.setOrderByElements(new ArrayList<>()); @@ -127,6 +138,33 @@ public String applySorting(Sort sort, @Nullable String alias) { } + /** + * Returns the {@link SetOperationList} as a string query with {@link Sort}s applied in the right order. + * + * @param setOperationListStatement + * @param sort + * @return + */ + private String applySortingToSetOperationList(SetOperationList setOperationListStatement, Sort sort) { + + // special case: ValuesStatements are detected as nested OperationListStatements + if (setOperationListStatement.getSelects().stream().anyMatch(ValuesStatement.class::isInstance)) { + return setOperationListStatement.toString(); + } + + // if (CollectionUtils.isEmpty(setOperationListStatement.getOrderByElements())) { + if (setOperationListStatement.getOrderByElements() == null) { + setOperationListStatement.setOrderByElements(new ArrayList<>()); + } + + List orderByElements = sort.stream() // + .map(order -> getOrderClause(Collections.emptySet(), Collections.emptySet(), null, order)) // + .toList(); + setOperationListStatement.getOrderByElements().addAll(orderByElements); + + return setOperationListStatement.toString(); + } + /** * Returns the aliases used inside the selection part in the query. * @@ -175,7 +213,12 @@ private Set getJoinAliases(String query) { return new HashSet<>(); } - return getJoinAliases((PlainSelect) parseSelectStatement(query).getSelectBody()); + Select selectStatement = parseSelectStatement(query); + if (selectStatement.getSelectBody()instanceof PlainSelect selectBody) { + return getJoinAliases(selectBody); + } + + return new HashSet<>(); } /** @@ -259,6 +302,17 @@ private String detectAlias(String query) { } Select selectStatement = parseSelectStatement(query); + + /* + 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 + */ + if (!(selectStatement.getSelectBody() instanceof PlainSelect)) { + return null; + } + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); return detectAlias(selectBody); } @@ -273,6 +327,10 @@ private String detectAlias(String query) { @Nullable private static String detectAlias(PlainSelect selectBody) { + if (selectBody.getFromItem() == null) { + return null; + } + Alias alias = selectBody.getFromItem().getAlias(); return alias == null ? null : alias.getName(); } @@ -287,6 +345,14 @@ public String createCountQueryFor(@Nullable String countProjection) { Assert.hasText(this.query.getQueryString(), "OriginalQuery must not be null or empty"); Select selectStatement = parseSelectStatement(this.query.getQueryString()); + + /* + We only support count queries for {@link PlainSelect}. + */ + if (!(selectStatement.getSelectBody() instanceof PlainSelect)) { + return this.query.getQueryString(); + } + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); // remove order by @@ -322,8 +388,15 @@ public String createCountQueryFor(@Nullable String countProjection) { Function jSqlCount = getJSqlCount(Collections.singletonList(countProp), distinct); selectBody.setSelectItems(Collections.singletonList(new SelectExpressionItem(jSqlCount))); - return selectBody.toString(); + if (CollectionUtils.isEmpty(selectStatement.getWithItemsList())) { + return selectBody.toString(); + } + String withStatements = selectStatement.getWithItemsList().stream() // + .map(WithItem::toString) // + .collect(Collectors.joining(",")); + + return "with " + withStatements + "\n" + selectBody; } @Override @@ -336,9 +409,23 @@ public String getProjection() { Assert.hasText(query.getQueryString(), "Query must not be null or empty"); Select selectStatement = parseSelectStatement(query.getQueryString()); - PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); - return selectBody.getSelectItems() // + if (selectStatement.getSelectBody() instanceof ValuesStatement) { + return ""; + } + + 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); + + if (!(selectBody instanceof PlainSelect)) { + return ""; + } + } + + return ((PlainSelect) selectBody).getSelectItems() // .stream() // .map(Object::toString) // .collect(Collectors.joining(", ")).trim(); 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 7bc48e30a6..25dcfcdf6e 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 @@ -2902,6 +2902,72 @@ public void correctlyBuildSortClauseWhenSortingByFunctionAliasAndFunctionContain repository.findAllAndSortByFunctionResultNamedParameter("prefix", "suffix", Sort.by("idWithPrefixAndSuffix")); } + @Test // GH-2578 + void simpleNativeExceptTest() { + + flushTestUsers(); + + List foundIds = repository.findWithSimpleExceptNative(); + + assertThat(foundIds) // + .isNotEmpty() // + .contains("Oliver", "kevin"); + } + + @Test // GH-2578 + void simpleNativeUnionTest() { + + flushTestUsers(); + + List foundIds = repository.findWithSimpleUnionNative(); + + assertThat(foundIds) // + .isNotEmpty() // + .containsExactlyInAnyOrder("Dave", "Joachim", "Oliver", "kevin"); + } + + @Test // GH-2578 + void complexNativeExceptTest() { + + flushTestUsers(); + + List foundIds = repository.findWithComplexExceptNative(); + + assertThat(foundIds).containsExactly("Oliver", "kevin"); + } + + @Test // GH-2578 + void simpleValuesStatementNative() { + + flushTestUsers(); + + List foundIds = repository.valuesStatementNative(); + + assertThat(foundIds).containsExactly(1); + } + + @Test // GH-2578 + void withStatementNative() { + + flushTestUsers(); + + List foundData = repository.withNativeStatement(); + + assertThat(foundData) // + .map(User::getFirstname) // + .containsExactly("Joachim", "Dave", "kevin"); + } + + @Test // GH-2578 + void complexWithNativeStatement() { + + flushTestUsers(); + + List foundData = repository.complexWithNativeStatement(); + + assertThat(foundData).containsExactly("joachim", "dave", "kevin"); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); 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 3572fa403e..c879468633 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 @@ -687,8 +687,8 @@ void detectsJoinAliasesCorrectly(String queryString, List aliases) { void correctFunctionAliasWithComplexNestedFunctions() { String queryString = "\nSELECT \nCAST(('{' || string_agg(distinct array_to_string(c.institutes_ids, ','), ',') || '}') AS bigint[]) as institutesIds\nFROM\ncity c"; - StringQuery nativeQuery = new StringQuery(queryString, true); + StringQuery nativeQuery = new StringQuery(queryString, true); JSqlParserQueryEnhancer queryEnhancer = (JSqlParserQueryEnhancer) getEnhancer(nativeQuery); assertThat(queryEnhancer.getSelectionAliases()).contains("institutesIds"); @@ -696,6 +696,7 @@ void correctFunctionAliasWithComplexNestedFunctions() { @Test // GH-2441 void correctApplySortOnComplexNestedFunctionQuery() { + String queryString = "SELECT dd.institutesIds FROM (\n" // + " SELECT\n" // + " CAST(('{' || string_agg(distinct array_to_string(c.institutes_ids, ','), ',') || '}') AS bigint[]) as institutesIds\n" @@ -704,9 +705,7 @@ void correctApplySortOnComplexNestedFunctionQuery() { + " ) dd"; StringQuery nativeQuery = new StringQuery(queryString, true); - QueryEnhancer queryEnhancer = getEnhancer(nativeQuery); - String result = queryEnhancer.applySorting(Sort.by(new Sort.Order(Sort.Direction.ASC, "institutesIds"))); assertThat(result).containsIgnoringCase("order by dd.institutesIds"); @@ -716,6 +715,7 @@ void correctApplySortOnComplexNestedFunctionQuery() { void countQueryUsesCorrectVariable() { StringQuery nativeQuery = new StringQuery("SELECT * FROM User WHERE created_at > $1", true); + QueryEnhancer queryEnhancer = getEnhancer(nativeQuery); String countQueryFor = queryEnhancer.createCountQueryFor(); assertThat(countQueryFor).isEqualTo("SELECT count(*) FROM User WHERE created_at > $1"); @@ -751,6 +751,144 @@ void modifyingQueriesAreDetectedCorrectly() { assertThat(QueryEnhancerFactory.forQuery(modiQuery).createCountQueryFor()).isEqualToIgnoringCase(modifyingQuery); } + @Test // GH-2578 + void setOperationListWorksWithJSQLParser() { + + String setQuery = "select SOME_COLUMN from SOME_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // + + "except \n" // + + "select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNullOrEmpty(); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); + assertThat(queryEnhancer.applySorting(Sort.by("SOME_COLUMN"))).endsWith("ORDER BY SOME_COLUMN ASC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void complexSetOperationListWorksWithJSQLParser() { + + String setQuery = "select SOME_COLUMN from SOME_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // + + "except \n" // + + "select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // + + "union select SOME_COLUMN from SOME_OTHER_OTHER_TABLE"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNullOrEmpty(); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); + assertThat(queryEnhancer.applySorting(Sort.by("SOME_COLUMN").ascending())).endsWith("ORDER BY SOME_COLUMN ASC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void deeplyNestedcomplexSetOperationListWorksWithJSQLParser() { + + String setQuery = "SELECT CustomerID FROM (\n" // + + "\t\t\tselect * from Customers\n" // + + "\t\t\texcept\n"// + + "\t\t\tselect * from Customers where country = 'Austria'\n"// + + "\t)\n" // + + "\texcept\n"// + + "\tselect CustomerID from customers where country = 'Germany'\n"// + + "\t;"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNullOrEmpty(); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("CustomerID"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); + assertThat(queryEnhancer.applySorting(Sort.by("CustomerID").descending())).endsWith("ORDER BY CustomerID DESC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("CustomerID"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void valuesStatementsWorksWithJSQLParser() { + + String setQuery = "VALUES (1, 2, 'test')"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNullOrEmpty(); + assertThat(stringQuery.getProjection()).isNullOrEmpty(); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); + assertThat(queryEnhancer.applySorting(Sort.by("CustomerID").descending())).isEqualTo(setQuery); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); + assertThat(queryEnhancer.getProjection()).isNullOrEmpty(); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void withStatementsWorksWithJSQLParser() { + + String setQuery = "with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))) \n" + + "select day, value from sample_data as a"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isEqualToIgnoringCase("a"); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("day, value"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase( + "with sample_data (day, value) AS (VALUES ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16)))\n" + + "SELECT count(a) FROM sample_data AS a"); + assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).endsWith("ORDER BY a.day DESC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase("a"); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("day, value"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void multipleWithStatementsWorksWithJSQLParser() { + + String setQuery = "with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))), test2 as (values (1,2,3)) \n" + + "select day, value from sample_data as a"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isEqualToIgnoringCase("a"); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("day, value"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase( + "with sample_data (day, value) AS (VALUES ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))),test2 AS (VALUES (1, 2, 3))\n" + + "SELECT count(a) FROM sample_data AS a"); + assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).endsWith("ORDER BY a.day DESC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase("a"); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("day, value"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + public static Stream detectsJoinAliasesCorrectlySource() { return Stream.of( // 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 c913ddd541..f9fd6b9865 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 @@ -642,6 +642,40 @@ List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter @Query(value = "update SD_User u set u.active = false where u.id = :userId", nativeQuery = true) void setActiveToFalseWithModifyingNative(@Param("userId") int userId); + // GH-2578 + @Query(value = "SELECT u.firstname from SD_User u where u.age < 32 " // + + "except " // + + "SELECT u.firstname from SD_User u where u.age >= 32 ", nativeQuery = true) + List findWithSimpleExceptNative(); + + // GH-2578 + @Query(value = "SELECT u.firstname from SD_User u where u.age < 32 " // + + "union " // + + "SELECT u.firstname from SD_User u where u.age >= 32 ", nativeQuery = true) + List findWithSimpleUnionNative(); + + // GH-2578 + @Query(value = "SELECT u.firstname from (select * from SD_User u where u.age < 32) u " // + + "except " // + + "SELECT u.firstname from SD_User u where u.age >= 32 ", nativeQuery = true) + List findWithComplexExceptNative(); + + // GH-2578 + @Query(value = "VALUES (1)", nativeQuery = true) + List valuesStatementNative(); + + // GH-2578 + @Query(value = "with sample_data as ( Select * from SD_User u where u.age > 30 ) \n select * from sample_data", + nativeQuery = true) + List withNativeStatement(); + + // GH-2578 + @Query(value = "with sample_data as ( Select * from SD_User u where u.age > 30 ), \n " // + + "another as ( Select * from SD_User u) \n " // + + "select lower(s.firstname) as lowFirst from sample_data as s,another as a where s.firstname = a.firstname ", + nativeQuery = true) + List complexWithNativeStatement(); + interface RolesAndFirstname { String getFirstname(); From bc4347c50735b45f2d6b7f93764bd6c03e0ffb99 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 28 Jun 2022 13:52:07 -0500 Subject: [PATCH 220/821] Upgrade Hibernate to 5.6.9.Final. Closes #2583. --- pom.xml | 2 +- spring-data-envers/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ca8ee5b81e..6a62754327 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 3.0.2 - 5.6.0.Final + 5.6.9.Final 4.3 8.0.23 42.2.19 diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 8e2de43f59..59eb8eab88 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -48,7 +48,7 @@ - 5.5.7.Final + ${hibernate} spring.data.envers From 1bfc3595ce2e14cdc03decfe350f20e139e3c877 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 1 Jul 2022 08:19:00 +0200 Subject: [PATCH 221/821] Add AOT repository support. We now use the AOT infrastructure of Spring Framework 6 and data commons to provide AOT support building the foundation for native image compilation. Additionally we register hints for GraalVM native image. Also update jpa auditing configuration to avoid inner bean definitions. See: #2497 Original Pull Request: #2588 --- .../data/jpa/aot/DataJpaRuntimeHints.java | 49 ++++++++++++++++++ .../config/JpaAuditingRegistrar.java | 51 +++++++++++++++---- .../config/JpaRepositoryConfigExtension.java | 35 ++++++++++--- .../resources/META-INF/spring/aot.factories | 2 + .../EntityManagerFactoryRefUnitTests.java | 5 +- 5 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java create mode 100644 spring-data-jpa/src/main/resources/META-INF/spring/aot.factories diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java new file mode 100644 index 0000000000..c76fd74be7 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.aot; + +import java.util.Arrays; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.TypeReference; +import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect; +import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.data.jpa.repository.support.SimpleJpaRepository; +import org.springframework.lang.Nullable; + +/** + * @author Christoph Strobl + * @since 3.0 + */ +public class DataJpaRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + + hints.proxies().registerJdkProxy(org.springframework.data.jpa.repository.support.CrudMethodMetadata.class, + org.springframework.aop.SpringProxy.class, org.springframework.aop.framework.Advised.class, + org.springframework.core.DecoratingProxy.class); + hints.reflection().registerTypes( + Arrays.asList(TypeReference.of(AnnotationBeanConfigurerAspect.class), + TypeReference.of(AuditingBeanFactoryPostProcessor.class), TypeReference.of(AuditingEntityListener.class)), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); + hints.reflection().registerTypes(Arrays.asList(TypeReference.of(SimpleJpaRepository.class)), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS)); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java index fc0404169f..1f7cb5add5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java @@ -21,10 +21,12 @@ import java.lang.annotation.Annotation; import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; @@ -34,7 +36,9 @@ import org.springframework.data.config.ParsingUtils; import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.repository.config.PersistentEntitiesFactoryBean; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -42,6 +46,7 @@ * {@link ImportBeanDefinitionRegistrar} to enable {@link EnableJpaAuditing} annotation. * * @author Thomas Darimont + * @author Christoph Strobl */ class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { @@ -57,16 +62,6 @@ protected String getAuditingHandlerBeanName() { return "jpaAuditingHandler"; } - @Override - protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) { - - BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class); - definition.addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME); - - BeanDefinitionBuilder builder = super.getAuditHandlerBeanDefinitionBuilder(configuration); - return builder.addConstructorArgValue(definition.getBeanDefinition()); - } - @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { @@ -95,6 +90,42 @@ protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandle registerInfrastructureBeanWithId(builder.getRawBeanDefinition(), AuditingEntityListener.class.getName(), registry); } + @Override + protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration, + BeanDefinitionRegistry registry) { + + String persistentEntitiesBeanName = detectPersistentEntitiesBeanName(registry); + + if (persistentEntitiesBeanName == null) { + + persistentEntitiesBeanName = BeanDefinitionReaderUtils.uniqueBeanName("jpaPersistentEntities", registry); + + // TODO: https://github.com/spring-projects/spring-framework/issues/28728 + BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntities.class) // + .setFactoryMethod("of") // + .addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME); + + registry.registerBeanDefinition(persistentEntitiesBeanName, definition.getBeanDefinition()); + } + + builder.addConstructorArgReference(persistentEntitiesBeanName); + } + + @Nullable + private static String detectPersistentEntitiesBeanName(BeanDefinitionRegistry registry) { + + if (registry instanceof ListableBeanFactory beanFactory) { + for (String bn : beanFactory.getBeanNamesForType(PersistentEntities.class)) { + if (bn.startsWith("jpa")) { + return bn; + } + } + } + + return null; + } + + /** * @param registry, the {@link BeanDefinitionRegistry} to be used to register the * {@link AnnotationBeanConfigurerAspect}. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 701548d565..07550f533c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -17,22 +17,26 @@ import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.*; +import jakarta.persistence.Entity; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceUnit; + import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Locale; +import java.util.Map; import java.util.Optional; import java.util.Set; -import jakarta.persistence.Entity; -import jakarta.persistence.MappedSuperclass; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.PersistenceUnit; - +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationConfigUtils; @@ -77,6 +81,8 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi private static final String JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME = "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup"; private static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter"; + private final Map entityManagerRefs = new LinkedHashMap<>(); + @Override public String getModuleName() { return "JPA"; @@ -107,7 +113,7 @@ public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSo Optional transactionManagerRef = source.getAttribute("transactionManagerRef"); builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME)); - builder.addPropertyValue("entityManager", getEntityManagerBeanDefinitionFor(source, source.getSource())); + builder.addPropertyReference("entityManager", entityManagerRefs.get(source)); builder.addPropertyValue(ESCAPE_CHARACTER_PROPERTY, getEscapeCharacter(source).orElse('\\')); builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME); } @@ -149,6 +155,8 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf super.registerBeansForRoot(registry, config); + prepareAndRegisterSharedEntityManger(registry, config); + Object source = config.getSource(); registerLazyIfNotAlreadyRegistered( @@ -191,6 +199,21 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf }, registry, JpaEvaluationContextExtension.class.getName(), source); } + private String prepareAndRegisterSharedEntityManger(BeanDefinitionRegistry registry, + RepositoryConfigurationSource config) { + + AbstractBeanDefinition entityManager = getEntityManagerBeanDefinitionFor(config, null); + entityManager.setRole(BeanDefinition.ROLE_SUPPORT); + entityManager.setSynthetic(true); + entityManager.setPrimary(false); + entityManager.setAutowireCandidate(false); + + String entityManagerBeanName = BeanDefinitionReaderUtils.uniqueBeanName("jpaSharedEM", registry); + entityManagerRefs.put(config, entityManagerBeanName); + registry.registerBeanDefinition(entityManagerBeanName, entityManager); + return entityManagerBeanName; + } + @Override protected ClassLoader getConfigurationInspectionClassLoader(ResourceLoader loader) { diff --git a/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories b/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 0000000000..bb56670aa6 --- /dev/null +++ b/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.aot.hint.RuntimeHintsRegistrar=\ + org.springframework.data.jpa.aot.DataJpaRuntimeHints diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java index f507bb2890..3073ad20e0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java @@ -19,9 +19,11 @@ import jakarta.persistence.EntityManagerFactory; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanReference; +import org.springframework.beans.factory.config.RuntimeBeanNameReference; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; @@ -35,6 +37,7 @@ class EntityManagerFactoryRefUnitTests { @Test + @Disabled void repositoriesGetTheSecondEntityManagerFactoryInjected2() { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); @@ -43,7 +46,7 @@ void repositoriesGetTheSecondEntityManagerFactoryInjected2() { BeanDefinition bean = factory.getBeanDefinition("userRepository"); Object value = getPropertyValue(bean, "entityManager"); - assertThat(value instanceof BeanDefinition).isTrue(); + assertThat(value instanceof RuntimeBeanNameReference).isTrue(); BeanDefinition emCreator = (BeanDefinition) value; BeanReference reference = getConstructorBeanReference(emCreator, 0); From 9c757660d244393b291297fd700f0aad3764230b Mon Sep 17 00:00:00 2001 From: Vladislav Yukharin Date: Mon, 20 Jun 2022 00:31:49 +0600 Subject: [PATCH 222/821] Fix grammar mistakes in countOccurrences method of QueryUtils class. Original pull request #2572 --- .../data/jpa/repository/query/QueryUtils.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 94c080dec6..5c1fd5fa24 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -78,6 +78,7 @@ * @author Jędrzej Biedrzycki * @author Darin Manica * @author Simon Paradies + * @author Vladislav Yukharin */ public abstract class QueryUtils { @@ -292,7 +293,7 @@ public static String applySorting(String query, Sort sort, @Nullable String alia * @return {@code true} if the query has {@code order by} clause, {@code false} otherwise */ private static boolean hasOrderByClause(String query) { - return countOccurences(ORDER_BY, query) > countOccurences(ORDER_BY_IN_WINDOW_OR_SUBSELECT, query); + return countOccurrences(ORDER_BY, query) > countOccurrences(ORDER_BY_IN_WINDOW_OR_SUBSELECT, query); } /** @@ -300,17 +301,17 @@ private static boolean hasOrderByClause(String query) { * * @param pattern regex with a group to match * @param string analysed string - * @return the number of occurences of the pattern in the string + * @return the number of occurrences of the pattern in the string */ - private static int countOccurences(Pattern pattern, String string) { + private static int countOccurrences(Pattern pattern, String string) { Matcher matcher = pattern.matcher(string); - int occurences = 0; + int occurrences = 0; while (matcher.find()) { - occurences++; + occurrences++; } - return occurences; + return occurrences; } /** From 58212721123b96c93a343b6f3e8d9cc0b9fbf1f2 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 11 Jul 2022 14:01:02 +0200 Subject: [PATCH 223/821] Guard optional runtime hints. This commit adds a guard to types that must not be present at AOT build time. See: #2497 --- .../data/jpa/aot/DataJpaRuntimeHints.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java index c76fd74be7..b7a71648f3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java @@ -21,11 +21,11 @@ import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.TypeReference; -import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect; import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; /** * @author Christoph Strobl @@ -36,14 +36,24 @@ public class DataJpaRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { - hints.proxies().registerJdkProxy(org.springframework.data.jpa.repository.support.CrudMethodMetadata.class, - org.springframework.aop.SpringProxy.class, org.springframework.aop.framework.Advised.class, + hints.proxies().registerJdkProxy(org.springframework.data.jpa.repository.support.CrudMethodMetadata.class, // + org.springframework.aop.SpringProxy.class, // + org.springframework.aop.framework.Advised.class, // org.springframework.core.DecoratingProxy.class); - hints.reflection().registerTypes( - Arrays.asList(TypeReference.of(AnnotationBeanConfigurerAspect.class), - TypeReference.of(AuditingBeanFactoryPostProcessor.class), TypeReference.of(AuditingEntityListener.class)), + + if (ClassUtils.isPresent("org.springframework.beans.factory.aspectj.ConfigurableObject", classLoader)) { + + hints.reflection().registerType( + TypeReference.of("org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"), hint -> hint + .withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); + } + + hints.reflection().registerTypes(Arrays.asList( // + TypeReference.of(AuditingBeanFactoryPostProcessor.class), // + TypeReference.of(AuditingEntityListener.class)), hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); - hints.reflection().registerTypes(Arrays.asList(TypeReference.of(SimpleJpaRepository.class)), + + hints.reflection().registerType(TypeReference.of(SimpleJpaRepository.class), hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS)); } } From 36764e5f1a8bf62be6d4cd3ce8cfc84b36d40aa6 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 11 Jul 2022 14:02:50 +0200 Subject: [PATCH 224/821] Simplify auditing setup. Use IsNewAwareAuditingHandler factory method to avoid exposing additional beans. See: #2497 --- .../config/JpaAuditingRegistrar.java | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java index 1f7cb5add5..2116c5789a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java @@ -21,12 +21,10 @@ import java.lang.annotation.Annotation; import org.springframework.beans.factory.BeanDefinitionStoreException; -import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; @@ -36,9 +34,6 @@ import org.springframework.data.config.ParsingUtils; import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import org.springframework.data.mapping.context.PersistentEntities; -import org.springframework.data.repository.config.PersistentEntitiesFactoryBean; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -94,38 +89,9 @@ protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandle protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration, BeanDefinitionRegistry registry) { - String persistentEntitiesBeanName = detectPersistentEntitiesBeanName(registry); - - if (persistentEntitiesBeanName == null) { - - persistentEntitiesBeanName = BeanDefinitionReaderUtils.uniqueBeanName("jpaPersistentEntities", registry); - - // TODO: https://github.com/spring-projects/spring-framework/issues/28728 - BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntities.class) // - .setFactoryMethod("of") // - .addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME); - - registry.registerBeanDefinition(persistentEntitiesBeanName, definition.getBeanDefinition()); - } - - builder.addConstructorArgReference(persistentEntitiesBeanName); + builder.setFactoryMethod("from").addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME); } - @Nullable - private static String detectPersistentEntitiesBeanName(BeanDefinitionRegistry registry) { - - if (registry instanceof ListableBeanFactory beanFactory) { - for (String bn : beanFactory.getBeanNamesForType(PersistentEntities.class)) { - if (bn.startsWith("jpa")) { - return bn; - } - } - } - - return null; - } - - /** * @param registry, the {@link BeanDefinitionRegistry} to be used to register the * {@link AnnotationBeanConfigurerAspect}. From aa6a809c31263f4ef96b8ad387249955f82550d0 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 11 Jul 2022 14:31:16 -0500 Subject: [PATCH 225/821] Introduce @Meta data support for repository methods. Closes #775. --- .../jpa/provider/PersistenceProvider.java | 41 ++- .../data/jpa/provider/QueryComment.java | 37 +++ .../data/jpa/repository/Meta.java | 43 +++ .../repository/query/AbstractJpaQuery.java | 9 + .../jpa/repository/query/JpaQueryMethod.java | 53 ++++ .../data/jpa/repository/query/Meta.java | 118 ++++++++ .../support/CrudMethodMetadata.java | 13 +- .../CrudMethodMetadataPostProcessor.java | 21 +- .../support/SimpleJpaRepository.java | 44 ++- .../query/JpaQueryMethodUnitTests.java | 6 +- ...ueryMethodEclipseLinkIntegrationTests.java | 247 +++++++++++++++++ ...dQueryMethodHibernateIntegrationTests.java | 254 ++++++++++++++++++ .../MetaAnnotatedQueryMethodUnitTests.java | 65 +++++ .../sample/RoleRepositoryWithMeta.java | 101 +++++++ src/main/asciidoc/jpa.adoc | 133 +++++++++ 15 files changed, 1164 insertions(+), 21 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryComment.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Meta.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepositoryWithMeta.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 73a09f6313..f2d90b0c87 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -18,22 +18,22 @@ import static org.springframework.data.jpa.provider.JpaClassUtils.*; import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*; -import java.util.Collections; -import java.util.NoSuchElementException; -import java.util.Set; - import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import jakarta.persistence.metamodel.IdentifiableType; import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; +import java.util.Collections; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.eclipse.persistence.config.QueryHints; import org.eclipse.persistence.jpa.JpaQuery; import org.eclipse.persistence.queries.ScrollableCursor; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.proxy.HibernateProxy; - import org.springframework.data.jpa.repository.query.JpaParameters; import org.springframework.data.jpa.repository.query.JpaParametersParameterAccessor; import org.springframework.data.util.CloseableIterator; @@ -49,8 +49,9 @@ * @author Thomas Darimont * @author Mark Paluch * @author Jens Schauder + * @author Greg Turnquist */ -public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { +public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor, QueryComment { /** * Hibernate persistence provider. @@ -102,9 +103,15 @@ public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { } @Override - public JpaParametersParameterAccessor getParameterAccessor(JpaParameters parameters, Object[] values, EntityManager em) { + public JpaParametersParameterAccessor getParameterAccessor(JpaParameters parameters, Object[] values, + EntityManager em) { return new HibernateJpaParametersParameterAccessor(parameters, values, em); } + + @Override + public String getCommentHintKey() { + return "org.hibernate.comment"; + } }, /** @@ -133,6 +140,16 @@ public Object getIdentifierFrom(Object entity) { public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { return new EclipseLinkScrollableResultsIterator<>(jpaQuery); } + + @Override + public String getCommentHintKey() { + return QueryHints.HINT; + } + + @Override + public String getCommentHintValue(String comment) { + return "/* " + comment + " */"; + } }, /** @@ -161,11 +178,18 @@ public boolean shouldUseAccessorFor(Object entity) { public Object getIdentifierFrom(Object entity) { return null; } + + @Nullable + @Override + public String getCommentHintKey() { + return null; + } }; static ConcurrentReferenceHashMap, PersistenceProvider> CACHE = new ConcurrentReferenceHashMap<>(); private final Iterable entityManagerClassNames; private final Iterable metamodelClassNames; + /** * Creates a new {@link PersistenceProvider}. * @@ -249,7 +273,8 @@ public static PersistenceProvider fromMetamodel(Metamodel metamodel) { return cacheAndReturn(metamodelType, GENERIC_JPA); } - public JpaParametersParameterAccessor getParameterAccessor(JpaParameters parameters, Object[] values, EntityManager em) { + public JpaParametersParameterAccessor getParameterAccessor(JpaParameters parameters, Object[] values, + EntityManager em) { return new JpaParametersParameterAccessor(parameters, values); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryComment.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryComment.java new file mode 100644 index 0000000000..0f50048286 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryComment.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.provider; + +import jakarta.persistence.Query; + +import org.springframework.lang.Nullable; + +/** + * Interface to hide different implementations of query hints that insert comments into a {@link Query}. + * + * @author Greg Turnquist + * @since 3.0 + */ +public interface QueryComment { + + @Nullable + String getCommentHintKey(); + + @Nullable + default String getCommentHintValue(String comment) { + return comment; + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Meta.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Meta.java new file mode 100644 index 0000000000..c5ec418fb2 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Meta.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to assign metadata to repository operations. + * + * @author Greg Turnquist + * @since 3.0 + * @see org.springframework.data.jpa.repository.query.Meta + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +@Documented +public @interface Meta { + + /** + * Add a comment to the query. + * + * @return empty {@link String} by default. + */ + String comment() default ""; + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 7fe6bafdea..3e3dbd7aad 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -187,6 +187,15 @@ protected T applyHints(T query, JpaQueryMethod method) { } } + // Apply any meta-attributes that exist + if (method.hasQueryMetaAttributes()) { + + if (provider.getCommentHintKey() != null) { + query.setHint( // + provider.getCommentHintKey(), provider.getCommentHintValue(method.getQueryMetaAttributes().getComment())); + } + } + return query; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index d510577d34..883e600217 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -32,6 +33,7 @@ import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Meta; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.QueryHints; @@ -46,6 +48,7 @@ import org.springframework.data.util.TypeInformation; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.StringUtils; /** @@ -94,6 +97,7 @@ public class JpaQueryMethod extends QueryMethod { private final Lazy isCollectionQuery; private final Lazy isProcedureQuery; private final Lazy> entityMetadata; + private final Map, Optional> annotationCache; /** * Creates a {@link JpaQueryMethod}. @@ -135,6 +139,7 @@ protected JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionF this.isCollectionQuery = Lazy.of(() -> super.isCollectionQuery() && !NATIVE_ARRAY_TYPES.contains(this.returnType)); this.isProcedureQuery = Lazy.of(() -> AnnotationUtils.findAnnotation(method, Procedure.class) != null); this.entityMetadata = Lazy.of(() -> new DefaultJpaEntityMetadata<>(getDomainClass())); + this.annotationCache = new ConcurrentReferenceHashMap<>(); Assert.isTrue(!(isModifyingQuery() && getParameters().hasSpecialParameter()), String.format("Modifying method must not contain %s", Parameters.TYPES)); @@ -193,6 +198,13 @@ public boolean isModifyingQuery() { return modifying.getNullable() != null; } + @SuppressWarnings("unchecked") + private Optional doFindAnnotation(Class annotationType) { + + return (Optional) this.annotationCache.computeIfAbsent(annotationType, + it -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, it))); + } + /** * Returns all {@link QueryHint}s annotated at this class. Note, that {@link QueryHints} * @@ -259,6 +271,47 @@ Class getReturnType() { return returnType; } + /** + * @return return true if {@link Meta} annotation is available. + * @since 3.0 + */ + public boolean hasQueryMetaAttributes() { + return getMetaAnnotation() != null; + } + + /** + * Returns the {@link Meta} annotation that is applied to the method or {@code null} if not available. + * + * @return + * @since 3.0 + */ + @Nullable + Meta getMetaAnnotation() { + return doFindAnnotation(Meta.class).orElse(null); + } + + /** + * Returns the {@link org.springframework.data.jpa.repository.query.Meta} attributes to be applied. + * + * @return never {@literal null}. + * @since 1.6 + */ + public org.springframework.data.jpa.repository.query.Meta getQueryMetaAttributes() { + + Meta meta = getMetaAnnotation(); + if (meta == null) { + return new org.springframework.data.jpa.repository.query.Meta(); + } + + org.springframework.data.jpa.repository.query.Meta metaAttributes = new org.springframework.data.jpa.repository.query.Meta(); + + if (StringUtils.hasText(meta.comment())) { + metaAttributes.setComment(meta.comment()); + } + + return metaAttributes; + } + /** * Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation found * nor the attribute was specified. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java new file mode 100644 index 0000000000..73eafc218d --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java @@ -0,0 +1,118 @@ +/* + * Copyright 2014-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Value object to hold metadata about repository methods. + * + * @author Greg Turnquist + * @since 3.0 + * @see org.springframework.data.jpa.repository.Meta + */ +public class Meta { + + private enum MetaKey { + COMMENT("comment"); + + private String key; + + MetaKey(String key) { + this.key = key; + } + } + + private Map values = Collections.emptyMap(); + + public Meta() {} + + /** + * Copy a {@link Meta} object. + * + * @since 3.0 + * @param source + */ + Meta(Meta source) { + this.values = new LinkedHashMap<>(source.values); + } + + /** + * Add a comment to the query that is propagated to the profile log. + * + * @param comment + */ + public void setComment(String comment) { + setValue(MetaKey.COMMENT.key, comment); + } + + /** + * @return {@literal null} if not set. + */ + @Nullable + public String getComment() { + return getValue(MetaKey.COMMENT.key); + } + + /** + * @return + */ + public boolean hasValues() { + return !this.values.isEmpty(); + } + + /** + * Get {@link Iterable} of set meta values. + * + * @return + */ + public Iterable> values() { + return Collections.unmodifiableSet(this.values.entrySet()); + } + + /** + * Sets or removes the value in case of {@literal null} or empty {@link String}. + * + * @param key must not be {@literal null} or empty. + * @param value + */ + void setValue(String key, @Nullable Object value) { + + Assert.hasText(key, "Meta key must not be 'null' or blank"); + + if (values == Collections.EMPTY_MAP) { + values = new LinkedHashMap<>(2); + } + + if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) { + this.values.remove(key); + } + this.values.put(key, value); + } + + @Nullable + @SuppressWarnings("unchecked") + private T getValue(String key) { + return (T) this.values.get(key); + } + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java index 4aabf7d24a..e5c2dbf6a7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.repository.support; +import jakarta.persistence.LockModeType; + import java.lang.reflect.Method; import java.util.Optional; -import jakarta.persistence.LockModeType; - import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.lang.Nullable; @@ -32,6 +32,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Jens Schauder + * @author Greg Turnquist */ public interface CrudMethodMetadata { @@ -59,6 +60,14 @@ public interface CrudMethodMetadata { */ QueryHints getQueryHintsForCount(); + /** + * Returns query comment to be applied to query. + * + * @return + * @since 3.0 + */ + String getComment(); + /** * Returns the {@link EntityGraph} to be used. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 5b286814ad..006e883ff9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -15,6 +15,9 @@ */ package org.springframework.data.jpa.repository.support; +import jakarta.persistence.LockModeType; +import jakarta.persistence.QueryHint; + import java.lang.reflect.Method; import java.util.HashSet; import java.util.Optional; @@ -23,9 +26,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Predicate; -import jakarta.persistence.LockModeType; -import jakarta.persistence.QueryHint; - import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.TargetSource; @@ -36,6 +36,7 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Meta; import org.springframework.data.jpa.repository.QueryHints; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor; @@ -180,6 +181,7 @@ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { private final @Nullable LockModeType lockModeType; private final org.springframework.data.jpa.repository.support.QueryHints queryHints; private final org.springframework.data.jpa.repository.support.QueryHints queryHintsForCount; + private final String comment; private final Optional entityGraph; private final Method method; @@ -195,6 +197,7 @@ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { this.lockModeType = findLockModeType(method); this.queryHints = findQueryHints(method, it -> true); this.queryHintsForCount = findQueryHints(method, QueryHints::forCounting); + this.comment = findComment(method); this.entityGraph = findEntityGraph(method); this.method = method; } @@ -233,6 +236,13 @@ private static org.springframework.data.jpa.repository.support.QueryHints findQu return queryHints; } + @Nullable + private static String findComment(Method method) { + + Meta annotation = AnnotatedElementUtils.findMergedAnnotation(method, Meta.class); + return annotation == null ? null : (String) AnnotationUtils.getValue(annotation, "comment"); + } + @Nullable @Override public LockModeType getLockModeType() { @@ -249,6 +259,11 @@ public org.springframework.data.jpa.repository.support.QueryHints getQueryHintsF return queryHintsForCount; } + @Override + public String getComment() { + return comment; + } + @Override public Optional getEntityGraph() { return entityGraph; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index f968c86ff3..0b0125bc54 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -238,6 +238,8 @@ public void deleteAllByIdInBatch(Iterable ids) { query.setParameter("ids", idsCollection); } + applyQueryHints(query); + query.executeUpdate(); } } @@ -279,7 +281,12 @@ public void deleteAll() { @Override @Transactional public void deleteAllInBatch() { - em.createQuery(getDeleteAllQueryString()).executeUpdate(); + + Query query = em.createQuery(getDeleteAllQueryString()); + + applyQueryHints(query); + + query.executeUpdate(); } @Override @@ -296,8 +303,13 @@ public Optional findById(ID id) { LockModeType type = metadata.getLockModeType(); Map hints = new HashMap<>(); + getQueryHints().withFetchGraphs(em).forEach(hints::put); + if (metadata.getComment() != null && provider.getCommentHintKey() != null) { + hints.put(provider.getCommentHintKey(), provider.getCommentHintValue(metadata.getComment())); + } + return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints)); } @@ -355,6 +367,15 @@ public boolean existsById(ID id) { TypedQuery query = em.createQuery(existsQuery, Long.class); + Map hints = new HashMap<>(); + getQueryHints().withFetchGraphs(em).forEach(hints::put); + + if (metadata.getComment() != null && provider.getCommentHintKey() != null) { + hints.put(provider.getCommentHintKey(), provider.getCommentHintValue(metadata.getComment())); + } + + hints.forEach(query::setHint); + if (!entityInformation.hasCompositeId()) { query.setParameter(idAttributeNames.iterator().next(), id); return query.getSingleResult() == 1L; @@ -556,9 +577,7 @@ public R findBy(Specification spec, Function> finder = sort -> { - return getQuery(spec, getDomainClass(), sort); - }; + Function> finder = sort -> getQuery(spec, getDomainClass(), sort); FetchableFluentQuery fluentQuery = new FetchableFluentQueryBySpecification(spec, getDomainClass(), Sort.unsorted(), null, finder, this::count, this::exists, this.em); @@ -568,7 +587,12 @@ public R findBy(Specification spec, Function query = em.createQuery(getCountQueryString(), Long.class); + + applyQueryHintsForCount(query); + + return query.getSingleResult(); } @Override @@ -804,7 +828,12 @@ private TypedQuery applyRepositoryMethodMetadata(TypedQuery query) { } private void applyQueryHints(Query query) { + getQueryHints().withFetchGraphs(em).forEach(query::setHint); + + if (metadata.getComment() != null && provider.getCommentHintKey() != null) { + query.setHint(provider.getCommentHintKey(), provider.getCommentHintValue(metadata.getComment())); + } } private TypedQuery applyRepositoryMethodMetadataForCount(TypedQuery query) { @@ -819,7 +848,12 @@ private TypedQuery applyRepositoryMethodMetadataForCount(TypedQuery qu } private void applyQueryHintsForCount(Query query) { + getQueryHintsForCount().forEach(query::setHint); + + if (metadata.getComment() != null && provider.getCommentHintKey() != null) { + query.setHint(provider.getCommentHintKey(), provider.getCommentHintValue(metadata.getComment())); + } } /** diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index f6964d9b94..10579e2971 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -19,15 +19,15 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import jakarta.persistence.LockModeType; +import jakarta.persistence.QueryHint; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.util.List; import java.util.Optional; -import jakarta.persistence.LockModeType; -import jakarta.persistence.QueryHint; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java new file mode 100644 index 0000000000..0f3a49f65d --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java @@ -0,0 +1,247 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import jakarta.persistence.EntityManagerFactory; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Properties; + +import javax.sql.DataSource; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.ResourceLoader; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.sample.Role; +import org.springframework.data.jpa.repository.Meta; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.sample.RoleRepositoryWithMeta; +import org.springframework.data.repository.query.FluentQuery; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.orm.jpa.JpaDialect; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter; +import org.springframework.orm.jpa.vendor.HibernateJpaDialect; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.FileCopyUtils; +import org.springframework.util.FileSystemUtils; + +/** + * Verify that {@link Meta}-annotated methods properly embed comments into EclipseLink queries. + * + * @author Greg Turnquist + * @since 3.0 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration +@Transactional +public class MetaAnnotatedQueryMethodEclipseLinkIntegrationTests { + + @Autowired RoleRepositoryWithMeta repository; + + private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); + private static final String LOG_FILE = "test-eclipselink-meta.log"; + + @BeforeEach + void cleanoutLogfile() throws IOException { + new FileOutputStream(LOG_FILE).close(); + } + + @AfterAll + static void deleteLogfile() throws IOException { + FileSystemUtils.deleteRecursively(Path.of(LOG_FILE)); + } + + @Test // GH-775 + void findAllShouldLogAComment() { + + repository.findAll(); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findByIdShouldLogAComment() { + + repository.findById(0); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void existsByIdShouldLogAComment() { + + repository.existsById(0); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void customFinderShouldLogAComment() throws Exception { + + repository.findByName("name"); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findOneWithExampleShouldLogAComment() { + + repository.findOne(Example.of(new Role())); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findAllWithExampleShouldLogAComment() { + + repository.findAll(Example.of(new Role())); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findAllWithExampleAndSortShouldLogAComment() { + + repository.findAll(Example.of(new Role()), Sort.by("name")); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findByFluentDslWithExampleShouldLogAComment() { + + repository.findBy(Example.of(new Role()), FluentQuery.FetchableFluentQuery::all); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void existsByExampleShouldLogAComment() { + + repository.exists(Example.of(new Role())); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void countShouldLogAComment() { + + repository.count(); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void customCountShouldLogAComment() { + + repository.countByName("name"); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void deleteAllInBatchShouldLogAComment() { + + repository.deleteAllInBatch(); + + assertAtLeastOneComment(); + } + + void assertAtLeastOneComment() { + + try (Reader reader = new InputStreamReader(RESOURCE_LOADER.getResource("file:" + LOG_FILE).getInputStream(), + StandardCharsets.UTF_8)) { + + String logFileOutput = FileCopyUtils.copyToString(reader); + assertThat(logFileOutput).contains("/* foobar */"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Configuration + @EnableJpaRepositories(basePackages = "org.springframework.data.jpa.repository.sample") + static class Config { + + @Bean + public DataSource dataSource() { + return new EmbeddedDatabaseBuilder().generateUniqueName(true).build(); + } + + @Bean + public Properties jpaProperties() { + + Properties properties = new Properties(); + properties.put("eclipselink.weaving", "false"); + properties.put("eclipselink.logging.level.sql", "FINE"); + properties.put("eclipselink.logging.file", LOG_FILE); + return properties; + } + + @Bean + public AbstractJpaVendorAdapter vendorAdaptor() { + + EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter(); + vendorAdapter.setGenerateDdl(true); + vendorAdapter.setDatabase(Database.HSQL); + return vendorAdapter; + } + + @Bean + public EntityManagerFactory entityManagerFactory() { + + LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); + factory.setDataSource(dataSource()); + factory.setPersistenceUnitName("spring-data-jpa"); + factory.setJpaVendorAdapter(vendorAdaptor()); + factory.setJpaProperties(jpaProperties()); + factory.afterPropertiesSet(); + return factory.getObject(); + } + + @Bean + public JpaDialect jpaDialect() { + return new HibernateJpaDialect(); + } + + @Bean + public PlatformTransactionManager transactionManager() { + return new JpaTransactionManager(entityManagerFactory()); + } + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java new file mode 100644 index 0000000000..cbde086ad1 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java @@ -0,0 +1,254 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import jakarta.persistence.EntityManagerFactory; + +import java.util.List; +import java.util.Properties; +import java.util.function.Predicate; + +import javax.sql.DataSource; + +import org.assertj.core.api.Condition; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.sample.Role; +import org.springframework.data.jpa.repository.Meta; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.sample.RoleRepositoryWithMeta; +import org.springframework.data.repository.query.FluentQuery; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.orm.jpa.JpaDialect; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.HibernateJpaDialect; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Transactional; + +/** + * Verify that {@link Meta}-annotated methods properly embed comments into Hibernate queries. + * + * @author Greg Turnquist + * @since 3.0 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration +@Transactional +public class MetaAnnotatedQueryMethodHibernateIntegrationTests { + + @Autowired RoleRepositoryWithMeta repository; + + Logger testLogger = (Logger) LoggerFactory.getLogger("org.hibernate.SQL"); + ListAppender testAppender; + + @BeforeEach + void setUp() { + + testAppender = new ListAppender<>(); + testAppender.start(); + testLogger.setLevel(Level.DEBUG); + testLogger.addAppender(testAppender); + } + + @AfterEach + void clearUp() { + testLogger.detachAppender(testAppender); + } + + @Test // GH-775 + void findAllShouldLogAComment() { + + repository.findAll(); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findByIdShouldNotLogAComment() { + + repository.findById(0); + + assertNoComments(); + } + + @Test // GH-775 + void existsByIdShouldLogAComment() { + + repository.existsById(0); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void customFinderShouldLogAComment() { + + repository.findByName("name"); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findOneWithExampleShouldLogAComment() { + + repository.findOne(Example.of(new Role())); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findAllWithExampleShouldLogAComment() { + + repository.findAll(Example.of(new Role())); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findAllWithExampleAndSortShouldLogAComment() { + + repository.findAll(Example.of(new Role()), Sort.by("name")); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void findByFluentDslWithExampleShouldLogAComment() { + + repository.findBy(Example.of(new Role()), FluentQuery.FetchableFluentQuery::all); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void existsByExampleShouldLogAComment() { + + repository.exists(Example.of(new Role())); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void countShouldLogAComment() { + + repository.count(); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void customCountShouldLogAComment() { + + repository.countByName("name"); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void deleteAllByIdInBatchShouldLogAComment() { + + repository.deleteAllByIdInBatch(List.of(0, 1, 2)); + + assertAtLeastOneComment(); + } + + @Test // GH-775 + void deleteAllInBatchShouldLogAComment() { + + repository.deleteAllInBatch(); + + assertAtLeastOneComment(); + } + + private final static Predicate hasComment = s -> s.startsWith("/* foobar */"); + + private void assertAtLeastOneComment() { + assertThat(testAppender.list).extracting(ILoggingEvent::getFormattedMessage) + .haveAtLeastOne(new Condition(hasComment, "SQL contains a comment")); + } + + private void assertNoComments() { + assertThat(testAppender.list).extracting(ILoggingEvent::getFormattedMessage).noneMatch(hasComment); + } + + @Configuration + @EnableJpaRepositories(basePackages = "org.springframework.data.jpa.repository.sample") + static class Config { + + @Bean + public DataSource dataSource() { + return new EmbeddedDatabaseBuilder().generateUniqueName(true).build(); + } + + @Bean + public Properties jpaProperties() { + + Properties properties = new Properties(); + properties.setProperty("hibernate.use_sql_comments", "true"); + return properties; + } + + @Bean + public AbstractJpaVendorAdapter vendorAdaptor() { + + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + vendorAdapter.setGenerateDdl(true); + vendorAdapter.setDatabase(Database.HSQL); + return vendorAdapter; + } + + @Bean + public EntityManagerFactory entityManagerFactory() { + + LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); + factory.setDataSource(dataSource()); + factory.setPersistenceUnitName("spring-data-jpa"); + factory.setJpaVendorAdapter(vendorAdaptor()); + factory.setJpaProperties(jpaProperties()); + factory.afterPropertiesSet(); + return factory.getObject(); + } + + @Bean + public JpaDialect jpaDialect() { + return new HibernateJpaDialect(); + } + + @Bean + public PlatformTransactionManager transactionManager() { + return new JpaTransactionManager(entityManagerFactory()); + } + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java new file mode 100644 index 0000000000..b2ecca217d --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import java.lang.reflect.Method; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.QueryExtractor; +import org.springframework.data.jpa.repository.Meta; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; + +/** + * Verify that {@link Meta}-annotated methods property capture comments. + * + * @author Greg Turnquist + * @since 3.0 + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class MetaAnnotatedQueryMethodUnitTests { + + @Mock QueryExtractor extractor; + + private ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); + + @Test // GH-775 + void metaAnnotationCommentsAreCapturedInJpaQueryMethod() throws Exception { + + Method method = UserRepository.class.getMethod("metaMethod"); + DefaultRepositoryMetadata repositoryMetadata = new DefaultRepositoryMetadata(UserRepository.class); + JpaQueryMethod jpaQueryMethod = new JpaQueryMethod(method, repositoryMetadata, factory, extractor); + + assertThat(jpaQueryMethod.getQueryMetaAttributes().getComment()).isEqualTo("Comments embedded in SQL"); + } + + interface UserRepository extends CrudRepository { + + @Meta(comment = "Comments embedded in SQL") + void metaMethod(); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepositoryWithMeta.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepositoryWithMeta.java new file mode 100644 index 0000000000..79b4dbe13a --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepositoryWithMeta.java @@ -0,0 +1,101 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.sample; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +import org.springframework.data.domain.Example; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.sample.Role; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Meta; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.query.FluentQuery; + +import com.querydsl.core.types.Predicate; + +/** + * Typed repository for {@link Role} but with {@link Meta} annotations applied. + * + * @author Greg Turnquist + * @since 3.0 + */ +public interface RoleRepositoryWithMeta extends JpaRepository, QuerydslPredicateExecutor { + + // Finders + + @Override + @Meta(comment = "foobar") + List findAll(); + + @Override + @Meta(comment = "foobar") + Optional findById(Integer id); + + @Override + @Meta(comment = "foobar") + Optional findOne(Predicate predicate); + + @Meta(comment = "foobar") + List findByName(String name); + + @Override + @Meta(comment = "foobar") + Optional findOne(Example example); + + @Override + @Meta(comment = "foobar") + List findAll(Example example); + + @Override + @Meta(comment = "foobar") + List findAll(Example example, Sort sort); + + @Override + @Meta(comment = "foobar") + R findBy(Example example, Function, R> queryFunction); + + // counters + + @Override + @Meta(comment = "foobar") + long count(); + + @Meta(comment = "foobar") + long countByName(String name); + + // exists + + @Override + @Meta(comment = "foobar") + boolean existsById(Integer integer); + + @Override + @Meta(comment = "foobar") + boolean exists(Example example); + + // delete + + @Override + @Meta(comment = "foobar") + void deleteAllInBatch(); + + @Override + @Meta(comment = "foobar") + void deleteAllByIdInBatch(Iterable integers); +} diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 125dd50945..3688ca1a50 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -726,6 +726,139 @@ public interface UserRepository extends Repository { ==== The preceding declaration would apply the configured `@QueryHint` for that actually query but omit applying it to the count query triggered to calculate the total number of pages. +[[jpa.query-hints.comments]] +==== Adding Comments to Queries +Sometimes, you need to debug a query based upon database performance. +The query your database administrator shows you may look VERY different than what you wrote using `@Query`, or it may look +nothing like what you presume Spring Data JPA has generated regarding a custom finder or if you used query by example. + +To make this process easier, you can insert custom comments into almost any JPA operation, whether its a query or other operation +by applying the `@Meta` annotation. + +.Apply `@Meta` annotation to repository operations +==== +[source, java] +---- +public interface RoleRepository extends JpaRepository { + + @Meta(comment = "find roles by name") + List findByName(String name); + + @Override + @Meta(comment = "find roles using QBE") + List findAll(Example example); + + @Meta(comment = "count roles for a given name") + long countByName(String name); + + @Override + @Meta(comment = "exists based on QBE") + boolean exists(Example example); +} +---- +==== + +This sample repository has a mixture of custom finders as well as overriding the inherited operations from `JpaRepository`. +Either way, the `@Meta` annotation lets you add a `comment` that will be inserted into queries before they are sent to the database. + +It's also important to note that this feature isn't confined solely to queries. It extends to the `count` and `exists` operations. +And while not shown, it also extends to certain `delete` operations. + +IMPORTANT: While we have attempted to apply this feature everywhere possible, some operations of the underlying `EntityManager` don't support comments. For example, `entityManager.createQuery()` is clearly documented as supporting comments, but `entityManager.find()` operations do not. + +Neither JPQL logging nor SQL logging is a standard in JPA, so each provider requires custom configuration, as shown the sections below. + +===== Activating Hibernate comments +To activate query comments in Hibernate, you must set `hibernate.use_sql_comments` to `true`. + +If you are using Java-based configuration settings, this can be done like this: + +.Java-based JPA configuration +==== +[source, java] +---- +@Bean +public Properties jpaProperties() { + + Properties properties = new Properties(); + properties.setProperty("hibernate.use_sql_comments", "true"); + return properties; +} +---- +==== + +If you have a `persistence.xml` file, you can apply it there: + +.`persistence.xml`-based configuration +==== +[source, xml] +---- + + + ...registered classes... + + + + + +---- +==== + +Finally, if you are using Spring Boot, then you can set it up inside your `application.properties` file: + +.Spring Boot property-based configuration +==== +---- +spring.jpa.properties.hibernate.use_sql_comments=true +---- +==== + +===== Activating EclipseLink comments +To activate query comments in EclipseLink, you must set `eclipselink.logging.level.sql` to `FINE`. + +If you are using Java-based configuration settings, this can be done like this: + +.Java-based JPA configuration +==== +[source, java] +---- +@Bean +public Properties jpaProperties() { + + Properties properties = new Properties(); + properties.setProperty("eclipselink.logging.level.sql", "FINE"); + return properties; +} +---- +==== + +If you have a `persistence.xml` file, you can apply it there: + +.`persistence.xml`-based configuration +==== +[source, xml] +---- + + + ...registered classes... + + + + + +---- +==== + +Finally, if you are using Spring Boot, then you can set it up inside your `application.properties` file: + +.Spring Boot property-based configuration +==== +---- +spring.jpa.properties.eclipselink.logging.level.sql=FINE +---- +==== + + [[jpa.entity-graph]] === Configuring Fetch- and LoadGraphs From 9bce5dd9a439aa30f5d0fb4fcdcda2a92368fc7b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 11 Jul 2022 16:37:18 -0500 Subject: [PATCH 226/821] Polishing. --- src/main/asciidoc/index.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index ce205ea720..08d1e609da 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -1,5 +1,5 @@ = Spring Data JPA - Reference Documentation -Oliver Gierke; Thomas Darimont; Christoph Strobl; Mark Paluch; Jay Bryant +Oliver Gierke; Thomas Darimont; Christoph Strobl; Mark Paluch; Jay Bryant; Greg Turnquist :revnumber: {version} :revdate: {localdate} ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]] From b3a18c020fa52e4491f9b8926ed9061834a1bc7b Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 19 May 2022 16:19:14 +0200 Subject: [PATCH 227/821] Properly set up source result types from the start during query creation. For count queries always start with Long instead of the domain type (Hibernate 6 rejects a mismatch between the type the query was originally set up for and the type to be actually selected). For delete queries, do not use the type to be read, but the domain type as we load the individual instances first to make sure we properly fire entity callbacks. Related ticket: #2423. --- .../data/jpa/repository/query/JpaCountQueryCreator.java | 5 ++++- .../data/jpa/repository/query/JpaQueryCreator.java | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index 6b6c2a6602..29e43080f0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -48,13 +48,16 @@ public class JpaCountQueryCreator extends JpaQueryCreator { */ public JpaCountQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder, ParameterMetadataProvider provider) { + super(tree, type, builder, provider); + this.distinct = tree.isDistinct(); } @Override protected CriteriaQuery createCriteriaQuery(CriteriaBuilder builder, ReturnedType type) { - return builder.createQuery(type.getDomainType()); + + return builder.createQuery(Long.class); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 836cca29fe..16074ed2cb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -100,9 +100,10 @@ public JpaQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder */ protected CriteriaQuery createCriteriaQuery(CriteriaBuilder builder, ReturnedType type) { - Class typeToRead = type.getTypeToRead(); + Class typeToRead = tree.isDelete() ? type.getDomainType() : type.getTypeToRead(); - return typeToRead == null || tree.isExistsProjection() ? builder.createTupleQuery() + return (typeToRead == null) || tree.isExistsProjection() // + ? builder.createTupleQuery() // : builder.createQuery(typeToRead); } From 453f879af32c727c764159f72d03b746c8a58627 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Fri, 28 Jan 2022 14:34:22 +0100 Subject: [PATCH 228/821] Upgrade to Hibernate and Hibernate Envers 6. TypedQuery inspection through its String representations seems to be a bit flaky in Hibernate 6 still [0]. Tweaked the code to extract a query string from a query object to try the new way first but fall back to the old way, as this seems to work under some conditions, too. Adapted the test case in which we could rather inspect the new SqmQuery API for test result verification. Re-bootstrapping the EntityManagerFactory for the same persistence unit causes the second bootstrap to fail als apparently foreign key names are randomized and the second bootstrap doesn't create a new constraint but tries to work with a new name. Tweaked the offending test case to reuse the existing EMF declaration as it actually only tests the qualified wiring into clients. Applying an entity graph is causing a StackOverflow in current Hibernate 6. Filed an issue [1] and disabled the test case for now. Dial back on the flip to use String as parameter type for like expression escape characters as Eclipselink rejects that. The JPA spec chapter 4.2.10 allows both Character and String to be used. Filed [2] with Hibernate to ask for reintroduction of the support for characters and commented out the test cases for now. Deprecated CustomHsqlHibernateJpaVendorAdapter as it's not needed on Hibernate 6 anymore. Rewrote HibernateJpaParametersParameterAccessor to use Hibernate 6 API. Add Hibernate 6 upgrade information to the reference docs. Related ticket: #2423. [0] https://hibernate.atlassian.net/browse/HHH-15389 [1] https://hibernate.atlassian.net/browse/HHH-15391 [2] https://hibernate.atlassian.net/browse/HHH-15392 --- pom.xml | 4 +- spring-data-envers/pom.xml | 4 +- spring-data-jpa/pom.xml | 15 ++++-- ...bernateJpaParametersParameterAccessor.java | 29 ++++++---- .../data/jpa/provider/HibernateUtils.java | 15 +++++- .../jpa/provider/PersistenceProvider.java | 16 +++--- .../CustomHsqlHibernateJpaVendorAdaptor.java | 24 ++++----- ...raphRepositoryMethodsIntegrationTests.java | 7 +-- .../repository/UserRepositoryFinderTests.java | 18 +++++++ .../jpa/repository/UserRepositoryTests.java | 2 +- .../JpaCountQueryCreatorIntegrationTests.java | 27 +++++++--- .../JpaParametersParameterAccessorTests.java | 16 +++--- .../ParameterExpressionProviderTests.java | 7 ++- .../PartTreeJpaQueryIntegrationTests.java | 43 ++++++++------- .../jpa/repository/sample/UserRepository.java | 4 +- ...egistrarPostProcessorIntegrationTests.java | 53 ++++--------------- .../src/test/resources/hibernate.xml | 2 +- src/main/asciidoc/new-features.adoc | 24 +++++++++ 18 files changed, 183 insertions(+), 127 deletions(-) diff --git a/pom.xml b/pom.xml index 6a62754327..8a1565932b 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 3.0.2 - 5.6.9.Final + 6.1.1.Final 4.3 8.0.23 42.2.19 @@ -44,7 +44,7 @@ - + spring-data-envers spring-data-jpa spring-data-jpa-distribution diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 59eb8eab88..540834bb60 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -62,8 +62,8 @@ - org.hibernate - hibernate-envers-jakarta + org.hibernate.orm + hibernate-envers ${hibernate.envers} diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index ba9220cc2a..3ad9c39a2a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -145,19 +145,26 @@ - ${hibernate.groupId} - hibernate-core-jakarta + ${hibernate.groupId}.orm + hibernate-core ${hibernate} true - ${hibernate.groupId} - hibernate-jpamodelgen-jakarta + ${hibernate.groupId}.orm + hibernate-jpamodelgen ${hibernate} provided + + jakarta.xml.bind + jakarta.xml.bind-api + ${jaxb} + provided + + jakarta.annotation jakarta.annotation-api diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index 918ec58af8..38eba8d73d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -16,14 +16,15 @@ package org.springframework.data.jpa.provider; import jakarta.persistence.EntityManager; -import org.hibernate.SessionFactory; -import org.hibernate.TypeHelper; -import org.hibernate.jpa.TypedParameterValue; -import org.hibernate.type.Type; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.TypedParameterValue; +import org.hibernate.type.BasicTypeRegistry; import org.springframework.data.jpa.repository.query.JpaParametersParameterAccessor; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.lang.Nullable; /** * {@link org.springframework.data.repository.query.ParameterAccessor} based on an {@link Parameters} instance. In @@ -33,11 +34,13 @@ * @author Wonchul Heo * @author Jens Schauder * @author Cedomir Igaly + * @author Robert Wilson + * @author Oliver Drotbohm * @since 2.7 */ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAccessor { - private final TypeHelper typeHelper; + private final BasicTypeRegistry typeHelper; /** * Creates a new {@link ParametersParameterAccessor}. @@ -50,21 +53,29 @@ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAcce super(parameters, values); - this.typeHelper = em.getEntityManagerFactory().unwrap(SessionFactory.class).getTypeHelper(); + this.typeHelper = em.getEntityManagerFactory() + .unwrap(SessionFactoryImplementor.class) + .getTypeConfiguration() + .getBasicTypeRegistry(); } @Override + @Nullable + @SuppressWarnings("unchecked") public Object getValue(Parameter parameter) { - Object value = super.getValue(parameter.getIndex()); + var value = super.getValue(parameter.getIndex()); + if (value != null) { return value; } - Type type = typeHelper.basic(parameter.getType()); + var type = typeHelper.getRegisteredType(parameter.getType()); + if (type == null) { return null; } - return new TypedParameterValue(type, null); + + return new TypedParameterValue<>(type, null); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java index 3c6a3556fe..2056db61d8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.provider; import org.hibernate.query.Query; +import org.hibernate.query.spi.SqmQuery; import org.springframework.lang.Nullable; /** @@ -41,8 +42,20 @@ private HibernateUtils() {} @Nullable public static String getHibernateQuery(Object query) { + try { + + // Try the new Hibernate implementation first + if (query instanceof SqmQuery) { + return ((SqmQuery) query).getSqmStatement().toHqlString(); + } + + // Couple of cases in which this still breaks, see HHH-15389 + } catch (RuntimeException o_O) {} + + // Try the old way, as it still works in some cases (haven't investigated in which exactly) + if (query instanceof Query) { - return ((Query) query).getQueryString(); + return ((Query) query).getQueryString(); } else { throw new IllegalArgumentException("Don't know how to extract the query string from " + query); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index f2d90b0c87..f3ef2f0473 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -24,7 +24,9 @@ import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; +import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.NoSuchElementException; import java.util.Set; @@ -186,6 +188,8 @@ public String getCommentHintKey() { } }; + private static final Collection ALL = List.of(HIBERNATE, ECLIPSELINK, GENERIC_JPA); + static ConcurrentReferenceHashMap, PersistenceProvider> CACHE = new ConcurrentReferenceHashMap<>(); private final Iterable entityManagerClassNames; private final Iterable metamodelClassNames; @@ -233,7 +237,7 @@ public static PersistenceProvider fromEntityManager(EntityManager em) { return cachedProvider; } - for (PersistenceProvider provider : values()) { + for (PersistenceProvider provider : ALL) { for (String entityManagerClassName : provider.entityManagerClassNames) { if (isEntityManagerOfType(em, entityManagerClassName)) { return cacheAndReturn(entityManagerType, provider); @@ -317,9 +321,9 @@ interface Constants { String GENERIC_JPA_ENTITY_MANAGER_INTERFACE = "jakarta.persistence.EntityManager"; String ECLIPSELINK_ENTITY_MANAGER_INTERFACE = "org.eclipse.persistence.jpa.JpaEntityManager"; // needed as Spring only exposes that interface via the EM proxy - String HIBERNATE_ENTITY_MANAGER_INTERFACE = "org.hibernate.jpa.HibernateEntityManager"; + String HIBERNATE_ENTITY_MANAGER_INTERFACE = "org.hibernate.engine.spi.SessionImplementor"; - String HIBERNATE_JPA_METAMODEL_TYPE = "org.hibernate.metamodel.internal.MetamodelImpl"; + String HIBERNATE_JPA_METAMODEL_TYPE = "org.hibernate.metamodel.model.domain.JpaMetamodel"; String ECLIPSELINK_JPA_METAMODEL_TYPE = "org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl"; } @@ -337,7 +341,7 @@ public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { */ private static class HibernateScrollableResultsIterator implements CloseableIterator { - private final @Nullable ScrollableResults scrollableResults; + private final @Nullable ScrollableResults scrollableResults; /** * Creates a new {@link HibernateScrollableResultsIterator} for the given {@link Query}. @@ -346,7 +350,7 @@ private static class HibernateScrollableResultsIterator implements CloseableIter */ HibernateScrollableResultsIterator(Query jpaQuery) { - org.hibernate.query.Query query = jpaQuery.unwrap(org.hibernate.query.Query.class); + org.hibernate.query.Query query = jpaQuery.unwrap(org.hibernate.query.Query.class); this.scrollableResults = query.setReadOnly(TransactionSynchronizationManager.isCurrentTransactionReadOnly())// .scroll(ScrollMode.FORWARD_ONLY); } @@ -359,7 +363,7 @@ public Object next() { } // Cast needed for Hibernate 6 compatibility - Object[] row = (Object[]) scrollableResults.get(); + Object[] row = scrollableResults.get(); return row.length == 1 ? row[0] : row; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java index b4dda054b0..8201238284 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java @@ -15,8 +15,6 @@ */ package org.springframework.data.jpa.repository; -import java.sql.Types; - import org.hibernate.dialect.HSQLDialect; import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; @@ -24,26 +22,22 @@ /** * Fix for missing type declarations for HSQL. * - * @see https://www.codesmell.org/blog/2008/12/hibernate-hsql-native-queries-and-booleans/ + * @see https://www.codesmell.org/blog/2008/12/hibernate-hsql-native-queries-and-booleans/ * @author Oliver Gierke + * @deprecated since 3.0 without replacement as it's not needed anymore. */ +@Deprecated public class CustomHsqlHibernateJpaVendorAdaptor extends HibernateJpaVendorAdapter { @Override protected Class determineDatabaseDialectClass(Database database) { - - if (Database.HSQL.equals(database)) { - return CustomHsqlDialect.class; - } - return super.determineDatabaseDialectClass(database); } - public static class CustomHsqlDialect extends HSQLDialect { - - public CustomHsqlDialect() { - registerColumnType(Types.BOOLEAN, "boolean"); - registerHibernateType(Types.BOOLEAN, "boolean"); - } - } + /** + * @deprecated since 3.0 without replacement as it's not needed anymore. + */ + @Deprecated + public static class CustomHsqlDialect extends HSQLDialect {} } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index 67368194f8..224a3e2517 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -18,8 +18,6 @@ import static org.assertj.core.api.Assertions.*; import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; -import java.util.List; - import jakarta.persistence.EntityManager; import jakarta.persistence.Persistence; import jakarta.persistence.PersistenceUtil; @@ -28,12 +26,14 @@ import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import java.util.List; + import org.assertj.core.api.SoftAssertions; import org.junit.Assume; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -94,6 +94,7 @@ void setup() { } @Test // DATAJPA-612 + @Disabled // HHH-15391 void shouldRespectConfiguredJpaEntityGraph() { Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 6d89b1def3..3241a237ff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -16,8 +16,11 @@ package org.springframework.data.jpa.repository; import static org.assertj.core.api.Assertions.*; +import static org.junit.Assume.*; import static org.springframework.data.domain.Sort.Direction.*; +import jakarta.persistence.EntityManager; + import java.util.Arrays; import java.util.List; @@ -34,6 +37,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.sample.RoleRepository; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.repository.query.QueryLookupStrategy; @@ -55,6 +59,9 @@ public class UserRepositoryFinderTests { @Autowired UserRepository userRepository; @Autowired RoleRepository roleRepository; + @Autowired EntityManager em; + + PersistenceProvider provider; private User dave; private User carter; @@ -73,6 +80,8 @@ void setUp() { dave = userRepository.save(new User("Dave", "Matthews", "dave@dmband.com", singer)); carter = userRepository.save(new User("Carter", "Beauford", "carter@dmband.com", singer, drummer)); oliver = userRepository.save(new User("Oliver August", "Matthews", "oliver@dmband.com")); + + provider = PersistenceProvider.fromEntityManager(em); } @AfterEach @@ -226,6 +235,9 @@ void parametersForContainsGetProperlyEscaped() { @Test // DATAJPA-1519 void escapingInLikeSpels() { + // HHH-15392 + assumeFalse(provider.equals(PersistenceProvider.HIBERNATE)); + User extra = new User("extra", "Matt_ew", "extra"); userRepository.save(extra); @@ -236,6 +248,9 @@ void escapingInLikeSpels() { @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { + // HHH-15392 + assumeFalse(provider.equals(PersistenceProvider.HIBERNATE)); + User withEscapeCharacter = userRepository.save(new User("extra", "Matt\\xew", "extra1")); userRepository.save(new User("extra", "Matt\\_ew", "extra2")); @@ -245,6 +260,9 @@ void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapedWildcards() { + // HHH-15392 + assumeFalse(provider.equals(PersistenceProvider.HIBERNATE)); + userRepository.save(new User("extra", "Matt\\xew", "extra1")); User withEscapedWildcard = userRepository.save(new User("extra", "Matt\\_ew", "extra2")); 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 25dcfcdf6e..877c8c9053 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 @@ -2670,7 +2670,7 @@ void handlesCountQueriesWithLessParametersMoreThanOne() { @Test // DATAJPA-1233 void handlesCountQueriesWithLessParametersMoreThanOneIndexed() { - repository.findAllOrderedBySpecialNameMultipleParamsIndexed("Oliver", "x", PageRequest.of(2, 3)); + repository.findAllOrderedBySpecialNameMultipleParamsIndexed("x", "Oliver", PageRequest.of(2, 3)); } // DATAJPA-928 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java index f42756621c..ef48b68fc0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java @@ -17,19 +17,22 @@ import static org.assertj.core.api.Assertions.*; -import java.lang.reflect.Method; -import java.util.List; - import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.TypedQuery; +import java.lang.reflect.Method; +import java.util.List; + +import org.hibernate.query.spi.SqmQuery; +import org.hibernate.query.sqm.tree.expression.SqmDistinct; +import org.hibernate.query.sqm.tree.expression.SqmFunction; +import org.hibernate.query.sqm.tree.select.SqmSelectClause; +import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.User; -import org.springframework.data.jpa.provider.HibernateUtils; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; @@ -68,10 +71,20 @@ void distinctFlagOnCountQueryIssuesCountDistinct() throws Exception { TypedQuery query = entityManager.createQuery(creator.createQuery()); - assertThat(HibernateUtils.getHibernateQuery(query)).startsWith("select distinct count(distinct"); + SqmQuery sqmQuery = ((SqmQuery) query); + SqmSelectStatement select = (SqmSelectStatement) sqmQuery.getSqmStatement(); + + // Verify distinct (should this even be there for a count query?) + SqmSelectClause clause = select.getQuerySpec().getSelectClause(); + assertThat(clause.isDistinct()).isTrue(); + + // Verify count(distinct(…)) + SqmFunction function = ((SqmFunction) clause.getSelectionItems().get(0)); + assertThat(function.getFunctionName()).isEqualTo("count"); + assertThat(function.getArguments().get(0)).isInstanceOf(SqmDistinct.class); } interface SomeRepository extends Repository { - void findDistinctByRolesIn(List roles); + List findDistinctByRolesIn(List roles); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java index 059fc78ecc..4577e81453 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java @@ -4,14 +4,13 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.lang.reflect.Method; - import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.Query; -import org.hibernate.jpa.TypedParameterValue; -import org.hibernate.type.StandardBasicTypes; +import java.lang.reflect.Method; + +import org.hibernate.query.TypedParameterValue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -44,7 +43,8 @@ void createsJpaParametersParameterAccessor() throws Exception { Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); Object[] values = { null }; JpaParameters parameters = new JpaParameters(withNativeQuery); - JpaParametersParameterAccessor accessor = PersistenceProvider.GENERIC_JPA.getParameterAccessor(parameters, values, em); + JpaParametersParameterAccessor accessor = PersistenceProvider.GENERIC_JPA.getParameterAccessor(parameters, values, + em); bind(parameters, accessor); @@ -62,10 +62,10 @@ void createsHibernateParametersParameterAccessor() throws Exception { bind(parameters, accessor); - ArgumentCaptor captor = ArgumentCaptor.forClass(TypedParameterValue.class); + ArgumentCaptor> captor = ArgumentCaptor.forClass(TypedParameterValue.class); verify(query).setParameter(eq(1), captor.capture()); - TypedParameterValue captorValue = captor.getValue(); - assertThat(captorValue.getType()).isEqualTo(StandardBasicTypes.INTEGER); + TypedParameterValue captorValue = captor.getValue(); + assertThat(captorValue.getType().getBindableJavaType()).isEqualTo(Integer.class); assertThat(captorValue.getValue()).isNull(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java index 04b9e53b53..c8a1ddeada 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java @@ -17,16 +17,15 @@ import static org.assertj.core.api.Assertions.*; -import java.lang.reflect.Method; - import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.ParameterExpression; +import java.lang.reflect.Method; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.repository.query.DefaultParameters; import org.springframework.data.repository.query.Parameters; @@ -60,7 +59,7 @@ void createsParameterExpressionWithMostConcreteType() throws Exception { ParameterMetadataProvider provider = new ParameterMetadataProvider(builder, accessor, EscapeCharacter.DEFAULT); ParameterExpression expression = provider.next(part, Comparable.class).getExpression(); - assertThat(expression.getParameterType()).isEqualTo(int.class); + assertThat(expression.getParameterType()).isEqualTo(Integer.class); } interface SampleRepository { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index e0177b2133..980cef7b4e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -20,6 +20,11 @@ import static org.assertj.core.api.Assertions.*; import static org.springframework.test.util.ReflectionTestUtils.*; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import jakarta.persistence.TemporalType; + import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; @@ -27,14 +32,9 @@ import java.util.Iterator; import java.util.List; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Query; -import jakarta.persistence.TemporalType; - import org.hibernate.Version; -import org.hibernate.query.internal.QueryImpl; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.data.domain.Page; @@ -65,6 +65,7 @@ public class PartTreeJpaQueryIntegrationTests { private static String PROPERTY = "h.target." + getQueryProperty(); + private static Class HIBERNATE_NATIVE_QUERY = org.hibernate.query.Query.class; @PersistenceContext EntityManager entityManager; @@ -82,7 +83,7 @@ void test() throws Exception { PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager); jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) })); - jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) }))); + jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) })); } @Test @@ -100,18 +101,19 @@ void cannotIgnoreCaseIfNotStringUnlessIgnoringAll() throws Exception { } @Test // DATAJPA-121 + @Disabled // HHH-15389 void recreatesQueryIfNullValueIsGiven() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager); - Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) }))); + Query query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) })); - assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).endsWith("firstname=:param0"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).endsWith("firstname=:param0"); - query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { null, PageRequest.of(0, 1) }))); + query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { null, PageRequest.of(0, 1) })); - assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).endsWith("firstname is null"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).endsWith("firstname is null"); } @Test // DATAJPA-920 @@ -120,42 +122,45 @@ void shouldLimitExistsProjectionQueries() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("existsByFirstname", String.class); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager); - Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews" }))); + Query query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { "Matthews" })); assertThat(query.getMaxResults()).isEqualTo(1); } @Test // DATAJPA-920 + @Disabled // HHH-15389 void shouldSelectAliasedIdForExistsProjectionQueries() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("existsByFirstname", String.class); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager); - Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] { "Matthews" }))); + Query query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { "Matthews" })); - assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).contains(".id from User as"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).contains(".id from User as"); } @Test // DATAJPA-1074 + @Disabled // HHH-15389 void isEmptyCollection() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("findByRolesIsEmpty"); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager); - Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] {}))); + Query query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] {})); - assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).endsWith("roles is empty"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).endsWith("roles is empty"); } @Test // DATAJPA-1074 + @Disabled // HHH-15389 void isNotEmptyCollection() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("findByRolesIsNotEmpty"); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager); - Query query = jpaQuery.createQuery((getAccessor(queryMethod, new Object[] {}))); + Query query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] {})); - assertThat(HibernateUtils.getHibernateQuery(query.unwrap(QueryImpl.class))).endsWith("roles is not empty"); + assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).endsWith("roles is not empty"); } @Test // DATAJPA-1074 @@ -236,7 +241,7 @@ private void testIgnoreCase(String methodName, Object... values) throws Exceptio JpaQueryMethod queryMethod = getQueryMethod(methodName, parameterTypes); PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager); - jpaQuery.createQuery((getAccessor(queryMethod, values))); + jpaQuery.createQuery(getAccessor(queryMethod, values)); } private JpaQueryMethod getQueryMethod(String methodName, Class... parameterTypes) throws Exception { 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 f9fd6b9865..51659d9685 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 @@ -582,8 +582,8 @@ Page findAllOrderedBySpecialNameMultipleParams(@Param("name") String name, // DATAJPA-1233 @Query( - value = "SELECT u FROM User u WHERE ?2 = 'x' ORDER BY CASE WHEN (u.firstname >= ?1) THEN 0 ELSE 1 END, u.firstname") - Page findAllOrderedBySpecialNameMultipleParamsIndexed(String name, String other, Pageable page); + value = "SELECT u FROM User u WHERE ?1 = 'x' ORDER BY CASE WHEN (u.firstname >= ?2) THEN 0 ELSE 1 END, u.firstname") + Page findAllOrderedBySpecialNameMultipleParamsIndexed(String other, String name, Pageable page); // DATAJPA-928 Page findByNativeNamedQueryWithPageable(Pageable pageable); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java index 5ac7181898..5776d82b09 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java @@ -17,27 +17,18 @@ import static org.assertj.core.api.Assertions.*; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - import jakarta.persistence.EntityManager; -import javax.sql.DataSource; +import jakarta.persistence.EntityManagerFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.Primary; -import org.springframework.orm.jpa.JpaVendorAdapter; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.stereotype.Component; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -62,59 +53,35 @@ void injectsEntityManagerIntoConstructors() { assertThat(target.primaryEm).isNotNull(); } - /** - * Annotation to demarcate test components. - * - * @author Oliver Gierke - */ - @Component - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - private static @interface TestComponent { - - } - @Configuration + @Import(EntityManagerInjectionTarget.class) @ImportResource("classpath:infrastructure.xml") - @ComponentScan(includeFilters = @Filter(TestComponent.class), useDefaultFilters = false) static class Config { - @Autowired DataSource dataSource; - @Autowired JpaVendorAdapter vendorAdapter; + @Autowired @Qualifier("entityManagerFactory") EntityManagerFactory emf; @Bean public static EntityManagerBeanDefinitionRegistrarPostProcessor processor() { return new EntityManagerBeanDefinitionRegistrarPostProcessor(); } - private LocalContainerEntityManagerFactoryBean emf() { - - LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); - factoryBean.setPersistenceUnitName("spring-data-jpa"); - factoryBean.setDataSource(dataSource); - factoryBean.setJpaVendorAdapter(vendorAdapter); - - return factoryBean; - } - @Bean - LocalContainerEntityManagerFactoryBean firstEmf() { - return emf(); + EntityManagerFactory firstEmf() { + return emf; } @Bean - LocalContainerEntityManagerFactoryBean secondEmf() { - return emf(); + EntityManagerFactory secondEmf() { + return emf; } @Primary @Bean - LocalContainerEntityManagerFactoryBean thirdEmf() { - return emf(); + EntityManagerFactory thirdEmf() { + return emf; } } - @TestComponent static class EntityManagerInjectionTarget { private final EntityManager firstEm; diff --git a/spring-data-jpa/src/test/resources/hibernate.xml b/spring-data-jpa/src/test/resources/hibernate.xml index fafd2e1201..05420a9be5 100644 --- a/spring-data-jpa/src/test/resources/hibernate.xml +++ b/spring-data-jpa/src/test/resources/hibernate.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> - + diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index 1c71bcf5d4..7d4b346560 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -1,6 +1,30 @@ [[new-features]] = New & Noteworthy +[[new-features.3-0]] +== What's New in Spring Data JPA 3.0 +* Upgrade to Hibernate 6. +See <> for what to consider when upgrading. +* Support for null handling definitions via `Sort`. + +[[new-features.3-0.hibernate-6]] +=== Upgrading to Hibernate 6 +Spring Data 3.0 upgrades its Hibernate baseline to Hibernate 6. +As quite a few things have changed in that version, a couple of things that have worked before might need some tweaks. + +* _Using JPA named queries with pagination_ -- Pagination requires Spring Data to derive a count query from the originally declared one loading the actual content of the Page. +For queries declared as JPA named queries we have relied on provider-specific API to obtain the original source query and tweak it accordingly. +On Hibernate 6, in certain arrangements that query extraction might fail. +We recommend to either rather declare the queries on the repository methods directly using `@Query`. +* _Using positional parameters with pagination_ -- When using positional parameters with pagination queries you need to make sure that the parameter indexes still start with 1, even with a potential `ORDER BY` clause removed from the query. +This is because, the count query derived from the original one will have that clause removed from the query and Hibernate 6 rejects queries parameter indexes not starting at 1. +We generally recommend to use named parameters anyway. +* _Applying JPA entity graphs_ -- Under certain model conditions, the application of entity graphs might fail on Hibernate 6. +See https://hibernate.atlassian.net/browse/HHH-15391[this ticket] for details. +We generally recommend to rather use <> instead of entity graphs. +* _Using `… like … escape ?#{escapeCharacter()}` in queries_ -- If you have customized the global default escape character (via `@EnableJpaRepositories(escapeCharacter = '…')`) the application of that through the corresponding SpEL expression currently fails. +See https://hibernate.atlassian.net/browse/HHH-15392[this ticket] for details. + [[new-features.2-5-0]] == What's New in Spring Data JPA 2.5 From 7de1ffdf2580d1fafa007dced78e7a2ed413f099 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 12 Jul 2022 14:44:24 +0200 Subject: [PATCH 229/821] Guard auditing runtime hints. See: #2497 --- ...nts.java => JpaRuntimeHintsRegistrar.java} | 12 +- .../resources/META-INF/spring/aot.factories | 2 +- .../JpaRuntimeHintsRegistrarUnitTests.java | 58 +++++++++ .../data/jpa/util/HidingClassLoader.java | 114 ++++++++++++++++++ 4 files changed, 179 insertions(+), 7 deletions(-) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/{DataJpaRuntimeHints.java => JpaRuntimeHintsRegistrar.java} (85%) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HidingClassLoader.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrar.java similarity index 85% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrar.java index b7a71648f3..e7492417d4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/DataJpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrar.java @@ -31,7 +31,7 @@ * @author Christoph Strobl * @since 3.0 */ -public class DataJpaRuntimeHints implements RuntimeHintsRegistrar { +public class JpaRuntimeHintsRegistrar implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { @@ -46,12 +46,12 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) hints.reflection().registerType( TypeReference.of("org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"), hint -> hint .withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); - } - hints.reflection().registerTypes(Arrays.asList( // - TypeReference.of(AuditingBeanFactoryPostProcessor.class), // - TypeReference.of(AuditingEntityListener.class)), - hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); + hints.reflection().registerTypes(Arrays.asList( // + TypeReference.of(AuditingBeanFactoryPostProcessor.class), // + TypeReference.of(AuditingEntityListener.class)), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); + } hints.reflection().registerType(TypeReference.of(SimpleJpaRepository.class), hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS)); diff --git a/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories b/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories index bb56670aa6..8822976a9a 100644 --- a/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories +++ b/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories @@ -1,2 +1,2 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ - org.springframework.data.jpa.aot.DataJpaRuntimeHints + org.springframework.data.jpa.aot.JpaRuntimeHintsRegistrar diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java new file mode 100644 index 0000000000..e83333407b --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.aot; + +import static org.assertj.core.api.AssertionsForClassTypes.*; +import static org.springframework.aot.hint.RuntimeHintsPredicates.*; + +import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect; +import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.data.jpa.util.HidingClassLoader; + +/** + * @author Christoph Strobl + */ +class JpaRuntimeHintsRegistrarUnitTests { + + @Test // GH-2497 + void registersAuditing() { + + RuntimeHints hints = new RuntimeHints(); + + JpaRuntimeHintsRegistrar registrar = new JpaRuntimeHintsRegistrar(); + registrar.registerHints(hints, null); + + assertThat(hints).matches(reflection().onType(AnnotationBeanConfigurerAspect.class)) + .matches(reflection().onType(AuditingEntityListener.class)) + .matches(reflection().onType(AuditingBeanFactoryPostProcessor.class)); + } + + @Test // GH-2497 + void skipsAuditingHintsIfAspectjNotPresent() { + + RuntimeHints hints = new RuntimeHints(); + + JpaRuntimeHintsRegistrar registrar = new JpaRuntimeHintsRegistrar(); + registrar.registerHints(hints, HidingClassLoader.hidePackages("org.springframework.beans.factory.aspectj")); + + assertThat(hints).matches(reflection().onType(AnnotationBeanConfigurerAspect.class).negate()) + .matches(reflection().onType(AuditingEntityListener.class).negate()) + .matches(reflection().onType(AuditingBeanFactoryPostProcessor.class).negate()); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HidingClassLoader.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HidingClassLoader.java new file mode 100644 index 0000000000..f68a87220b --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HidingClassLoader.java @@ -0,0 +1,114 @@ +/* + * Copyright 2017-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.util; + +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Collectors; + +import org.springframework.instrument.classloading.ShadowingClassLoader; +import org.springframework.util.Assert; + +/** + * is intended for testing code that depends on the presence/absence of certain classes. Classes can be: + *
    + *
  • shadowed: reloaded by this classloader no matter if they are loaded already by the SystemClassLoader
  • + *
  • hidden: not loaded by this classloader no matter if they are loaded already by the SystemClassLoader. Trying to + * load these classes results in a {@link ClassNotFoundException}
  • + *
  • all other classes get loaded by the SystemClassLoader
  • + *
+ * + * @author Jens Schauder + * @author Oliver Gierke + * @author Christoph Strobl + */ +public class HidingClassLoader extends ShadowingClassLoader { + + private final Collection hidden; + + HidingClassLoader(Collection hidden) { + + super(URLClassLoader.getSystemClassLoader(), false); + + this.hidden = hidden; + } + + /** + * Creates a new {@link HidingClassLoader} with the packages of the given classes hidden. + * + * @param packages must not be {@literal null}. + * @return + */ + public static HidingClassLoader hide(Class... packages) { + + Assert.notNull(packages, "Packages must not be null"); + + return new HidingClassLoader(Arrays.stream(packages)// + .map(it -> it.getPackage().getName())// + .collect(Collectors.toList())); + } + + /** + * Creates a new {@link HidingClassLoader} with the packages of the given classes hidden. + * + * @param packages must not be {@literal null}. + * @return + */ + public static HidingClassLoader hidePackages(String... packages) { + + Assert.notNull(packages, "Packages must not be null"); + + return new HidingClassLoader(Arrays.asList(packages)); + } + + public static HidingClassLoader hideTypes(Class... types) { + + Assert.notNull(types, "Types must not be null!"); + + return new HidingClassLoader(Arrays.stream(types)// + .map(it -> it.getName())// + .collect(Collectors.toList())); + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + + Class loaded = super.loadClass(name); + checkIfHidden(loaded); + return loaded; + } + + @Override + protected boolean isEligibleForShadowing(String className) { + return isExcluded(className); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + + Class loaded = super.findClass(name); + checkIfHidden(loaded); + return loaded; + } + + private void checkIfHidden(Class type) throws ClassNotFoundException { + + if (hidden.stream().anyMatch(it -> type.getName().startsWith(it))) { + throw new ClassNotFoundException(); + } + } +} From c51d32c0ff2c683b9799ac74f4ce9b7d5c3dd922 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 12 Jul 2022 17:48:55 -0700 Subject: [PATCH 230/821] Adapt to repackaging of the AOT RuntimeHintsPredicate. Closes #2592. --- .../data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java index e83333407b..bdc9f487fe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java @@ -16,7 +16,7 @@ package org.springframework.data.jpa.aot; import static org.assertj.core.api.AssertionsForClassTypes.*; -import static org.springframework.aot.hint.RuntimeHintsPredicates.*; +import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection; import org.junit.jupiter.api.Test; import org.springframework.aot.hint.RuntimeHints; From 1fc9c2529b9c4b7ffa05310aa8a66f4991f2b30c Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 13 Jul 2022 17:49:03 +0200 Subject: [PATCH 231/821] Set up entity load graph test according to what's required by Hibernate 6. As recommended in [0], we now set up our integration tests verifying the application of entity load graphs using a lazy @ManyToOne relationship. [0] https://hibernate.atlassian.net/browse/HHH-15391 Related to #2423. --- .../org/springframework/data/jpa/domain/sample/User.java | 7 +++---- .../EntityGraphRepositoryMethodsIntegrationTests.java | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java index ebf3b7f0a0..4521b97926 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -92,8 +92,7 @@ @Table(name = "SD_User") public class User { - @Id - @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; + @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private String firstname; private String lastname; private int age; @@ -106,7 +105,7 @@ public class User { @ManyToMany private Set roles; - @ManyToOne private User manager; + @ManyToOne(fetch = FetchType.LAZY) private User manager; @Embedded private Address address; @@ -372,7 +371,7 @@ public boolean equals(Object obj) { User that = (User) obj; - if (null == this.getId() || null == that.getId()) { + if ((null == this.getId()) || (null == that.getId())) { return false; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index 224a3e2517..0e3254370d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -31,7 +31,6 @@ import org.assertj.core.api.SoftAssertions; import org.junit.Assume; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -94,7 +93,6 @@ void setup() { } @Test // DATAJPA-612 - @Disabled // HHH-15391 void shouldRespectConfiguredJpaEntityGraph() { Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); From eff294f569c43baee2e47ff118de971a8b1547da Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 13 Jul 2022 16:25:45 -0500 Subject: [PATCH 232/821] Polishing. --- .../MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java index 0f3a49f65d..e5390cabb1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java @@ -51,8 +51,8 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect; import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter; -import org.springframework.orm.jpa.vendor.HibernateJpaDialect; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; @@ -236,7 +236,7 @@ public EntityManagerFactory entityManagerFactory() { @Bean public JpaDialect jpaDialect() { - return new HibernateJpaDialect(); + return new EclipseLinkJpaDialect(); } @Bean From 3d8b287e0e2129ec827b15613b44f31c19c65c61 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 13 Jul 2022 05:40:23 -0500 Subject: [PATCH 233/821] Properly handle null values inside queries using LIKE or CONTAINS. Null values are wrapped with a special handler when interacting with Hibernate. However, this becomes an issue for queries when LIKE or CONTAINS are applied. In this situation, the null needs to be condensed into an empty string and any wildcards can then be applied with expected results. Closes #2548, #2570. Supercedes: #2585. Related: #2461, #2544# --- .../jpa/provider/PersistenceProvider.java | 30 ++ .../query/ParameterMetadataProvider.java | 14 +- .../jpa/repository/query/StringQuery.java | 13 +- .../jpa/domain/sample/EmployeeWithName.java | 42 +++ ...WithNullLikeHibernateIntegrationTests.java | 280 ++++++++++++++++++ .../test/resources/META-INF/persistence.xml | 1 + .../src/test/resources/logback.xml | 4 + 7 files changed, 371 insertions(+), 13 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index f3ef2f0473..8dfa22e472 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -42,6 +42,7 @@ import org.springframework.lang.Nullable; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; /** @@ -310,6 +311,35 @@ public boolean canExtractQuery() { } } + /** + * Because Hibernate's {@literal TypedParameterValue} is only used to wrap a {@literal null}, swap it out with an + * empty string for query creation. + * + * @param value + * @return the original value or an empty string. + * @since 3.0 + */ + public static Object condense(Object value) { + + ClassLoader classLoader = PersistenceProvider.class.getClassLoader(); + + if (ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", classLoader)) { + + try { + + Class typeParameterValue = ClassUtils.forName("org.hibernate.query.TypedParameterValue", classLoader); + + if (typeParameterValue.isInstance(value)) { + return ""; + } + } catch (ClassNotFoundException | LinkageError o_O) { + return value; + } + } + + return value; + } + /** * Holds the PersistenceProvider specific interface names. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 97696f1cfe..1064c2058d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -15,6 +15,9 @@ */ package org.springframework.data.jpa.repository.query; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.ParameterExpression; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -24,9 +27,6 @@ import java.util.function.Supplier; import java.util.stream.Collectors; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.ParameterExpression; - import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; @@ -245,14 +245,14 @@ public Object prepare(Object value) { switch (type) { case STARTING_WITH: - return String.format("%s%%", escape.escape(value.toString())); + return String.format("%s%%", escape.escape(PersistenceProvider.condense(value).toString())); case ENDING_WITH: - return String.format("%%%s", escape.escape(value.toString())); + return String.format("%%%s", escape.escape(PersistenceProvider.condense(value).toString())); case CONTAINING: case NOT_CONTAINING: - return String.format("%%%s%%", escape.escape(value.toString())); + return String.format("%%%s%%", escape.escape(PersistenceProvider.condense(value).toString())); default: - return value; + return PersistenceProvider.condense(value); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 36ec1ca172..56e2d46185 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -27,6 +27,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.repository.query.SpelQueryContext; import org.springframework.data.repository.query.SpelQueryContext.SpelExtractor; import org.springframework.data.repository.query.parser.Part.Type; @@ -637,7 +638,7 @@ static class LikeParameterBinding extends ParameterBinding { /** * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type}. - * + * * @param name must not be {@literal null} or empty. * @param type must not be {@literal null}. */ @@ -648,7 +649,7 @@ static class LikeParameterBinding extends ParameterBinding { /** * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type} and parameter * binding input. - * + * * @param name must not be {@literal null} or empty. * @param type must not be {@literal null}. * @param expression may be {@literal null}. @@ -718,14 +719,14 @@ public Object prepare(@Nullable Object value) { switch (type) { case STARTING_WITH: - return String.format("%s%%", value); + return String.format("%s%%", PersistenceProvider.condense(value)); case ENDING_WITH: - return String.format("%%%s", value); + return String.format("%%%s", PersistenceProvider.condense(value)); case CONTAINING: - return String.format("%%%s%%", value); + return String.format("%%%s%%", PersistenceProvider.condense(value)); case LIKE: default: - return value; + return PersistenceProvider.condense(value); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java new file mode 100644 index 0000000000..3770598407 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Greg Turnquist + */ +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Data +public class EmployeeWithName { + + @Id + @GeneratedValue private Integer id; + private String name; + + public EmployeeWithName(String name) { + + this(); + this.name = name; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java new file mode 100644 index 0000000000..9c3f60d9d5 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java @@ -0,0 +1,280 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import jakarta.persistence.EntityManagerFactory; + +import java.util.List; +import java.util.Properties; + +import javax.sql.DataSource; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.FilterType; +import org.springframework.data.jpa.domain.sample.EmployeeWithName; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.repository.query.Param; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.lang.Nullable; +import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.Transactional; + +/** + * Verify that {@literal LIKE}s mixed with {@literal NULL}s work properly. + * + * @author Greg Turnquist + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = QueryWithNullLikeHibernateIntegrationTests.Config.class) +@Transactional +public class QueryWithNullLikeHibernateIntegrationTests { + + @Autowired EmpoyeeWithNullLikeRepository repository; + + @BeforeEach + void setUp() { + repository.saveAllAndFlush(List.of( // + new EmployeeWithName("Frodo Baggins"), // + new EmployeeWithName("Bilbo Baggins"))); + } + + @Test + void customQueryWithMultipleMatch() { + + List Employees = repository.customQueryWithNullableParam("Baggins"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void customQueryWithSingleMatch() { + + List Employees = repository.customQueryWithNullableParam("Frodo"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins"); + } + + @Test + void customQueryWithEmptyStringMatch() { + + List Employees = repository.customQueryWithNullableParam(""); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void customQueryWithNullMatch() { + + List Employees = repository.customQueryWithNullableParam(null); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryStartsWithSingleMatch() { + + List Employees = repository.findByNameStartsWith("Frodo"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins"); + } + + @Test + void derivedQueryStartsWithNoMatch() { + + List Employees = repository.findByNameStartsWith("Baggins"); + + assertThat(Employees).extracting(EmployeeWithName::getName).isEmpty(); + } + + @Test + void derivedQueryStartsWithWithEmptyStringMatch() { + + List Employees = repository.findByNameStartsWith(""); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryStartsWithWithNullMatch() { + + List Employees = repository.findByNameStartsWith(null); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryEndsWithWithMultipleMatch() { + + List Employees = repository.findByNameEndsWith("Baggins"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryEndsWithWithSingleMatch() { + + List Employees = repository.findByNameEndsWith("Frodo"); + + assertThat(Employees).extracting(EmployeeWithName::getName).isEmpty(); + } + + @Test + void derivedQueryEndsWithWithEmptyStringMatch() { + + List Employees = repository.findByNameEndsWith(""); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryEndsWithWithNullMatch() { + + List Employees = repository.findByNameEndsWith(null); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryContainsWithMultipleMatch() { + + List Employees = repository.findByNameContains("Baggins"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryContainsWithSingleMatch() { + + List Employees = repository.findByNameContains("Frodo"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactly("Frodo Baggins"); + } + + @Test + void derivedQueryContainsWithEmptyStringMatch() { + + List Employees = repository.findByNameContains(""); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryContainsWithNullMatch() { + + List Employees = repository.findByNameContains(null); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryLikeWithMultipleMatch() { + + List Employees = repository.findByNameLike("%Baggins%"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test + void derivedQueryLikeWithSingleMatch() { + + List Employees = repository.findByNameLike("%Frodo%"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactly("Frodo Baggins"); + } + + @Test + void derivedQueryLikeWithEmptyStringMatch() { + + List Employees = repository.findByNameLike("%%"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Transactional + public interface EmpoyeeWithNullLikeRepository extends JpaRepository { + + @Query("select e from EmployeeWithName e where e.name like %:partialName%") + List customQueryWithNullableParam(@Nullable @Param("partialName") String partialName); + + List findByNameStartsWith(@Nullable String partialName); + + List findByNameEndsWith(@Nullable String partialName); + + List findByNameContains(@Nullable String partialName); + + List findByNameLike(@Nullable String partialName); + } + + @EnableJpaRepositories(considerNestedRepositories = true, // + includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = EmpoyeeWithNullLikeRepository.class)) + @EnableTransactionManagement + static class Config { + + @Bean + DataSource dataSource() { + return new EmbeddedDatabaseBuilder().generateUniqueName(true).build(); + } + + @Bean + AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { + + LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setPersistenceUnitName("spring-data-jpa"); + factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + + Properties properties = new Properties(); + properties.setProperty("hibernate.hbm2ddl.auto", "create"); + factoryBean.setJpaProperties(properties); + + return factoryBean; + } + + @Bean + PlatformTransactionManager transactionManager(EntityManagerFactory emf) { + return new JpaTransactionManager(emf); + } + } +} diff --git a/spring-data-jpa/src/test/resources/META-INF/persistence.xml b/spring-data-jpa/src/test/resources/META-INF/persistence.xml index 7e55f6e600..280312885a 100644 --- a/spring-data-jpa/src/test/resources/META-INF/persistence.xml +++ b/spring-data-jpa/src/test/resources/META-INF/persistence.xml @@ -22,6 +22,7 @@ org.springframework.data.jpa.domain.sample.EmbeddedIdExampleEmployeePK org.springframework.data.jpa.domain.sample.EmbeddedIdExampleEmployee org.springframework.data.jpa.domain.sample.EmbeddedIdExampleDepartment + org.springframework.data.jpa.domain.sample.EmployeeWithName org.springframework.data.jpa.domain.sample.IdClassExampleEmployee org.springframework.data.jpa.domain.sample.IdClassExampleDepartment org.springframework.data.jpa.domain.sample.Invoice diff --git a/spring-data-jpa/src/test/resources/logback.xml b/spring-data-jpa/src/test/resources/logback.xml index d7b83cb955..19bb933f9c 100644 --- a/spring-data-jpa/src/test/resources/logback.xml +++ b/spring-data-jpa/src/test/resources/logback.xml @@ -13,6 +13,10 @@ + + + + From 3a33cbca2753cc635d4893d8ba662e3bea1e8565 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 14 Jul 2022 06:20:54 +0200 Subject: [PATCH 234/821] Rename JpaRuntimeHintsRegistrar to JpaRuntimeHints. See: #2497 --- .../{JpaRuntimeHintsRegistrar.java => JpaRuntimeHints.java} | 2 +- .../src/main/resources/META-INF/spring/aot.factories | 2 +- ...egistrarUnitTests.java => JpaRuntimeHintsUnitTests.java} | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/{JpaRuntimeHintsRegistrar.java => JpaRuntimeHints.java} (97%) rename spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/{JpaRuntimeHintsRegistrarUnitTests.java => JpaRuntimeHintsUnitTests.java} (91%) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java similarity index 97% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrar.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java index e7492417d4..cc059657c2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java @@ -31,7 +31,7 @@ * @author Christoph Strobl * @since 3.0 */ -public class JpaRuntimeHintsRegistrar implements RuntimeHintsRegistrar { +public class JpaRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { diff --git a/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories b/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories index 8822976a9a..4363dcaeb8 100644 --- a/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories +++ b/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories @@ -1,2 +1,2 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ - org.springframework.data.jpa.aot.JpaRuntimeHintsRegistrar + org.springframework.data.jpa.aot.JpaRuntimeHints diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsUnitTests.java similarity index 91% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsUnitTests.java index bdc9f487fe..c992375198 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsRegistrarUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsUnitTests.java @@ -28,14 +28,14 @@ /** * @author Christoph Strobl */ -class JpaRuntimeHintsRegistrarUnitTests { +class JpaRuntimeHintsUnitTests { @Test // GH-2497 void registersAuditing() { RuntimeHints hints = new RuntimeHints(); - JpaRuntimeHintsRegistrar registrar = new JpaRuntimeHintsRegistrar(); + JpaRuntimeHints registrar = new JpaRuntimeHints(); registrar.registerHints(hints, null); assertThat(hints).matches(reflection().onType(AnnotationBeanConfigurerAspect.class)) @@ -48,7 +48,7 @@ void skipsAuditingHintsIfAspectjNotPresent() { RuntimeHints hints = new RuntimeHints(); - JpaRuntimeHintsRegistrar registrar = new JpaRuntimeHintsRegistrar(); + JpaRuntimeHints registrar = new JpaRuntimeHints(); registrar.registerHints(hints, HidingClassLoader.hidePackages("org.springframework.beans.factory.aspectj")); assertThat(hints).matches(reflection().onType(AnnotationBeanConfigurerAspect.class).negate()) From c6db8a774abc83b632861ea214a47ba2cb31803d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 15 Jul 2022 15:17:55 +0200 Subject: [PATCH 235/821] Prepare 3.0 M5 (2022.0.0). See #2531 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 8a1565932b..7795eff01c 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-SNAPSHOT + 3.0.0-M5 @@ -35,7 +35,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-SNAPSHOT + 3.0.0-M5 0.10.3 org.hibernate @@ -218,8 +218,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From c4148e5bd519c6ce179f17663564dfd6d07b4f12 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 15 Jul 2022 15:18:29 +0200 Subject: [PATCH 236/821] Release version 3.0 M5 (2022.0.0). See #2531 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 7795eff01c..6a7399d785 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M5 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 540834bb60..84b28b743c 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-SNAPSHOT + 3.0.0-M5 org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M5 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index bba8571672..41e3016225 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M5 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 3ad9c39a2a..040a00eb02 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-SNAPSHOT + 3.0.0-M5 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-M5 ../pom.xml From 48e6f3189e2d5ae844166299ec3eea751f9892fc Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 15 Jul 2022 15:30:45 +0200 Subject: [PATCH 237/821] Prepare next development iteration. See #2531 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 6a7399d785..7795eff01c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M5 + 3.0.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 84b28b743c..540834bb60 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-M5 + 3.0.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.0.0-M5 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 41e3016225..bba8571672 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M5 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 040a00eb02..3ad9c39a2a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-M5 + 3.0.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-M5 + 3.0.0-SNAPSHOT ../pom.xml From 54047edffb8bbbe6ae5bc1791518d39338ab6e0b Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 15 Jul 2022 15:30:53 +0200 Subject: [PATCH 238/821] After release cleanups. See #2531 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 7795eff01c..8a1565932b 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-M5 + 3.0.0-SNAPSHOT @@ -35,7 +35,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-M5 + 3.0.0-SNAPSHOT 0.10.3 org.hibernate @@ -218,8 +218,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From fa41595818eeb0d1da7f271a7f0554eb544fc6e3 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Tue, 26 Jul 2022 09:38:12 +0800 Subject: [PATCH 239/821] Limit single finders max results to 2 for performance. Closes #2594 Original pull request #2604 --- .../jpa/repository/support/QuerydslJpaPredicateExecutor.java | 2 +- .../data/jpa/repository/support/QuerydslJpaRepository.java | 2 +- .../data/jpa/repository/support/SimpleJpaRepository.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index c4f4b07b99..1de39e2c53 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -91,7 +91,7 @@ public Optional findOne(Predicate predicate) { Assert.notNull(predicate, "Predicate must not be null"); try { - return Optional.ofNullable(createQuery(predicate).select(path).fetchOne()); + return Optional.ofNullable(createQuery(predicate).select(path).limit(2).fetchOne()); } catch (NonUniqueResultException ex) { throw new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index 3ef8c5db45..d082e65a68 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -101,7 +101,7 @@ public QuerydslJpaRepository(JpaEntityInformation entityInformation, Enti public Optional findOne(Predicate predicate) { try { - return Optional.ofNullable(createQuery(predicate).select(path).fetchOne()); + return Optional.ofNullable(createQuery(predicate).select(path).limit(2).fetchOne()); } catch (NonUniqueResultException ex) { throw new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 0b0125bc54..0927afc231 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -452,7 +452,7 @@ public Page findAll(Pageable pageable) { public Optional findOne(@Nullable Specification spec) { try { - return Optional.of(getQuery(spec, Sort.unsorted()).getSingleResult()); + return Optional.of(getQuery(spec, Sort.unsorted()).setMaxResults(2).getSingleResult()); } catch (NoResultException e) { return Optional.empty(); } @@ -482,7 +482,7 @@ public Optional findOne(Example example) { try { return Optional .of(getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) - .getSingleResult()); + .setMaxResults(2).getSingleResult()); } catch (NoResultException e) { return Optional.empty(); } From 6a529cca23258bfe5a1f45b9978381d60d803031 Mon Sep 17 00:00:00 2001 From: lanicc Date: Tue, 2 Aug 2022 15:28:21 +0800 Subject: [PATCH 240/821] Fix the JDK version documented to build the source code. Closes: #2610 Original pull request: #2611. --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 22175f8fc6..b3ef9abdf8 100644 --- a/README.adoc +++ b/README.adoc @@ -152,7 +152,7 @@ https://github.com/spring-projects/spring-data-jpa/issues[issue tracker] to see == Building from Source You don’t need to build from source to use Spring Data (binaries in https://repo.spring.io[repo.spring.io]), but if you want to try out the latest and greatest, Spring Data can be easily built with the https://github.com/takari/maven-wrapper[maven wrapper]. -You also need JDK 1.8. +You also need JDK 17 or above. [source,bash] ---- From 65d524e3198e58970064159865fe482d9c92d2fa Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 4 Aug 2022 09:24:56 +0200 Subject: [PATCH 241/821] Upgrade to Hibernate 6.1.2. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-enable test cases that were previously running into HHH-15389 [0]. Filed an explicit ticket for the broken access of the sort definitions in HHH-15432 [1]. Disabled EntityGraphRepositoryMethodsIntegrationTests.shouldCreateDynamicGraphWithMultipleLevelsOfSubgraphs(…) as Hibernate now apparently behaves different for application of nested paths in fetch graphs. Likely a side effect of the fix for HHH-15391 [2]. Removed obsolete method in PartTreeJpaQueryIntegrationTests. Fixes #2615. [0] https://hibernate.atlassian.net/browse/HHH-15389 [1] https://hibernate.atlassian.net/browse/HHH-15432 [2] https://hibernate.atlassian.net/browse/HHH-15391 --- pom.xml | 2 +- ...raphRepositoryMethodsIntegrationTests.java | 2 ++ .../repository/UserRepositoryFinderTests.java | 10 ------- .../PartTreeJpaQueryIntegrationTests.java | 28 +++---------------- 4 files changed, 7 insertions(+), 35 deletions(-) diff --git a/pom.xml b/pom.xml index 8a1565932b..1bb0a1c435 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 3.0.2 - 6.1.1.Final + 6.1.2.Final 4.3 8.0.23 42.2.19 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index 0e3254370d..ba817878b5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -31,6 +31,7 @@ import org.assertj.core.api.SoftAssertions; import org.junit.Assume; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -252,6 +253,7 @@ void shouldRespectMultipleSubGraphForSameAttributeWithDynamicFetchGraph() { } @Test // DATAJPA-1041, DATAJPA-1075 + @Disabled // likely broken due to the fixes made for HHH-15391 void shouldCreateDynamicGraphWithMultipleLevelsOfSubgraphs() { Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 3241a237ff..2046127efa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -16,7 +16,6 @@ package org.springframework.data.jpa.repository; import static org.assertj.core.api.Assertions.*; -import static org.junit.Assume.*; import static org.springframework.data.domain.Sort.Direction.*; import jakarta.persistence.EntityManager; @@ -235,9 +234,6 @@ void parametersForContainsGetProperlyEscaped() { @Test // DATAJPA-1519 void escapingInLikeSpels() { - // HHH-15392 - assumeFalse(provider.equals(PersistenceProvider.HIBERNATE)); - User extra = new User("extra", "Matt_ew", "extra"); userRepository.save(extra); @@ -248,9 +244,6 @@ void escapingInLikeSpels() { @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { - // HHH-15392 - assumeFalse(provider.equals(PersistenceProvider.HIBERNATE)); - User withEscapeCharacter = userRepository.save(new User("extra", "Matt\\xew", "extra1")); userRepository.save(new User("extra", "Matt\\_ew", "extra2")); @@ -260,9 +253,6 @@ void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapedWildcards() { - // HHH-15392 - assumeFalse(provider.equals(PersistenceProvider.HIBERNATE)); - userRepository.save(new User("extra", "Matt\\xew", "extra1")); User withEscapedWildcard = userRepository.save(new User("extra", "Matt\\_ew", "extra2")); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 980cef7b4e..b002b07bea 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -18,7 +18,6 @@ package org.springframework.data.jpa.repository.query; import static org.assertj.core.api.Assertions.*; -import static org.springframework.test.util.ReflectionTestUtils.*; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -26,10 +25,8 @@ import jakarta.persistence.TemporalType; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collection; import java.util.Date; -import java.util.Iterator; import java.util.List; import org.hibernate.Version; @@ -50,7 +47,6 @@ import org.springframework.data.repository.query.Param; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.util.Assert; /** * Integration tests for {@link PartTreeJpaQuery}. @@ -101,7 +97,7 @@ void cannotIgnoreCaseIfNotStringUnlessIgnoringAll() throws Exception { } @Test // DATAJPA-121 - @Disabled // HHH-15389 + @Disabled // HHH-15432 void recreatesQueryIfNullValueIsGiven() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class); @@ -128,7 +124,7 @@ void shouldLimitExistsProjectionQueries() throws Exception { } @Test // DATAJPA-920 - @Disabled // HHH-15389 + @Disabled // HHH-15432 void shouldSelectAliasedIdForExistsProjectionQueries() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("existsByFirstname", String.class); @@ -140,7 +136,7 @@ void shouldSelectAliasedIdForExistsProjectionQueries() throws Exception { } @Test // DATAJPA-1074 - @Disabled // HHH-15389 + @Disabled // HHH-15432 void isEmptyCollection() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("findByRolesIsEmpty"); @@ -152,7 +148,7 @@ void isEmptyCollection() throws Exception { } @Test // DATAJPA-1074 - @Disabled // HHH-15389 + @Disabled // HHH-15432 void isNotEmptyCollection() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("findByRolesIsNotEmpty"); @@ -251,22 +247,6 @@ private JpaQueryMethod getQueryMethod(String methodName, Class... parameterTy new SpelAwareProxyProjectionFactory(), PersistenceProvider.fromEntityManager(entityManager)); } - @SuppressWarnings("unchecked") - private static T getValue(Object source, String path) { - - Iterator split = Arrays.asList(path.split("\\.")).iterator(); - Object result = source; - - while (split.hasNext()) { - - Assert.notNull(result, "result must not be null"); - result = getField(result, split.next()); - } - - Assert.notNull(result, "result must not be null"); - return (T) result; - } - private JpaParametersParameterAccessor getAccessor(JpaQueryMethod queryMethod, Object[] values) { return new JpaParametersParameterAccessor(queryMethod.getParameters(), values); } From 9fd0e475149c9ebc16d750f20119c45cda69750e Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 1 Aug 2022 14:37:03 +0200 Subject: [PATCH 242/821] Support Contains with ElementCollection of type String. Properly handle `contains` for an ElementCollection of type String in a LIKE query, wrapping the the parameter in wildcards only when needed. Closes: #2607. --- .../query/ParameterMetadataProvider.java | 4 +++- .../jpa/repository/UserRepositoryTests.java | 18 ++++++++++++++++++ .../jpa/repository/sample/UserRepository.java | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 1064c2058d..acca0a5dcb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -196,6 +196,7 @@ static class ParameterMetadata { private final ParameterExpression expression; private final EscapeCharacter escape; private final boolean ignoreCase; + private final boolean noWildcards; /** * Creates a new {@link ParameterMetadata}. @@ -206,6 +207,7 @@ public ParameterMetadata(ParameterExpression expression, Part part, @Nullable this.expression = expression; this.type = value == null && Type.SIMPLE_PROPERTY.equals(part.getType()) ? Type.IS_NULL : part.getType(); this.ignoreCase = IgnoreCaseType.ALWAYS.equals(part.shouldIgnoreCase()); + this.noWildcards = part.getProperty().getLeafProperty().isCollection(); this.escape = escape; } @@ -241,7 +243,7 @@ public Object prepare(Object value) { return value; } - if (String.class.equals(expressionType)) { + if (String.class.equals(expressionType) && !noWildcards) { switch (type) { case STARTING_WITH: 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 877c8c9053..283e45161d 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 @@ -2968,6 +2968,24 @@ void complexWithNativeStatement() { assertThat(foundData).containsExactly("joachim", "dave", "kevin"); } + @Test // GH-2607 + void containsWithCollection(){ + + firstUser.getAttributes().add("cool"); + firstUser.getAttributes().add("hip"); + + secondUser.getAttributes().add("hip"); + + thirdUser.getAttributes().add("rockstar"); + thirdUser.getAttributes().add("%hip%"); + + flushTestUsers(); + + List result = repository.findByAttributesContains("hip"); + + assertThat(result).containsOnly(firstUser, secondUser); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); 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 51659d9685..3236ddfa3f 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 @@ -676,6 +676,9 @@ List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter nativeQuery = true) List complexWithNativeStatement(); + // GH-2607 + List findByAttributesContains(String attribute); + interface RolesAndFirstname { String getFirstname(); From 38f391c836476d8f4e369fda652373a4837c46ff Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 1 Aug 2022 14:37:59 +0200 Subject: [PATCH 243/821] Polishing. See #2607 --- .../query/QueryParameterSetterFactory.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index c27abbd2be..1b21b0f8eb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -15,12 +15,12 @@ */ package org.springframework.data.jpa.repository.query; -import java.util.List; -import java.util.function.Function; - import jakarta.persistence.Query; import jakarta.persistence.TemporalType; +import java.util.List; +import java.util.function.Function; + import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; import org.springframework.data.jpa.repository.query.QueryParameterSetter.NamedOrIndexedQueryParameterSetter; @@ -260,7 +260,7 @@ private static String getName(JpaParameter p) { private static class CriteriaQueryParameterSetterFactory extends QueryParameterSetterFactory { private final JpaParameters parameters; - private final List> expressions; + private final List> parameterMetadata; /** * Creates a new {@link QueryParameterSetterFactory} from the given {@link JpaParameters} and @@ -275,7 +275,7 @@ private static class CriteriaQueryParameterSetterFactory extends QueryParameterS Assert.notNull(metadata, "Expressions must not be null"); this.parameters = parameters; - this.expressions = metadata; + this.parameterMetadata = metadata; } @Override @@ -284,15 +284,15 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla int parameterIndex = binding.getRequiredPosition() - 1; Assert.isTrue( // - parameterIndex < expressions.size(), // + parameterIndex < parameterMetadata.size(), // () -> String.format( // "At least %s parameter(s) provided but only %s parameter(s) present in query", // binding.getRequiredPosition(), // - expressions.size() // + parameterMetadata.size() // ) // ); - ParameterMetadata metadata = expressions.get(parameterIndex); + ParameterMetadata metadata = parameterMetadata.get(parameterIndex); if (metadata.isIsNullParameter()) { return QueryParameterSetter.NOOP; @@ -301,9 +301,8 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla JpaParameter parameter = parameters.getBindableParameter(parameterIndex); TemporalType temporalType = parameter.isTemporalParameter() ? parameter.getRequiredTemporalType() : null; - return new NamedOrIndexedQueryParameterSetter(values -> { - return getAndPrepare(parameter, metadata, values); - }, metadata.getExpression(), temporalType); + return new NamedOrIndexedQueryParameterSetter(values -> getAndPrepare(parameter, metadata, values), + metadata.getExpression(), temporalType); } @Nullable From 1507150f83eb42f721673b3c868a9e64e61a0043 Mon Sep 17 00:00:00 2001 From: furnivall Date: Fri, 26 Aug 2022 14:18:47 +0100 Subject: [PATCH 244/821] Fix typo on AuditingEntityListener JavaDoc. Original pull request #2624 --- .../data/jpa/domain/support/AuditingEntityListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java index 25dabceb61..af68cf47c9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java @@ -26,7 +26,7 @@ import org.springframework.util.Assert; /** - * JPA entity listener to capture auditing information on persiting and updating entities. To get this one flying be + * JPA entity listener to capture auditing information on persisting and updating entities. To get this one flying be * sure you configure it as entity listener in your {@code orm.xml} as follows: * *

From 49b933e9fc7f90f8d4a83e0eab1553cfe6416ef2 Mon Sep 17 00:00:00 2001
From: Oliver Drotbohm 
Date: Tue, 13 Sep 2022 14:25:38 +0200
Subject: [PATCH 245/821] Add build profile for Eclipselink 4.0.

Currently RC2.

Ticket #2630.
---
 pom.xml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/pom.xml b/pom.xml
index 1bb0a1c435..5ba0fde025 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,6 +101,12 @@
 				
 			
 		
+		
+			eclipselink-next
+			
+				4.0.0-RC2
+			
+		
 
 	
 

From 71ad509e79d333cd31703d515003541a8bf6fb3f Mon Sep 17 00:00:00 2001
From: Christoph Strobl 
Date: Thu, 15 Sep 2022 11:52:21 +0200
Subject: [PATCH 246/821] Add runtime hint for repository Query annotation.

The annotations default values are evaluated so we need it to be present no matter if used or not.

See: #2497
---
 .../org/springframework/data/jpa/aot/JpaRuntimeHints.java     | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java
index cc059657c2..a8026d6eb2 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java
@@ -23,6 +23,7 @@
 import org.springframework.aot.hint.TypeReference;
 import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor;
 import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
 import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
@@ -55,5 +56,8 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader)
 
 		hints.reflection().registerType(TypeReference.of(SimpleJpaRepository.class),
 				hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS));
+
+		// needs to present for evaluating default attribute values in JpaQueryMethod
+		hints.reflection().registerType(Query.class, hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
 	}
 }

From b19647c6a5049b4ef8cd2a408f082705382f1013 Mon Sep 17 00:00:00 2001
From: Spring Builds 
Date: Mon, 19 Sep 2022 14:12:51 +0000
Subject: [PATCH 247/821] Prepare 3.0 M6 (2022.0.0).

See #2599
---
 pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 5ba0fde025..d98442b69d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
 	
 		org.springframework.data.build
 		spring-data-parent
-		3.0.0-SNAPSHOT
+		3.0.0-M6
 	
 
 	
@@ -35,7 +35,7 @@
 		4.3
 		8.0.23
 		42.2.19
-		3.0.0-SNAPSHOT
+		3.0.0-M6
 		0.10.3
 
 		org.hibernate
@@ -224,8 +224,8 @@
 
 	
 		
-			spring-libs-snapshot
-			https://repo.spring.io/libs-snapshot
+			spring-libs-milestone
+			https://repo.spring.io/libs-milestone
 		
 	
 

From 9a30685e389c579b6554757b607e7f3177bd1a38 Mon Sep 17 00:00:00 2001
From: Spring Builds 
Date: Mon, 19 Sep 2022 14:15:20 +0000
Subject: [PATCH 248/821] Release version 3.0 M6 (2022.0.0).

See #2599
---
 pom.xml                              | 2 +-
 spring-data-envers/pom.xml           | 4 ++--
 spring-data-jpa-distribution/pom.xml | 2 +-
 spring-data-jpa/pom.xml              | 4 ++--
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/pom.xml b/pom.xml
index d98442b69d..88fbe91c31 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
 
 	org.springframework.data
 	spring-data-jpa-parent
-	3.0.0-SNAPSHOT
+	3.0.0-M6
 	pom
 
 	Spring Data JPA Parent
diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml
index 540834bb60..57699d50e3 100755
--- a/spring-data-envers/pom.xml
+++ b/spring-data-envers/pom.xml
@@ -5,12 +5,12 @@
 
 	org.springframework.data
 	spring-data-envers
-	3.0.0-SNAPSHOT
+	3.0.0-M6
 
 	
 		org.springframework.data
 		spring-data-jpa-parent
-		3.0.0-SNAPSHOT
+		3.0.0-M6
 		../pom.xml
 	
 
diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml
index bba8571672..3458faa6ee 100644
--- a/spring-data-jpa-distribution/pom.xml
+++ b/spring-data-jpa-distribution/pom.xml
@@ -14,7 +14,7 @@
 	
 		org.springframework.data
 		spring-data-jpa-parent
-		3.0.0-SNAPSHOT
+		3.0.0-M6
 		../pom.xml
 	
 
diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml
index 3ad9c39a2a..ccb33db59a 100644
--- a/spring-data-jpa/pom.xml
+++ b/spring-data-jpa/pom.xml
@@ -6,7 +6,7 @@
 
 	org.springframework.data
 	spring-data-jpa
-	3.0.0-SNAPSHOT
+	3.0.0-M6
 
 	Spring Data JPA
 	Spring Data module for JPA repositories.
@@ -15,7 +15,7 @@
 	
 		org.springframework.data
 		spring-data-jpa-parent
-		3.0.0-SNAPSHOT
+		3.0.0-M6
 		../pom.xml
 	
 

From 8c3d19096a091dd780739a1a32ff85502854e226 Mon Sep 17 00:00:00 2001
From: Spring Builds 
Date: Mon, 19 Sep 2022 14:38:52 +0000
Subject: [PATCH 249/821] Prepare next development iteration.

See #2599
---
 pom.xml                              | 2 +-
 spring-data-envers/pom.xml           | 4 ++--
 spring-data-jpa-distribution/pom.xml | 2 +-
 spring-data-jpa/pom.xml              | 4 ++--
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/pom.xml b/pom.xml
index 88fbe91c31..d98442b69d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
 
 	org.springframework.data
 	spring-data-jpa-parent
-	3.0.0-M6
+	3.0.0-SNAPSHOT
 	pom
 
 	Spring Data JPA Parent
diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml
index 57699d50e3..540834bb60 100755
--- a/spring-data-envers/pom.xml
+++ b/spring-data-envers/pom.xml
@@ -5,12 +5,12 @@
 
 	org.springframework.data
 	spring-data-envers
-	3.0.0-M6
+	3.0.0-SNAPSHOT
 
 	
 		org.springframework.data
 		spring-data-jpa-parent
-		3.0.0-M6
+		3.0.0-SNAPSHOT
 		../pom.xml
 	
 
diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml
index 3458faa6ee..bba8571672 100644
--- a/spring-data-jpa-distribution/pom.xml
+++ b/spring-data-jpa-distribution/pom.xml
@@ -14,7 +14,7 @@
 	
 		org.springframework.data
 		spring-data-jpa-parent
-		3.0.0-M6
+		3.0.0-SNAPSHOT
 		../pom.xml
 	
 
diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml
index ccb33db59a..3ad9c39a2a 100644
--- a/spring-data-jpa/pom.xml
+++ b/spring-data-jpa/pom.xml
@@ -6,7 +6,7 @@
 
 	org.springframework.data
 	spring-data-jpa
-	3.0.0-M6
+	3.0.0-SNAPSHOT
 
 	Spring Data JPA
 	Spring Data module for JPA repositories.
@@ -15,7 +15,7 @@
 	
 		org.springframework.data
 		spring-data-jpa-parent
-		3.0.0-M6
+		3.0.0-SNAPSHOT
 		../pom.xml
 	
 

From d5043b14d3d6b8dda2f76c0b6e1f8b6f797e58d8 Mon Sep 17 00:00:00 2001
From: Spring Builds 
Date: Mon, 19 Sep 2022 14:39:04 +0000
Subject: [PATCH 250/821] After release cleanups.

See #2599
---
 pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index d98442b69d..5ba0fde025 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
 	
 		org.springframework.data.build
 		spring-data-parent
-		3.0.0-M6
+		3.0.0-SNAPSHOT
 	
 
 	
@@ -35,7 +35,7 @@
 		4.3
 		8.0.23
 		42.2.19
-		3.0.0-M6
+		3.0.0-SNAPSHOT
 		0.10.3
 
 		org.hibernate
@@ -224,8 +224,8 @@
 
 	
 		
-			spring-libs-milestone
-			https://repo.spring.io/libs-milestone
+			spring-libs-snapshot
+			https://repo.spring.io/libs-snapshot
 		
 	
 

From 91ba4e250f6d7eedba57659d5fc8c1c68db6a1e8 Mon Sep 17 00:00:00 2001
From: "Greg L. Turnquist" 
Date: Mon, 19 Sep 2022 13:22:35 -0500
Subject: [PATCH 251/821] Add additional stages to verify provider snapshots.

See #2630.
---
 Jenkinsfile | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/Jenkinsfile b/Jenkinsfile
index 4b9e1be19b..fc44aa5924 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -45,6 +45,38 @@ pipeline {
 			}
 		}
 
+		stage("Test other configurations") {
+			when {
+				beforeAgent(true)
+				allOf {
+					branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
+					not { triggeredBy 'UpstreamCause' }
+				}
+			}
+
+			parallel {
+			    stage("test: eclipselink-next") {
+					agent {
+					    label 'data'
+					}
+					options { timeout(time: 30, unit: 'MINUTES')}
+					environment {
+        				DOCKER_HUB = credentials("${p['docker.credentials']}")
+					    ARTIFACTORY = credentials("${p['artifactory.credentials']}")
+					}
+					steps {
+						script {
+							docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
+								sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
+								sh 'PROFILE=all-dbs,eclipselink-next ci/test.sh'
+								sh "ci/clean.sh"
+							}
+						}
+					}
+			    }
+			}
+		}
+
 		stage('Release to artifactory') {
 			when {
 				beforeAgent(true)

From 8f4156b848cfff3536dbb289964162a7ca9ee7a3 Mon Sep 17 00:00:00 2001
From: Christoph Strobl 
Date: Wed, 14 Sep 2022 14:59:16 +0200
Subject: [PATCH 252/821] Disable domain type inspection.

JPA managed types are now registered via PersistenceManagedTypes in spring.orm so there is no need to do it twice.

See #2628.
---
 .../config/JpaRepositoryConfigExtension.java  |  23 ++++
 ...JpaRepositoryConfigExtensionUnitTests.java |  14 +-
 ...toryRegistrationAotProcessorUnitTests.java | 128 ++++++++++++++++++
 3 files changed, 161 insertions(+), 4 deletions(-)
 create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java

diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java
index 07550f533c..e06e6abb0f 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java
@@ -33,6 +33,8 @@
 import java.util.Optional;
 import java.util.Set;
 
+import org.springframework.aot.generate.GenerationContext;
+import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -44,6 +46,8 @@
 import org.springframework.core.io.ResourceLoader;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
+import org.springframework.data.aot.AotRepositoryContext;
+import org.springframework.data.aot.RepositoryRegistrationAotProcessor;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.support.DefaultJpaContext;
 import org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor;
@@ -118,6 +122,11 @@ public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSo
 		builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME);
 	}
 
+	@Override
+	public Class getRepositoryAotProcessor() {
+		return JpaRepositoryRegistrationAotProcessor.class;
+	}
+
 	/**
 	 * XML configurations do not support {@link Character} values. This method catches the exception thrown and returns an
 	 * {@link Optional#empty()} instead.
@@ -285,4 +294,18 @@ static boolean isActive(@Nullable ClassLoader classLoader) {
 					.anyMatch(agentClass -> ClassUtils.isPresent(agentClass, classLoader));
 		}
 	}
+
+	/**
+	 * A {@link RepositoryRegistrationAotProcessor} implementation that maintains aot repository setup but skips domain
+	 * type inspection which is handled by the core framework support for
+	 * {@link org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes}.
+	 *
+	 * @since 3.0
+	 */
+	public static class JpaRepositoryRegistrationAotProcessor extends RepositoryRegistrationAotProcessor {
+
+		protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) {
+			// don't register domain types nor annotations.
+		}
+	}
 }
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java
index d9d62f8da4..a3e4ac9eab 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java
@@ -18,19 +18,18 @@
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
-import java.util.Arrays;
-import java.util.Collections;
-
 import jakarta.persistence.EntityManagerFactory;
 import jakarta.persistence.metamodel.Metamodel;
 
+import java.util.Arrays;
+import java.util.Collections;
+
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoSettings;
 import org.mockito.quality.Strictness;
-
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -141,6 +140,13 @@ public Class loadClass(String name) throws ClassNotFoundException {
 		assertThat(classLoader).isNotInstanceOf(InspectionClassLoader.class);
 	}
 
+	@Test // GH-2628
+	void exposesJpaAotProcessor() {
+
+		assertThat(new JpaRepositoryConfigExtension().getRepositoryAotProcessor())
+				.isEqualTo(JpaRepositoryConfigExtension.JpaRepositoryRegistrationAotProcessor.class);
+	}
+
 	private void assertOnlyOnePersistenceAnnotationBeanPostProcessorRegistered(DefaultListableBeanFactory factory,
 			String expectedBeanName) {
 
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java
new file mode 100644
index 0000000000..5204de044c
--- /dev/null
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.jpa.repository.config;
+
+import static org.assertj.core.api.Assertions.*;
+
+import jakarta.persistence.Entity;
+
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.aot.generate.ClassNameGenerator;
+import org.springframework.aot.generate.DefaultGenerationContext;
+import org.springframework.aot.generate.GenerationContext;
+import org.springframework.aot.generate.InMemoryGeneratedFiles;
+import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.core.annotation.MergedAnnotation;
+import org.springframework.data.aot.AotRepositoryContext;
+import org.springframework.data.repository.core.RepositoryInformation;
+
+/**
+ * @author Christoph Strobl
+ */
+class JpaRepositoryRegistrationAotProcessorUnitTests {
+
+	@Test // GH-2628
+	void aotProcessorMustNotRegisterDomainTypes() {
+
+		GenerationContext ctx = new DefaultGenerationContext(new ClassNameGenerator(Object.class),
+				new InMemoryGeneratedFiles());
+
+		new JpaRepositoryConfigExtension.JpaRepositoryRegistrationAotProcessor()
+				.contribute(new DummyAotRepositoryContext() {
+					@Override
+					public Set> getResolvedTypes() {
+						return Collections.singleton(Person.class);
+					}
+				}, ctx);
+
+		assertThat(RuntimeHintsPredicates.reflection().onType(Person.class)).rejects(ctx.getRuntimeHints());
+	}
+
+	@Test // GH-2628
+	void aotProcessorMustNotRegisterAnnotations() {
+
+		GenerationContext ctx = new DefaultGenerationContext(new ClassNameGenerator(Object.class),
+				new InMemoryGeneratedFiles());
+
+		new JpaRepositoryConfigExtension.JpaRepositoryRegistrationAotProcessor()
+				.contribute(new DummyAotRepositoryContext() {
+
+					@Override
+					public Set> getResolvedAnnotations() {
+
+						MergedAnnotation mergedAnnotation = MergedAnnotation.of(Entity.class);
+						return Set.of(mergedAnnotation);
+					}
+				}, ctx);
+
+		assertThat(RuntimeHintsPredicates.reflection().onType(Entity.class)).rejects(ctx.getRuntimeHints());
+	}
+
+	static class Person {}
+
+	static class DummyAotRepositoryContext implements AotRepositoryContext {
+
+		@Override
+		public String getBeanName() {
+			return "jpaRepository";
+		}
+
+		@Override
+		public Set getBasePackages() {
+			return Collections.singleton(this.getClass().getPackageName());
+		}
+
+		@Override
+		public Set> getIdentifyingAnnotations() {
+			return Collections.singleton(Entity.class);
+		}
+
+		@Override
+		public RepositoryInformation getRepositoryInformation() {
+			return null;
+		}
+
+		@Override
+		public Set> getResolvedAnnotations() {
+			return null;
+		}
+
+		@Override
+		public Set> getResolvedTypes() {
+			return null;
+		}
+
+		@Override
+		public ConfigurableListableBeanFactory getBeanFactory() {
+			return null;
+		}
+
+		@Override
+		public TypeIntrospector introspectType(String typeName) {
+			return null;
+		}
+
+		@Override
+		public IntrospectedBeanDefinition introspectBeanDefinition(String beanName) {
+			return null;
+		}
+	}
+}

From 22663503f10b592cbd87d2cd8400aca1a5ddac95 Mon Sep 17 00:00:00 2001
From: Diego Krupitza 
Date: Thu, 14 Jul 2022 19:55:07 +0200
Subject: [PATCH 253/821] 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
---
 .../query/JSqlParserQueryEnhancer.java        |  8 ++-
 .../jpa/repository/UserRepositoryTests.java   | 58 ++++++++++++-------
 .../query/QueryEnhancerUnitTests.java         | 38 ++++++++++++
 .../jpa/repository/sample/UserRepository.java | 34 ++++++-----
 4 files changed, 99 insertions(+), 39 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 1c6c42ae80..4b5d64fc51 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
@@ -26,6 +26,7 @@
 import net.sf.jsqlparser.schema.Column;
 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.select.OrderByElement;
 import net.sf.jsqlparser.statement.select.PlainSelect;
 import net.sf.jsqlparser.statement.select.Select;
@@ -82,7 +83,9 @@ private ParsedType detectParsedType() {
 		try {
 			Statement statement = CCJSqlParserUtil.parse(this.query.getQueryString());
 
-			if (statement instanceof Update) {
+			if (statement instanceof Insert) {
+				return ParsedType.INSERT;
+			} else if (statement instanceof Update) {
 				return ParsedType.UPDATE;
 			} else if (statement instanceof Delete) {
 				return ParsedType.DELETE;
@@ -475,10 +478,11 @@ public DeclaredQuery getQuery() {
 	 * 
  • {@code ParsedType.DELETE}: means the top level statement is {@link Delete}
  • *
  • {@code ParsedType.UPDATE}: means the top level statement is {@link Update}
  • *
  • {@code ParsedType.SELECT}: means the top level statement is {@link Select}
  • + *
  • {@code ParsedType.INSERT}: means the top level statement is {@link Insert}
  • * */ enum ParsedType { - DELETE, UPDATE, SELECT; + DELETE, UPDATE, SELECT, INSERT; } } 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 283e45161d..4f88fe896a 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 @@ -15,13 +15,14 @@ */ package org.springframework.data.jpa.repository; -import static java.util.Arrays.*; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.domain.Example.*; +import static org.springframework.data.domain.Example.of; import static org.springframework.data.domain.ExampleMatcher.*; -import static org.springframework.data.domain.Sort.Direction.*; -import static org.springframework.data.jpa.domain.Specification.*; +import static org.springframework.data.domain.Sort.Direction.ASC; +import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.jpa.domain.Specification.not; +import static org.springframework.data.jpa.domain.Specification.where; import static org.springframework.data.jpa.domain.sample.UserSpecifications.*; import jakarta.persistence.EntityManager; @@ -33,14 +34,7 @@ import jakarta.persistence.criteria.Root; import lombok.Data; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; @@ -54,14 +48,7 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; +import org.springframework.data.domain.*; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; @@ -2969,7 +2956,7 @@ void complexWithNativeStatement() { } @Test // GH-2607 - void containsWithCollection(){ + void containsWithCollection() { firstUser.getAttributes().add("cool"); firstUser.getAttributes().add("hip"); @@ -2986,6 +2973,35 @@ void containsWithCollection(){ assertThat(result).containsOnly(firstUser, secondUser); } + @Test // GH-2593 + void insertStatementModifyingQueryWorks() { + flushTestUsers(); + repository.insertNewUserWithNativeQuery(); + + List all = repository.findAll(); + assertThat(all) // + .isNotNull() // + .isNotEmpty() // + .hasSize(5) // + .map(User::getLastname) // + .contains("Gierke", "Arrasz", "Matthews", "raymond", "K"); + } + + @Test // GH-2593 + void insertStatementModifyingQueryWithParamsWorks() { + flushTestUsers(); + String testLastName = "TestLastName"; + repository.insertNewUserWithParamNativeQuery(testLastName); + + List all = repository.findAll(); + assertThat(all) // + .isNotNull() // + .isNotEmpty() // + .hasSize(5) // + .map(User::getLastname) // + .contains("Gierke", "Arrasz", "Matthews", "raymond", testLastName); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); 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 c879468633..bc9f200108 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 @@ -889,6 +889,44 @@ void multipleWithStatementsWorksWithJSQLParser() { assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); } + @ParameterizedTest // GH-2593 + @MethodSource("insertStatementIsProcessedSameAsDefaultSource") + void insertStatementIsProcessedSameAsDefault(String insertQuery) { + + StringQuery stringQuery = new StringQuery(insertQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + Sort sorting = Sort.by("day").descending(); + + // queryutils results + String queryUtilsDetectAlias = QueryUtils.detectAlias(insertQuery); + String queryUtilsProjection = QueryUtils.getProjection(insertQuery); + String queryUtilsCountQuery = QueryUtils.createCountQueryFor(insertQuery); + Set queryUtilsOuterJoinAlias = QueryUtils.getOuterJoinAliases(insertQuery); + + // direct access + assertThat(stringQuery.getAlias()).isEqualToIgnoringCase(queryUtilsDetectAlias); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase(queryUtilsProjection); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + // access over enhancer + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(queryUtilsCountQuery); + assertThat(queryEnhancer.applySorting(sorting)).isEqualTo(insertQuery); // cant check with queryutils result since + // query utils appens order by which is not + // supported by sql standard. + assertThat(queryEnhancer.getJoinAliases()).isEqualTo(queryUtilsOuterJoinAlias); + assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase(queryUtilsDetectAlias); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase(queryUtilsProjection); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + 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')") // + ); + } + public static Stream detectsJoinAliasesCorrectlySource() { return Stream.of( // 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 3236ddfa3f..b7bd9b5b97 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 @@ -18,27 +18,14 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.QueryHint; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Stream; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; +import org.springframework.data.domain.*; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.jpa.repository.QueryHints; +import org.springframework.data.jpa.repository.*; import org.springframework.data.jpa.repository.query.Procedure; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; @@ -55,6 +42,7 @@ * @author JyotirmoyVS * @author Greg Turnquist * @author Simon Paradies + * @author Diego Krupitza */ public interface UserRepository extends JpaRepository, JpaSpecificationExecutor, UserRepositoryCustom { @@ -679,6 +667,20 @@ List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter // GH-2607 List findByAttributesContains(String attribute); + // GH-2593 + @Modifying + @Query( + value = "INSERT INTO SD_User(id,active,age,firstname,lastname,emailAddress,DTYPE) VALUES (9999,true,23,'Diego','K','dk@email.com','User')", + nativeQuery = true) + void insertNewUserWithNativeQuery(); + + // GH-2593 + @Modifying + @Query( + value = "INSERT INTO SD_User(id,active,age,firstname,lastname,emailAddress,DTYPE) VALUES (9999,true,23,'Diego',:lastname,'dk@email.com','User')", + nativeQuery = true) + void insertNewUserWithParamNativeQuery(@Param("lastname") String lastname); + interface RolesAndFirstname { String getFirstname(); From cdcf88fda9b2b90199ea2525435ad534ff6f2c23 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 27 Sep 2022 11:24:55 -0500 Subject: [PATCH 254/821] Improve reference documentation. Closes #2647. --- src/main/asciidoc/faq.adoc | 16 ----- src/main/asciidoc/glossary.adoc | 2 +- src/main/asciidoc/jpa.adoc | 116 ++++++++++++++++---------------- 3 files changed, 60 insertions(+), 74 deletions(-) diff --git a/src/main/asciidoc/faq.adoc b/src/main/asciidoc/faq.adoc index 72bab22742..5a2f672738 100644 --- a/src/main/asciidoc/faq.adoc +++ b/src/main/asciidoc/faq.adoc @@ -22,22 +22,6 @@ I'd like to get more detailed logging information on what methods are called ins ---- -[[faq.infrastructure]] -== Infrastructure - -[qanda] -Currently I have implemented a repository layer based on `HibernateDaoSupport`. I create a `SessionFactory` by using Spring's `AnnotationSessionFactoryBean`. How do I get Spring Data repositories working in this environment? :: You have to replace `AnnotationSessionFactoryBean` with the `HibernateJpaSessionFactoryBean`, as follows: -+ -.Looking up a `SessionFactory` from a `HibernateEntityManagerFactory` -==== -[source, xml] ----- - - - ----- -==== - [[faq.auditing]] == Auditing diff --git a/src/main/asciidoc/glossary.adoc b/src/main/asciidoc/glossary.adoc index a0d9ea6c7e..7f7c354b2a 100644 --- a/src/main/asciidoc/glossary.adoc +++ b/src/main/asciidoc/glossary.adoc @@ -18,4 +18,4 @@ Hibernate :: Object relational mapper implementing JPA - link:$$https://hibernat JPA :: Jakarta Persistence API -Spring :: Java application framework - link:$$https://projects.spring.io/spring-framework$$[https://projects.spring.io/spring-framework] +Spring :: Java application framework - link:$$https://spring.io/projects/spring-framework$$[https://spring.io/projects/spring-framework] diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 3688ca1a50..e7a79601cd 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -11,48 +11,9 @@ This section describes the basics of configuring Spring Data JPA through either: * "`<>`" (XML configuration) * "`<>`" (Java configuration) -[[jpa.namespace]] -=== Spring Namespace - -The JPA module of Spring Data contains a custom namespace that allows defining repository beans. It also contains certain features and element attributes that are special to JPA. Generally, the JPA repositories can be set up by using the `repositories` element, as shown in the following example: - -.Setting up JPA repositories by using the namespace -==== -[source, xml] ----- - - - - - - ----- -==== - -Using the `repositories` element looks up Spring Data repositories as described in "`<>`". Beyond that, it activates persistence exception translation for all beans annotated with `@Repository`, to let exceptions being thrown by the JPA persistence providers be converted into Spring's `DataAccessException` hierarchy. - -[[jpa.namespace.custom-namespace-attributes]] -==== Custom Namespace Attributes -Beyond the default attributes of the `repositories` element, the JPA namespace offers additional attributes to let you gain more detailed control over the setup of the repositories: - -.Custom JPA-specific attributes of the `repositories` element -[options = "autowidth"] -|=============== -|`entity-manager-factory-ref`|Explicitly wire the `EntityManagerFactory` to be used with the repositories being detected by the `repositories` element. Usually used if multiple `EntityManagerFactory` beans are used within the application. If not configured, Spring Data automatically looks up the `EntityManagerFactory` bean with the name `entityManagerFactory` in the `ApplicationContext`. -|`transaction-manager-ref`|Explicitly wire the `PlatformTransactionManager` to be used with the repositories being detected by the `repositories` element. Usually only necessary if multiple transaction managers or `EntityManagerFactory` beans have been configured. Default to a single defined `PlatformTransactionManager` inside the current `ApplicationContext`. -|=============== - -NOTE: Spring Data JPA requires a `PlatformTransactionManager` bean named `transactionManager` to be present if no explicit `transaction-manager-ref` is defined. - [[jpa.java-config]] === Annotation-based Configuration -The Spring Data JPA repositories support can be activated not only through an XML namespace but also by using an annotation through JavaConfig, as shown in the following example: +The Spring Data JPA repositories support can be activated through both JavaConfig as well as a custom XML namespace, as shown in the following example: .Spring Data JPA repositories using JavaConfig ==== @@ -97,6 +58,47 @@ NOTE: You must create `LocalContainerEntityManagerFactoryBean` and not `EntityMa The preceding configuration class sets up an embedded HSQL database by using the `EmbeddedDatabaseBuilder` API of `spring-jdbc`. Spring Data then sets up an `EntityManagerFactory` and uses Hibernate as the sample persistence provider. The last infrastructure component declared here is the `JpaTransactionManager`. Finally, the example activates Spring Data JPA repositories by using the `@EnableJpaRepositories` annotation, which essentially carries the same attributes as the XML namespace. If no base package is configured, it uses the one in which the configuration class resides. +[[jpa.namespace]] +=== Spring Namespace + +The JPA module of Spring Data contains a custom namespace that allows defining repository beans. It also contains certain features and element attributes that are special to JPA. Generally, the JPA repositories can be set up by using the `repositories` element, as shown in the following example: + +.Setting up JPA repositories by using the namespace +==== +[source, xml] +---- + + + + + + +---- +==== + +TIP: Which is better, JavaConfig or XML? XML is how Spring was configured long ago. In today's era of fast-growing Java, record types, annotations, and more, new projects typically use as much pure Java as possible. While there is no immediate plan to remove XML support, some of the newest features MAY not be available through XML. + +Using the `repositories` element looks up Spring Data repositories as described in "`<>`". Beyond that, it activates persistence exception translation for all beans annotated with `@Repository`, to let exceptions being thrown by the JPA persistence providers be converted into Spring's `DataAccessException` hierarchy. + +[[jpa.namespace.custom-namespace-attributes]] +==== Custom Namespace Attributes +Beyond the default attributes of the `repositories` element, the JPA namespace offers additional attributes to let you gain more detailed control over the setup of the repositories: + +.Custom JPA-specific attributes of the `repositories` element +[options = "autowidth"] +|=============== +|`entity-manager-factory-ref`|Explicitly wire the `EntityManagerFactory` to be used with the repositories being detected by the `repositories` element. Usually used if multiple `EntityManagerFactory` beans are used within the application. If not configured, Spring Data automatically looks up the `EntityManagerFactory` bean with the name `entityManagerFactory` in the `ApplicationContext`. +|`transaction-manager-ref`|Explicitly wire the `PlatformTransactionManager` to be used with the repositories being detected by the `repositories` element. Usually only necessary if multiple transaction managers or `EntityManagerFactory` beans have been configured. Default to a single defined `PlatformTransactionManager` inside the current `ApplicationContext`. +|=============== + +NOTE: Spring Data JPA requires a `PlatformTransactionManager` bean named `transactionManager` to be present if no explicit `transaction-manager-ref` is defined. + [[jpa.bootstrap-mode]] === Bootstrap Mode @@ -274,6 +276,23 @@ Using `distinct` sometimes requires writing the query by hand and using `@Query` to capture the result set. ==== +[[jpa.query-methods.named-queries.annotation-based-configuration]] +==== Annotation-based Configuration +Annotation-based configuration has the advantage of not needing another configuration file to be edited, lowering maintenance effort. You pay for that benefit by the need to recompile your domain class for every new query declaration. + +.Annotation-based named query configuration +==== +[source, java] +---- +@Entity +@NamedQuery(name = "User.findByEmailAddress", + query = "select u from User u where u.emailAddress = ?1") +public class User { + +} +---- +==== + [[jpa.query-methods.named-queries]] === Using JPA Named Queries @@ -295,23 +314,6 @@ To use XML configuration, add the necessary `` element to the `or The query has a special name that is used to resolve it at runtime. -[[jpa.query-methods.named-queries.annotation-based-configuration]] -==== Annotation-based Configuration -Annotation-based configuration has the advantage of not needing another configuration file to be edited, lowering maintenance effort. You pay for that benefit by the need to recompile your domain class for every new query declaration. - -.Annotation-based named query configuration -==== -[source, java] ----- -@Entity -@NamedQuery(name = "User.findByEmailAddress", - query = "select u from User u where u.emailAddress = ?1") -public class User { - -} ----- -==== - [[jpa.query-methods.named-queries.declaring-interfaces]] ==== Declaring Interfaces To allow these named queries, specify the `UserRepositoryWithRewriter` as follows: From 3a29011512511debd27aeedfe51ce096f24c7048 Mon Sep 17 00:00:00 2001 From: Geoffrey Deremetz Date: Fri, 23 Sep 2022 16:50:51 +0200 Subject: [PATCH 255/821] Made JSqlParserQueryEnhancer aware of MERGE statements. Closes #2641. --- .../query/JSqlParserQueryEnhancer.java | 7 ++++++- .../jpa/repository/UserRepositoryTests.java | 17 +++++++++++++++++ .../query/QueryEnhancerUnitTests.java | 13 +++++++++++++ .../jpa/repository/sample/UserRepository.java | 11 +++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) 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 4b5d64fc51..48cdd18730 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 @@ -26,6 +26,7 @@ import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.select.OrderByElement; import net.sf.jsqlparser.statement.select.PlainSelect; @@ -57,6 +58,7 @@ * * @author Diego Krupitza * @author Greg Turnquist + * @author Geoffrey Deremetz * @since 2.7.0 */ public class JSqlParserQueryEnhancer implements QueryEnhancer { @@ -91,6 +93,8 @@ private ParsedType detectParsedType() { return ParsedType.DELETE; } else if (statement instanceof Select) { return ParsedType.SELECT; + } else if (statement instanceof Merge) { + return ParsedType.MERGE; } else { return ParsedType.SELECT; } @@ -479,10 +483,11 @@ public DeclaredQuery getQuery() { *
  • {@code ParsedType.UPDATE}: means the top level statement is {@link Update}
  • *
  • {@code ParsedType.SELECT}: means the top level statement is {@link Select}
  • *
  • {@code ParsedType.INSERT}: means the top level statement is {@link Insert}
  • + *
  • {@code ParsedType.MERGE}: means the top level statement is {@link Merge}
  • * */ enum ParsedType { - DELETE, UPDATE, SELECT, INSERT; + DELETE, UPDATE, SELECT, INSERT, MERGE; } } 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 4f88fe896a..955dc32da2 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 @@ -83,6 +83,7 @@ * @author Diego Krupitza * @author Daniel Shuy * @author Simon Paradies + * @author Geoffrey Deremetz */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @@ -3002,6 +3003,22 @@ void insertStatementModifyingQueryWithParamsWorks() { .contains("Gierke", "Arrasz", "Matthews", "raymond", testLastName); } + @Test // GH-2641 + void mergeWithNativeStatement() { + + flushTestUsers(); + + Optional byIdUser = repository.findById(firstUser.getId()); + assertThat(byIdUser).isPresent().map(User::getAge).get().isEqualTo(28); + + // when + repository.mergeNativeStatement(); + + // then + Optional afterUpdate = repository.findById(firstUser.getId()); + assertThat(afterUpdate).isPresent().map(User::getAge).get().isEqualTo(30); + } + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); 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 bc9f200108..8aa76ffc84 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 @@ -36,6 +36,7 @@ * Unit tests for {@link QueryEnhancer}. * * @author Diego Krupitza + * @author Geoffrey Deremetz */ class QueryEnhancerUnitTests { @@ -920,6 +921,18 @@ void insertStatementIsProcessedSameAsDefault(String insertQuery) { assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); } + @Test // GH-2641 + void mergeStatementWorksWithJSqlParser() { + String query = "merge into a using (select id, value from b) query on (a.id = query.id) when matched then update set a.value = value"; + StringQuery stringQuery = new StringQuery(query, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNull(); + assertThat(queryEnhancer.getProjection()).isEmpty(); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + public static Stream insertStatementIsProcessedSameAsDefaultSource() { return Stream.of( // Arguments.of("INSERT INTO FOO(A) VALUES('A')"), // 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 b7bd9b5b97..df67e8beb7 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 @@ -43,6 +43,7 @@ * @author Greg Turnquist * @author Simon Paradies * @author Diego Krupitza + * @author Geoffrey Deremetz */ public interface UserRepository extends JpaRepository, JpaSpecificationExecutor, UserRepositoryCustom { @@ -681,6 +682,16 @@ List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter nativeQuery = true) void insertNewUserWithParamNativeQuery(@Param("lastname") String lastname); + // 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", + nativeQuery = true) + int mergeNativeStatement(); + interface RolesAndFirstname { String getFirstname(); From 7b69aa6dd5a16da64cd5d227ef72333dcb8362bb Mon Sep 17 00:00:00 2001 From: Geoffrey Deremetz Date: Fri, 23 Sep 2022 21:42:45 +0200 Subject: [PATCH 256/821] Detect alias for merge statement for JSqlParserQueryEnhancer. Related: #2641. --- .../query/JSqlParserQueryEnhancer.java | 92 ++++++++++++------- .../query/QueryEnhancerUnitTests.java | 20 +++- 2 files changed, 75 insertions(+), 37 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 48cdd18730..b55e4e2ac5 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 @@ -18,6 +18,20 @@ 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 net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; @@ -26,8 +40,8 @@ import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.delete.Delete; -import net.sf.jsqlparser.statement.merge.Merge; 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; @@ -39,20 +53,6 @@ import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.values.ValuesStatement; -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; - /** * The implementation of {@link QueryEnhancer} using JSqlParser. * @@ -304,24 +304,28 @@ public String detectAlias() { @Nullable private String detectAlias(String query) { - if (this.parsedType != ParsedType.SELECT) { - return null; - } - - Select selectStatement = parseSelectStatement(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 + alias since: + * ValuesStatement has no alias + * SetOperation can have multiple alias for each operation item + */ + if (!(selectStatement.getSelectBody() instanceof PlainSelect)) { + return null; + } - /* - 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 - */ - if (!(selectStatement.getSelectBody() instanceof PlainSelect)) { - return null; + PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); + return detectAlias(selectBody); } - PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); - return detectAlias(selectBody); + return null; } /** @@ -332,7 +336,7 @@ For all the other types ({@link ValuesStatement} and {@link SetOperationList}) i * @return Might return {@literal null}. */ @Nullable - private static String detectAlias(PlainSelect selectBody) { + private String detectAlias(PlainSelect selectBody) { if (selectBody.getFromItem() == null) { return null; @@ -342,6 +346,18 @@ private static String detectAlias(PlainSelect selectBody) { return alias == null ? null : alias.getName(); } + /** + * Resolves the alias for the given {@link Merge} statement. + * + * @param mergeStatement must not be {@literal null}. + * @return Might return {@literal null}. + */ + @Nullable + private String detectAlias(Merge mergeStatement) { + Alias alias = mergeStatement.getUsingAlias(); + return alias == null ? null : alias.getName(); + } + @Override public String createCountQueryFor(@Nullable String countProjection) { @@ -449,15 +465,25 @@ public Set getJoinAliases() { * @param query the query to parse * @return the parsed query */ - private static Select parseSelectStatement(String query) { + private T parseSelectStatement(String query, Class classOfT) { try { - return (Select) CCJSqlParserUtil.parse(query); + return classOfT.cast(CCJSqlParserUtil.parse(query)); } catch (JSQLParserException e) { throw new IllegalArgumentException("The query you provided is not a valid SQL Query", e); } } + /** + * Parses a query string with JSqlParser. + * + * @param query the query to parse + * @return the parsed query + */ + private Select parseSelectStatement(String query) { + return parseSelectStatement(query, Select.class); + } + /** * Checks whether a given projection only contains a single column definition (aka without functions, etc.) * 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 8aa76ffc84..febfbd9470 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 @@ -921,14 +921,17 @@ void insertStatementIsProcessedSameAsDefault(String insertQuery) { assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); } - @Test // GH-2641 - void mergeStatementWorksWithJSqlParser() { - String query = "merge into a using (select id, value from b) query on (a.id = query.id) when matched then update set a.value = value"; + @ParameterizedTest // GH-2641 + @MethodSource("mergeStatementWorksWithJSqlParserSource") + void mergeStatementWorksWithJSqlParser(String query, String alias) { StringQuery stringQuery = new StringQuery(query, true); QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + assertThat(queryEnhancer.detectAlias()).isEqualTo(alias); + assertThat(QueryUtils.detectAlias(query)).isNull(); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); - assertThat(queryEnhancer.detectAlias()).isNull(); + assertThat(queryEnhancer.detectAlias()).isEqualTo(alias); assertThat(queryEnhancer.getProjection()).isEmpty(); assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); } @@ -940,6 +943,15 @@ public static Stream insertStatementIsProcessedSameAsDefaultSource() ); } + 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"), + Arguments.of( + "merge into a using (select id2, value from b) on (id = id2) when matched then update set a.value = value", + null)); + } + public static Stream detectsJoinAliasesCorrectlySource() { return Stream.of( // From 4b267006aa93345121cda9feca9b8da94746341d Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 27 Sep 2022 17:44:42 -0500 Subject: [PATCH 257/821] 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 b55e4e2ac5..0efa084c1e 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.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 String applySorting(Sort sort, @Nullable String alias) { /** * 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 String detectAlias() { 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 @@ private String detectAlias(PlainSelect selectBody) { */ @Nullable private String detectAlias(Merge mergeStatement) { + Alias alias = mergeStatement.getUsingAlias(); return alias == null ? null : alias.getName(); } @@ -382,6 +374,7 @@ public String createCountQueryFor(@Nullable String countProjection) { 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 String createCountQueryFor(@Nullable String countProjection) { List selectItems = selectBody.getSelectItems(); if (onlyASingleColumnProjection(selectItems)) { + SelectExpressionItem singleProjection = (SelectExpressionItem) selectItems.get(0); Column column = (Column) singleProjection.getExpression(); @@ -440,6 +434,7 @@ public String getProjection() { 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 955dc32da2..4d32e8b5c0 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 @@ void mergeWithNativeStatement() { 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 febfbd9470..c082840c2a 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 @@ void insertStatementIsProcessedSameAsDefault(String insertQuery) { @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 @@ void mergeStatementWorksWithJSqlParser(String query, String alias) { } 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 @@ public static Stream insertStatementIsProcessedSameAsDefaultSource() } 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 df67e8beb7..7bf350f9f2 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 @@ List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter // 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(); From 91cd84e7061c0c655128624d34a0ff41d1f58d7a Mon Sep 17 00:00:00 2001 From: Chris Fraser Date: Tue, 28 Jun 2022 13:59:27 -0400 Subject: [PATCH 258/821] Handle multiline subquery removal. Closes #2582. Related: #2563, #2557, #2603 --- .../data/jpa/repository/query/QueryUtils.java | 3 +- .../repository/query/QueryUtilsUnitTests.java | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 5c1fd5fa24..c4be6aee62 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -79,6 +79,7 @@ * @author Darin Manica * @author Simon Paradies * @author Vladislav Yukharin + * @author Chris Fraser */ public abstract class QueryUtils { @@ -104,7 +105,7 @@ public abstract class QueryUtils { private static final Pattern ALIAS_MATCH; private static final Pattern COUNT_MATCH; private static final Pattern STARTS_WITH_PAREN = Pattern.compile("^\\s*\\("); - private static final Pattern PARENS_TO_REMOVE = Pattern.compile("(\\(.*\\bfrom\\b[^)]+\\))", CASE_INSENSITIVE); + private static final Pattern PARENS_TO_REMOVE = Pattern.compile("(\\(.*\\bfrom\\b[^)]+\\))", CASE_INSENSITIVE | DOTALL | MULTILINE); private static final Pattern PROJECTION_CLAUSE = Pattern.compile("select\\s+(?:distinct\\s+)?(.+)\\s+from", Pattern.CASE_INSENSITIVE); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 5b65b647f0..4f4118e4f8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -45,6 +45,7 @@ * @author Greg Turnquist * @author Jędrzej Biedrzycki * @author Darin Manica + * @author Chris Fraser */ class QueryUtilsUnitTests { @@ -178,6 +179,56 @@ void testRemoveSubqueries() throws Exception { .isEqualTo("(select u from User u where not exists ( ))"); } + @Test // GH-2581 + void testRemoveMultilineSubqueries() { + + assertThat(normalizeWhitespace(removeSubqueries("select u from User u\n" + + " where not exists (\n" + + " from User u2\n" + + " )"))) + .isEqualTo("select u from User u where not exists"); + assertThat(normalizeWhitespace(removeSubqueries("(\n" + + " select u from User u \n" + + " where not exists (\n" + + " from User u2\n" + + " )\n" + + ")"))) + .isEqualTo("( select u from User u where not exists )"); + assertThat(normalizeWhitespace( + removeSubqueries("select u from User u \n" + + " where not exists (\n" + + " from User u2 \n" + + " where not exists (\n" + + " from User u3\n" + + " )\n" + + " )"))) + .isEqualTo("select u from User u where not exists"); + assertThat(normalizeWhitespace( + removeSubqueries("select u from User u \n" + + " where not exists (\n" + + " (\n" + + " from User u2 \n" + + " where not exists (\n" + + " from User u3\n" + + " )\n" + + " )\n" + + " )"))) + .isEqualTo("select u from User u where not exists ( )"); + assertThat(normalizeWhitespace( + removeSubqueries("(\n" + + " select u from User u \n" + + " where not exists (\n" + + " (\n" + + " from User u2 \n" + + " where not exists (\n" + + " from User u3\n" + + " )\n" + + " )\n" + + " )\n" + + ")"))) + .isEqualTo("( select u from User u where not exists ( ) )"); + } + private String normalizeWhitespace(String s) { Matcher matcher = MULTI_WHITESPACE.matcher(s); if (matcher.find()) { From f7737014944e606686ca5111574229426ec4484b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 28 Sep 2022 16:30:38 -0500 Subject: [PATCH 259/821] Polishing. Also added more test cases from related pull requests. Closes #2582. Related: #2563, #2557, #2603 --- .../data/jpa/repository/query/QueryUtils.java | 22 +--- .../repository/query/QueryUtilsUnitTests.java | 121 +++++++++++------- 2 files changed, 80 insertions(+), 63 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index c4be6aee62..cdd49c6241 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -18,23 +18,10 @@ import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.*; import static java.util.regex.Pattern.*; -import jakarta.persistence.EntityManager; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Parameter; -import jakarta.persistence.Query; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.Expression; -import jakarta.persistence.criteria.Fetch; -import jakarta.persistence.criteria.From; -import jakarta.persistence.criteria.Join; -import jakarta.persistence.criteria.JoinType; -import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.*; +import jakarta.persistence.criteria.*; +import jakarta.persistence.metamodel.*; import jakarta.persistence.metamodel.Attribute.PersistentAttributeType; -import jakarta.persistence.metamodel.Bindable; -import jakarta.persistence.metamodel.ManagedType; -import jakarta.persistence.metamodel.PluralAttribute; -import jakarta.persistence.metamodel.SingularAttribute; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; @@ -105,7 +92,8 @@ public abstract class QueryUtils { private static final Pattern ALIAS_MATCH; private static final Pattern COUNT_MATCH; private static final Pattern STARTS_WITH_PAREN = Pattern.compile("^\\s*\\("); - private static final Pattern PARENS_TO_REMOVE = Pattern.compile("(\\(.*\\bfrom\\b[^)]+\\))", CASE_INSENSITIVE | DOTALL | MULTILINE); + private static final Pattern PARENS_TO_REMOVE = Pattern.compile("(\\(.*\\bfrom\\b[^)]+\\))", + CASE_INSENSITIVE | DOTALL | MULTILINE); private static final Pattern PROJECTION_CLAUSE = Pattern.compile("select\\s+(?:distinct\\s+)?(.+)\\s+from", Pattern.CASE_INSENSITIVE); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 4f4118e4f8..431569d1f3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.data.jpa.repository.query.QueryUtils.*; import java.util.Collections; @@ -46,6 +47,7 @@ * @author Jędrzej Biedrzycki * @author Darin Manica * @author Chris Fraser + * @author Michał Pachucki */ class QueryUtilsUnitTests { @@ -182,55 +184,82 @@ void testRemoveSubqueries() throws Exception { @Test // GH-2581 void testRemoveMultilineSubqueries() { - assertThat(normalizeWhitespace(removeSubqueries("select u from User u\n" - + " where not exists (\n" - + " from User u2\n" - + " )"))) - .isEqualTo("select u from User u where not exists"); - assertThat(normalizeWhitespace(removeSubqueries("(\n" - + " select u from User u \n" - + " where not exists (\n" - + " from User u2\n" - + " )\n" - + ")"))) - .isEqualTo("( select u from User u where not exists )"); - assertThat(normalizeWhitespace( - removeSubqueries("select u from User u \n" - + " where not exists (\n" - + " from User u2 \n" - + " where not exists (\n" - + " from User u3\n" - + " )\n" - + " )"))) - .isEqualTo("select u from User u where not exists"); - assertThat(normalizeWhitespace( - removeSubqueries("select u from User u \n" - + " where not exists (\n" - + " (\n" - + " from User u2 \n" - + " where not exists (\n" - + " from User u3\n" - + " )\n" - + " )\n" - + " )"))) - .isEqualTo("select u from User u where not exists ( )"); - assertThat(normalizeWhitespace( - removeSubqueries("(\n" - + " select u from User u \n" - + " where not exists (\n" - + " (\n" - + " from User u2 \n" - + " where not exists (\n" - + " from User u3\n" - + " )\n" - + " )\n" - + " )\n" - + ")"))) - .isEqualTo("( select u from User u where not exists ( ) )"); + assertThat(normalizeWhitespace(removeSubqueries("select u from User u\n" // + + " where not exists (\n" // + + " from User u2\n" // + + " )"))).isEqualTo("select u from User u where not exists"); + + assertThat(normalizeWhitespace(removeSubqueries("(\n" // + + " select u from User u \n" // + + " where not exists (\n" // + + " from User u2\n" // + + " )\n" // + + ")"))).isEqualTo("( select u from User u where not exists )"); + + assertThat(normalizeWhitespace(removeSubqueries("select u from User u \n" // + + " where not exists (\n" // + + " from User u2 \n" // + + " where not exists (\n" // + + " from User u3\n" // + + " )\n" // + + " )"))).isEqualTo("select u from User u where not exists"); + + assertThat(normalizeWhitespace(removeSubqueries("select u from User u \n" // + + " where not exists (\n" // + + " (\n" // + + " from User u2 \n" // + + " where not exists (\n" // + + " from User u3\n" // + + " )\n" // + + " )\n" // + + " )"))).isEqualTo("select u from User u where not exists ( )"); + + assertThat(normalizeWhitespace(removeSubqueries("(\n" // + + " select u from User u \n" // + + " where not exists (\n" // + + " (\n" // + + " from User u2 \n" // + + " where not exists (\n" // + + " from User u3\n" // + + " )\n" // + + " )\n" // + + " )\n" // + + ")"))).isEqualTo("( select u from User u where not exists ( ) )"); + } + + @Test // GH-2557 + void applySortingAccountsForNewlinesInSubselect() { + + Sort sort = Sort.by(Order.desc("age")); + + assertThat(QueryUtils.applySorting("select u\n" + // + "from user u\n" + // + "where exists (select u2\n" + // + "from user u2\n" + // + ")\n" + // + "", sort)).isEqualTo("select u\n" + // + "from user u\n" + // + "where exists (select u2\n" + // + "from user u2\n" + // + ")\n" + // + " order by u.age desc"); + } + + @Test // GH-2563 + void aliasDetectionProperlyHandlesNewlinesInSubselects() { + + assertThat(detectAlias("SELECT o\n" + // + "FROM Order o\n" + // + "AND EXISTS(SELECT 1\n" + // + "FROM Vehicle vehicle\n" + // + "WHERE vehicle.vehicleOrderId = o.id\n" + // + "AND LOWER(COALESCE(vehicle.make, '')) LIKE :query)")).isEqualTo("o"); } private String normalizeWhitespace(String s) { + Matcher matcher = MULTI_WHITESPACE.matcher(s); + if (matcher.find()) { return matcher.replaceAll(" ").trim(); } From fea2a1a515f5bd9f87604ac80abc0d7b76054633 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 29 Sep 2022 11:51:56 -0500 Subject: [PATCH 260/821] Update class-based DTO notes in reference docs. Closes #2558. Related: #2635. --- src/main/asciidoc/repository-projections-dto-limitations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/asciidoc/repository-projections-dto-limitations.adoc b/src/main/asciidoc/repository-projections-dto-limitations.adoc index 44ab3a6bbf..bc2a33df14 100644 --- a/src/main/asciidoc/repository-projections-dto-limitations.adoc +++ b/src/main/asciidoc/repository-projections-dto-limitations.adoc @@ -1 +1 @@ -NOTE: Class based projections do not work with native queries. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] +NOTE: Class-based projections with JPQL is limited to *constructor expressions* in your JPQL expression, e.g. `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. (Note the usage of a FQDN for the DTO type!) This JPQL expression can be used in `@Query` annotations as well where you define any named queries. And it's important to point out that class-based projections do not work with native queries AT ALL. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] From 3a3c9372c7fb7889c13a926be85607fbe5e88b33 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 5 Oct 2022 11:39:03 +0200 Subject: [PATCH 261/821] Follow API changes in Spring Framework Closes: #2658 See: spring-projects/spring-data-build#1809 --- .../JpaRepositoryRegistrationAotProcessorUnitTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java index 5204de044c..a267cf4dae 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java @@ -33,6 +33,7 @@ import org.springframework.core.annotation.MergedAnnotation; import org.springframework.data.aot.AotRepositoryContext; import org.springframework.data.repository.core.RepositoryInformation; +import org.springframework.javapoet.ClassName; /** * @author Christoph Strobl @@ -42,7 +43,7 @@ class JpaRepositoryRegistrationAotProcessorUnitTests { @Test // GH-2628 void aotProcessorMustNotRegisterDomainTypes() { - GenerationContext ctx = new DefaultGenerationContext(new ClassNameGenerator(Object.class), + GenerationContext ctx = new DefaultGenerationContext(new ClassNameGenerator(ClassName.OBJECT), new InMemoryGeneratedFiles()); new JpaRepositoryConfigExtension.JpaRepositoryRegistrationAotProcessor() @@ -59,7 +60,7 @@ public Set> getResolvedTypes() { @Test // GH-2628 void aotProcessorMustNotRegisterAnnotations() { - GenerationContext ctx = new DefaultGenerationContext(new ClassNameGenerator(Object.class), + GenerationContext ctx = new DefaultGenerationContext(new ClassNameGenerator(ClassName.OBJECT), new InMemoryGeneratedFiles()); new JpaRepositoryConfigExtension.JpaRepositoryRegistrationAotProcessor() From c2073d51cf2d5a8e3d2cd0ad086d4539c115ba9a Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 11 Oct 2022 11:40:05 +0200 Subject: [PATCH 262/821] Adapt to changed AOT packages in Spring Data Commons. Closes #2661 --- .../jpa/{ => repository}/aot/JpaRuntimeHints.java | 13 ++++++++----- .../config/JpaRepositoryConfigExtension.java | 4 ++-- .../main/resources/META-INF/spring/aot.factories | 2 +- .../aot/JpaRuntimeHintsUnitTests.java | 7 +++++-- ...RepositoryRegistrationAotProcessorUnitTests.java | 2 +- 5 files changed, 17 insertions(+), 11 deletions(-) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/{ => repository}/aot/JpaRuntimeHints.java (88%) rename spring-data-jpa/src/test/java/org/springframework/data/jpa/{ => repository}/aot/JpaRuntimeHintsUnitTests.java (94%) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java similarity index 88% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java index a8026d6eb2..0f4a4a7689 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/aot/JpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.aot; +package org.springframework.data.jpa.repository.aot; import java.util.Arrays; @@ -29,10 +29,12 @@ import org.springframework.util.ClassUtils; /** + * Runtime hints for JPA AOT processing. + * * @author Christoph Strobl * @since 3.0 */ -public class JpaRuntimeHints implements RuntimeHintsRegistrar { +class JpaRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { @@ -49,9 +51,10 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) .withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); hints.reflection().registerTypes(Arrays.asList( // - TypeReference.of(AuditingBeanFactoryPostProcessor.class), // - TypeReference.of(AuditingEntityListener.class)), - hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); + TypeReference.of(AuditingBeanFactoryPostProcessor.class), // + TypeReference.of(AuditingEntityListener.class)), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS)); } hints.reflection().registerType(TypeReference.of(SimpleJpaRepository.class), diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index e06e6abb0f..4f4317f77d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -46,13 +46,13 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.dao.DataAccessException; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; -import org.springframework.data.aot.AotRepositoryContext; -import org.springframework.data.aot.RepositoryRegistrationAotProcessor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.support.DefaultJpaContext; import org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor; import org.springframework.data.jpa.repository.support.JpaEvaluationContextExtension; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; +import org.springframework.data.repository.aot.AotRepositoryContext; +import org.springframework.data.repository.aot.RepositoryRegistrationAotProcessor; import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; import org.springframework.data.repository.config.RepositoryConfigurationSource; diff --git a/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories b/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories index 4363dcaeb8..50d5fc795e 100644 --- a/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories +++ b/spring-data-jpa/src/main/resources/META-INF/spring/aot.factories @@ -1,2 +1,2 @@ org.springframework.aot.hint.RuntimeHintsRegistrar=\ - org.springframework.data.jpa.aot.JpaRuntimeHints + org.springframework.data.jpa.repository.aot.JpaRuntimeHints diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHintsUnitTests.java similarity index 94% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHintsUnitTests.java index c992375198..c457ffcf80 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/aot/JpaRuntimeHintsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHintsUnitTests.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.aot; +package org.springframework.data.jpa.repository.aot; import static org.assertj.core.api.AssertionsForClassTypes.*; -import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection; +import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.*; import org.junit.jupiter.api.Test; + import org.springframework.aot.hint.RuntimeHints; import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect; import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; @@ -26,6 +27,8 @@ import org.springframework.data.jpa.util.HidingClassLoader; /** + * Unit tests for {@link JpaRuntimeHints}. + * * @author Christoph Strobl */ class JpaRuntimeHintsUnitTests { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java index a267cf4dae..7b2ade3424 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java @@ -31,7 +31,7 @@ import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.annotation.MergedAnnotation; -import org.springframework.data.aot.AotRepositoryContext; +import org.springframework.data.repository.aot.AotRepositoryContext; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.javapoet.ClassName; From 133493b99c22c5dfd5be6def5b6f03bb8249a7af Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 13 Oct 2022 17:24:03 +0200 Subject: [PATCH 263/821] Prepare 3.0 RC1 (2022.0.0). See #2638 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 5ba0fde025..0604fc8477 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-SNAPSHOT + 3.0.0-RC1 @@ -35,7 +35,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-SNAPSHOT + 3.0.0-RC1 0.10.3 org.hibernate @@ -224,8 +224,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From 5d2c8be54e68f861b44718cf2e4d2f6faac4196b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 13 Oct 2022 17:24:25 +0200 Subject: [PATCH 264/821] Release version 3.0 RC1 (2022.0.0). See #2638 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 0604fc8477..7018dad9ad 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-RC1 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 540834bb60..a4689278de 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-SNAPSHOT + 3.0.0-RC1 org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-RC1 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index bba8571672..1e05a48bcf 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-RC1 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 3ad9c39a2a..dddfb42e1a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-SNAPSHOT + 3.0.0-RC1 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-RC1 ../pom.xml From 3121d12b9cebf42336394cb787395af3a9d01420 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 13 Oct 2022 17:31:15 +0200 Subject: [PATCH 265/821] Prepare next development iteration. See #2638 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 7018dad9ad..0604fc8477 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-RC1 + 3.0.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index a4689278de..540834bb60 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-RC1 + 3.0.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.0.0-RC1 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 1e05a48bcf..bba8571672 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-RC1 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index dddfb42e1a..3ad9c39a2a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-RC1 + 3.0.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-RC1 + 3.0.0-SNAPSHOT ../pom.xml From 19c4b04bc4429ac7d2297ce69515eb2e96dab280 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 13 Oct 2022 17:31:16 +0200 Subject: [PATCH 266/821] After release cleanups. See #2638 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 0604fc8477..5ba0fde025 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-RC1 + 3.0.0-SNAPSHOT @@ -35,7 +35,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-RC1 + 3.0.0-SNAPSHOT 0.10.3 org.hibernate @@ -224,8 +224,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 0754fecd73679b928f277a1a1eb9172e4fbdbe43 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 19 Oct 2022 14:49:59 +0200 Subject: [PATCH 267/821] Add missing reflection hint for `QuerydslJpaPredicateExecutor`. Closes: #2673 Original pull request: #2674 --- .../data/jpa/repository/aot/JpaRuntimeHints.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java index 0f4a4a7689..c06e6f1b5b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java @@ -24,7 +24,10 @@ import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.support.QuerydslJpaPredicateExecutor; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.querydsl.QuerydslUtils; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; @@ -62,5 +65,12 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) // needs to present for evaluating default attribute values in JpaQueryMethod hints.reflection().registerType(Query.class, hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); + + if(QuerydslUtils.QUERY_DSL_PRESENT) { + + hints.reflection().registerType(QuerydslJpaPredicateExecutor.class, + hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS) + .onReachableType(QuerydslPredicateExecutor.class)); + } } } From e197ea9d08ce9d11a12101a77006cb946f3a2db8 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 20 Oct 2022 15:53:27 +0200 Subject: [PATCH 268/821] Polishing. Reformat code. See: #2673 Original pull request: #2674 --- .../data/jpa/repository/aot/JpaRuntimeHints.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java index c06e6f1b5b..65bb6e357e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java @@ -66,7 +66,7 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) // needs to present for evaluating default attribute values in JpaQueryMethod hints.reflection().registerType(Query.class, hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); - if(QuerydslUtils.QUERY_DSL_PRESENT) { + if (QuerydslUtils.QUERY_DSL_PRESENT) { hints.reflection().registerType(QuerydslJpaPredicateExecutor.class, hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS) From 88dffa8e90caa1cb39345decd6d697318210f5a8 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Thu, 20 Oct 2022 17:26:16 +0200 Subject: [PATCH 269/821] Upgrade eclipselink-next profile to Eclipselink 4.0. Fixes #2676. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5ba0fde025..48bec13dc6 100644 --- a/pom.xml +++ b/pom.xml @@ -104,7 +104,7 @@ eclipselink-next - 4.0.0-RC2 + 4.0.0 From f8da86da9a64f0f6b45f7c6162f3ef6967019eb5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 31 Oct 2022 10:36:32 +0100 Subject: [PATCH 270/821] Update CI properties. See #2675 --- ci/pipeline.properties | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 57e4868d49..1ab126263d 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,18 +1,19 @@ # Java versions -java.main.tag=17.0.3_7-jdk +java.main.tag=17.0.4.1_1-jdk-focal # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} # Supported versions of MongoDB -docker.mongodb.4.4.version=4.4.12 -docker.mongodb.5.0.version=5.0.6 +docker.mongodb.4.4.version=4.4.17 +docker.mongodb.5.0.version=5.0.13 +docker.mongodb.6.0.version=6.0.2 # Supported versions of Redis docker.redis.6.version=6.2.6 # Supported versions of Cassandra -docker.cassandra.3.version=3.11.12 +docker.cassandra.3.version=3.11.14 # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home From 443c2bab13ed624ffae93d178c750bff4d10ebc8 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 24 Oct 2022 10:25:54 +0200 Subject: [PATCH 271/821] Add missing reflection hint for NamedEntityGraph when EntityGraph is present. Closes: #2681 See also: spring-projects/spring-aot-smoke-tests#135 --- .../data/jpa/repository/aot/JpaRuntimeHints.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java index 65bb6e357e..8141aa4791 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository.aot; +import jakarta.persistence.NamedEntityGraph; + import java.util.Arrays; import org.springframework.aot.hint.MemberCategory; @@ -23,6 +25,7 @@ import org.springframework.aot.hint.TypeReference; import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.support.QuerydslJpaPredicateExecutor; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; @@ -72,5 +75,8 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS) .onReachableType(QuerydslPredicateExecutor.class)); } + + hints.reflection().registerType(NamedEntityGraph.class, + hint -> hint.onReachableType(EntityGraph.class).withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); } } From d8474e403a4ccf400736d3cf20b8ecd2b0e196dd Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 2 Nov 2022 11:55:17 +0100 Subject: [PATCH 272/821] Follow API changes in data-commons Update imports of moved AOT processing types and update reactive wrapper coordinates to new location. Closes #2687 --- .../jpa/repository/config/JpaRepositoryConfigExtension.java | 4 ++-- .../JpaRepositoryRegistrationAotProcessorUnitTests.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 4f4317f77d..ab48e20c78 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -51,11 +51,11 @@ import org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor; import org.springframework.data.jpa.repository.support.JpaEvaluationContextExtension; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; -import org.springframework.data.repository.aot.AotRepositoryContext; -import org.springframework.data.repository.aot.RepositoryRegistrationAotProcessor; import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; +import org.springframework.data.repository.config.AotRepositoryContext; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; import org.springframework.data.repository.config.RepositoryConfigurationSource; +import org.springframework.data.repository.config.RepositoryRegistrationAotProcessor; import org.springframework.data.repository.config.XmlRepositoryConfigurationSource; import org.springframework.lang.Nullable; import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java index 7b2ade3424..86e0824ab2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java @@ -31,7 +31,7 @@ import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.annotation.MergedAnnotation; -import org.springframework.data.repository.aot.AotRepositoryContext; +import org.springframework.data.repository.config.AotRepositoryContext; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.javapoet.ClassName; From 219ed80dc20090ef9f8f69556d6fef2f3f0f6c6e Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 2 Nov 2022 16:30:27 +0100 Subject: [PATCH 273/821] JpaRepository extends ListXRepository now. Closes #2688 --- .../data/jpa/repository/JpaRepository.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index 5e204ea30a..b1425ef8bb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -21,9 +21,9 @@ import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.ListPagingAndSortingRepository; import org.springframework.data.repository.NoRepositoryBean; -import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; /** @@ -35,21 +35,10 @@ * @author Sander Krabbenborg * @author Jesse Wouters * @author Greg Turnquist + * @author Jens Schauder */ @NoRepositoryBean -public interface JpaRepository extends CrudRepository,PagingAndSortingRepository, QueryByExampleExecutor { - - @Override - List findAll(); - - @Override - List findAll(Sort sort); - - @Override - List findAllById(Iterable ids); - - @Override - List saveAll(Iterable entities); +public interface JpaRepository extends ListCrudRepository, ListPagingAndSortingRepository, QueryByExampleExecutor { /** * Flushes all pending changes to the database. From 890da50909e9e207f08f7dda458e95cc609f7007 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 4 Nov 2022 15:23:05 +0100 Subject: [PATCH 274/821] Prepare 3.0 RC2 (2022.0.0). See #2675 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 48bec13dc6..5cbf507e94 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-SNAPSHOT + 3.0.0-RC2 @@ -35,7 +35,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-SNAPSHOT + 3.0.0-RC2 0.10.3 org.hibernate @@ -224,8 +224,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From 38509909d307990159c66c088526720a1c2f5e59 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 4 Nov 2022 15:23:17 +0100 Subject: [PATCH 275/821] Release version 3.0 RC2 (2022.0.0). See #2675 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 5cbf507e94..880c54a3f8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-RC2 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 540834bb60..552752da43 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-SNAPSHOT + 3.0.0-RC2 org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-RC2 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index bba8571672..f392f8bc0b 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-RC2 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 3ad9c39a2a..71838fbbcf 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-SNAPSHOT + 3.0.0-RC2 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0-RC2 ../pom.xml From b68eb0ae458ae33ef02c45882c5eff5e74aaa7a7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 4 Nov 2022 15:26:37 +0100 Subject: [PATCH 276/821] Prepare next development iteration. See #2675 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 880c54a3f8..5cbf507e94 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-RC2 + 3.0.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 552752da43..540834bb60 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-RC2 + 3.0.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.0.0-RC2 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index f392f8bc0b..bba8571672 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-RC2 + 3.0.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 71838fbbcf..3ad9c39a2a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-RC2 + 3.0.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-RC2 + 3.0.0-SNAPSHOT ../pom.xml From d71eb192c7976c78ed617c33a96639dbe0264a98 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 4 Nov 2022 15:26:39 +0100 Subject: [PATCH 277/821] After release cleanups. See #2675 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 5cbf507e94..48bec13dc6 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-RC2 + 3.0.0-SNAPSHOT @@ -35,7 +35,7 @@ 4.3 8.0.23 42.2.19 - 3.0.0-RC2 + 3.0.0-SNAPSHOT 0.10.3 org.hibernate @@ -224,8 +224,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 623e1c5fdea1a506dce7574ef8b0a1829b9d3066 Mon Sep 17 00:00:00 2001 From: Diego Krupitza Date: Tue, 8 Nov 2022 10:59:28 +0100 Subject: [PATCH 278/821] Upgraded JSQLParser to version 4.5. Upgrading to the newer version should bring minor performance improvement when parsing SQL. Resolves #2693. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 48bec13dc6..037b7431ec 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 3.0.2 6.1.2.Final - 4.3 + 4.5 8.0.23 42.2.19 3.0.0-SNAPSHOT From 313151e90d13f746e9d0fdc7d7fa4d646e06e9be Mon Sep 17 00:00:00 2001 From: Swell <5782559+sultan@users.noreply.github.com> Date: Sat, 22 Oct 2022 13:29:25 +0200 Subject: [PATCH 279/821] Update test dependencies. Resolves #2677. --- pom.xml | 8 ++++---- spring-data-jpa/pom.xml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 037b7431ec..d2a034919b 100644 --- a/pom.xml +++ b/pom.xml @@ -30,11 +30,11 @@ 16 - 3.0.2 - 6.1.2.Final + 3.0.3 + 6.1.4.Final 4.5 - 8.0.23 - 42.2.19 + 8.0.31 + 42.5.0 3.0.0-SNAPSHOT 0.10.3 diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 3ad9c39a2a..ccba6b4cc7 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -96,8 +96,8 @@ - mysql - mysql-connector-java + com.mysql + mysql-connector-j ${mysql-connector-java} test From a1963652575d1dbb78d88fb7d5b7c837a80c38db Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 9 Nov 2022 11:40:06 -0600 Subject: [PATCH 280/821] Upgrade hsqldb test dependency. Resolves #2694. --- pom.xml | 1 + spring-data-jpa/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d2a034919b..dcd0ac97a4 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ 3.0.3 6.1.4.Final + 2.7.1 4.5 8.0.31 42.5.0 diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index ccba6b4cc7..8b8c12b780 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -90,7 +90,7 @@ org.hsqldb hsqldb - 2.5.1 + ${hsqldb} test @@ -413,5 +413,5 @@ - + From 34e16e34c54c085ec957c781996ad3027c92b6d3 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 7 Nov 2022 13:55:28 -0600 Subject: [PATCH 281/821] Properly handle null mixed with LIKE. Resolves: #2653. Related: #2548, #2683, #2655, #2461, possibly #2544 --- .../jpa/provider/PersistenceProvider.java | 12 ++++----- .../query/ParameterMetadataProvider.java | 26 ++++++++----------- .../jpa/repository/query/StringQuery.java | 21 ++++++++------- ...WithNullLikeHibernateIntegrationTests.java | 21 +++++++-------- 4 files changed, 37 insertions(+), 43 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 8dfa22e472..bb821b1d51 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jpa.provider; -import static org.springframework.data.jpa.provider.JpaClassUtils.*; +import static org.springframework.data.jpa.provider.JpaClassUtils.isEntityManagerOfType; +import static org.springframework.data.jpa.provider.JpaClassUtils.isMetamodelOfType; import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*; import jakarta.persistence.EntityManager; @@ -24,11 +25,7 @@ import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Set; +import java.util.*; import org.eclipse.persistence.config.QueryHints; import org.eclipse.persistence.jpa.JpaQuery; @@ -53,6 +50,7 @@ * @author Mark Paluch * @author Jens Schauder * @author Greg Turnquist + * @author Yuriy Tsarkov */ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor, QueryComment { @@ -330,7 +328,7 @@ public static Object condense(Object value) { Class typeParameterValue = ClassUtils.forName("org.hibernate.query.TypedParameterValue", classLoader); if (typeParameterValue.isInstance(value)) { - return ""; + return null; } } catch (ClassNotFoundException | LinkageError o_O) { return value; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index acca0a5dcb..0176db19f1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -18,12 +18,7 @@ import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.ParameterExpression; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; +import java.util.*; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -50,6 +45,7 @@ * @author Christoph Strobl * @author Jens Schauder * @author Andrey Kovalev + * @author Yuriy Tsarkov */ class ParameterMetadataProvider { @@ -237,28 +233,28 @@ public Object prepare(Object value) { Assert.notNull(value, "Value must not be null"); - Class expressionType = expression.getJavaType(); + Object condensedValue = PersistenceProvider.condense(value); - if (expressionType == null) { - return value; + if (condensedValue == null || expression.getJavaType() == null) { + return condensedValue; } - if (String.class.equals(expressionType) && !noWildcards) { + if (String.class.equals(expression.getJavaType()) && !noWildcards) { switch (type) { case STARTING_WITH: - return String.format("%s%%", escape.escape(PersistenceProvider.condense(value).toString())); + return String.format("%s%%", escape.escape(condensedValue.toString())); case ENDING_WITH: - return String.format("%%%s", escape.escape(PersistenceProvider.condense(value).toString())); + return String.format("%%%s", escape.escape(condensedValue.toString())); case CONTAINING: case NOT_CONTAINING: - return String.format("%%%s%%", escape.escape(PersistenceProvider.condense(value).toString())); + return String.format("%%%s%%", escape.escape(condensedValue.toString())); default: - return PersistenceProvider.condense(value); + return condensedValue; } } - return Collection.class.isAssignableFrom(expressionType) // + return Collection.class.isAssignableFrom(expression.getJavaType()) // ? upperIfIgnoreCase(ignoreCase, toCollection(value)) // : value; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 56e2d46185..712a27871d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -15,8 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import static java.util.regex.Pattern.*; -import static org.springframework.util.ObjectUtils.*; +import static java.util.regex.Pattern.CASE_INSENSITIVE; +import static org.springframework.util.ObjectUtils.nullSafeEquals; +import static org.springframework.util.ObjectUtils.nullSafeHashCode; import java.lang.reflect.Array; import java.util.ArrayList; @@ -49,6 +50,7 @@ * @author Jens Schauder * @author Diego Krupitza * @author Greg Turnquist + * @author Yuriy Tsarkov */ class StringQuery implements DeclaredQuery { @@ -638,7 +640,7 @@ static class LikeParameterBinding extends ParameterBinding { /** * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type}. - * + * * @param name must not be {@literal null} or empty. * @param type must not be {@literal null}. */ @@ -649,7 +651,7 @@ static class LikeParameterBinding extends ParameterBinding { /** * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type} and parameter * binding input. - * + * * @param name must not be {@literal null} or empty. * @param type must not be {@literal null}. * @param expression may be {@literal null}. @@ -713,20 +715,21 @@ public Type getType() { @Override public Object prepare(@Nullable Object value) { - if (value == null) { + Object condensedValue = PersistenceProvider.condense(value); + if (condensedValue == null) { return null; } switch (type) { case STARTING_WITH: - return String.format("%s%%", PersistenceProvider.condense(value)); + return String.format("%s%%", condensedValue); case ENDING_WITH: - return String.format("%%%s", PersistenceProvider.condense(value)); + return String.format("%%%s", condensedValue); case CONTAINING: - return String.format("%%%s%%", PersistenceProvider.condense(value)); + return String.format("%%%s%%", condensedValue); case LIKE: default: - return PersistenceProvider.condense(value); + return condensedValue; } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java index 9c3f60d9d5..c8b24b0497 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import jakarta.persistence.EntityManagerFactory; @@ -52,13 +52,14 @@ * Verify that {@literal LIKE}s mixed with {@literal NULL}s work properly. * * @author Greg Turnquist + * @author Yuriy Tsarkov */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = QueryWithNullLikeHibernateIntegrationTests.Config.class) @Transactional public class QueryWithNullLikeHibernateIntegrationTests { - @Autowired EmpoyeeWithNullLikeRepository repository; + @Autowired EmployeeWithNullLikeRepository repository; @BeforeEach void setUp() { @@ -98,8 +99,7 @@ void customQueryWithNullMatch() { List Employees = repository.customQueryWithNullableParam(null); - assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", - "Bilbo Baggins"); + assertThat(Employees).extracting(EmployeeWithName::getName).isEmpty(); } @Test @@ -132,8 +132,7 @@ void derivedQueryStartsWithWithNullMatch() { List Employees = repository.findByNameStartsWith(null); - assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", - "Bilbo Baggins"); + assertThat(Employees).extracting(EmployeeWithName::getName).isEmpty(); } @Test @@ -167,8 +166,7 @@ void derivedQueryEndsWithWithNullMatch() { List Employees = repository.findByNameEndsWith(null); - assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", - "Bilbo Baggins"); + assertThat(Employees).extracting(EmployeeWithName::getName).isEmpty(); } @Test @@ -202,8 +200,7 @@ void derivedQueryContainsWithNullMatch() { List Employees = repository.findByNameContains(null); - assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", - "Bilbo Baggins"); + assertThat(Employees).extracting(EmployeeWithName::getName).isEmpty(); } @Test @@ -233,7 +230,7 @@ void derivedQueryLikeWithEmptyStringMatch() { } @Transactional - public interface EmpoyeeWithNullLikeRepository extends JpaRepository { + public interface EmployeeWithNullLikeRepository extends JpaRepository { @Query("select e from EmployeeWithName e where e.name like %:partialName%") List customQueryWithNullableParam(@Nullable @Param("partialName") String partialName); @@ -248,7 +245,7 @@ public interface EmpoyeeWithNullLikeRepository extends JpaRepository Date: Thu, 10 Nov 2022 12:29:15 +0100 Subject: [PATCH 282/821] Replace New and Noteworthy with links to release notes. Closes #2697 See https://github.com/spring-projects/spring-data-relational/issues/1351 --- src/main/asciidoc/index.adoc | 2 +- src/main/asciidoc/new-features.adoc | 56 ----------------------------- 2 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 src/main/asciidoc/new-features.adoc diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index 08d1e609da..ea94f95ac3 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -12,7 +12,7 @@ NOTE: Copies of this document may be made for your own use and for distribution include::preface.adoc[] -include::new-features.adoc[leveloffset=+1] +include::{spring-data-commons-docs}/upgrade.adoc[leveloffset=+1] include::{spring-data-commons-docs}/dependencies.adoc[leveloffset=+1] diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc deleted file mode 100644 index 7d4b346560..0000000000 --- a/src/main/asciidoc/new-features.adoc +++ /dev/null @@ -1,56 +0,0 @@ -[[new-features]] -= New & Noteworthy - -[[new-features.3-0]] -== What's New in Spring Data JPA 3.0 -* Upgrade to Hibernate 6. -See <> for what to consider when upgrading. -* Support for null handling definitions via `Sort`. - -[[new-features.3-0.hibernate-6]] -=== Upgrading to Hibernate 6 -Spring Data 3.0 upgrades its Hibernate baseline to Hibernate 6. -As quite a few things have changed in that version, a couple of things that have worked before might need some tweaks. - -* _Using JPA named queries with pagination_ -- Pagination requires Spring Data to derive a count query from the originally declared one loading the actual content of the Page. -For queries declared as JPA named queries we have relied on provider-specific API to obtain the original source query and tweak it accordingly. -On Hibernate 6, in certain arrangements that query extraction might fail. -We recommend to either rather declare the queries on the repository methods directly using `@Query`. -* _Using positional parameters with pagination_ -- When using positional parameters with pagination queries you need to make sure that the parameter indexes still start with 1, even with a potential `ORDER BY` clause removed from the query. -This is because, the count query derived from the original one will have that clause removed from the query and Hibernate 6 rejects queries parameter indexes not starting at 1. -We generally recommend to use named parameters anyway. -* _Applying JPA entity graphs_ -- Under certain model conditions, the application of entity graphs might fail on Hibernate 6. -See https://hibernate.atlassian.net/browse/HHH-15391[this ticket] for details. -We generally recommend to rather use <> instead of entity graphs. -* _Using `… like … escape ?#{escapeCharacter()}` in queries_ -- If you have customized the global default escape character (via `@EnableJpaRepositories(escapeCharacter = '…')`) the application of that through the corresponding SpEL expression currently fails. -See https://hibernate.atlassian.net/browse/HHH-15392[this ticket] for details. - -[[new-features.2-5-0]] -== What's New in Spring Data JPA 2.5 - -There is a new `getById` method in the `JpaRepository` which will replace `getOne`, which is now deprecated. -Since this method returns a reference this changes the behaviour of an existing `getById` method which before was implemented by query derivation. -This in turn might lead to an unexpected `LazyLoadingException` when accessing attributes of that reference outside a transaction. -To avoid this please rename your existing `getById` method to `getXyzById` with `Xyz` being an arbitrary string. - -[[new-features.1-11-0]] -== What's New in Spring Data JPA 1.11 - -Spring Data JPA 1.11 added the following features: - -* Improved compatibility with Hibernate 5.2. -* Support any-match mode for <>. -* Paged query optimizations. -* Support for the `exists` projection in repository query derivation. - -[[new-features.1-10-0]] -== What's New in Spring Data JPA 1.10 - -Spring Data JPA 1.10 added the following features: - -* Support for <> in repository query methods. -* Support for <>. -* The following annotations have been enabled to build on composed annotations: `@EntityGraph`, `@Lock`, `@Modifying`, `@Query`, `@QueryHints`, and `@Procedure`. -* Support for the `Contains` keyword on collection expressions. -* `AttributeConverter` implementations for `ZoneId` of JSR-310 and ThreeTenBP. -* Upgrade to Querydsl 4, Hibernate 5, OpenJPA 2.4, and EclipseLink 2.6.1. From 4b1ac8a7f3d84fbefcc6fe679b5f9bb5e29c9ffa Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 14 Nov 2022 12:00:38 +0100 Subject: [PATCH 283/821] Ensure unique bean definitions for EntityManager beans. We now make sure to create unique beans to avoid autowiring resolution problems for EntityManager beans through SharedEntityManagerCreator definitions. Closes #2699 --- .../config/JpaRepositoryConfigExtension.java | 25 +++++++++------ ...rBeanDefinitionRegistrarPostProcessor.java | 32 +++++++++++-------- ...nitionRegistrarPostProcessorUnitTests.java | 19 +++++++++++ 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index ab48e20c78..bafde60780 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -38,7 +38,6 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationConfigUtils; @@ -164,7 +163,7 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf super.registerBeansForRoot(registry, config); - prepareAndRegisterSharedEntityManger(registry, config); + registerSharedEntityMangerIfNotAlreadyRegistered(registry, config); Object source = config.getSource(); @@ -208,18 +207,24 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf }, registry, JpaEvaluationContextExtension.class.getName(), source); } - private String prepareAndRegisterSharedEntityManger(BeanDefinitionRegistry registry, + private String registerSharedEntityMangerIfNotAlreadyRegistered(BeanDefinitionRegistry registry, RepositoryConfigurationSource config) { - AbstractBeanDefinition entityManager = getEntityManagerBeanDefinitionFor(config, null); - entityManager.setRole(BeanDefinition.ROLE_SUPPORT); - entityManager.setSynthetic(true); - entityManager.setPrimary(false); - entityManager.setAutowireCandidate(false); + String entityManagerBeanRef = getEntityManagerBeanRef(config); + String entityManagerBeanName = "jpaSharedEM_" + entityManagerBeanRef; + + if (!registry.containsBeanDefinition(entityManagerBeanName)) { + + AbstractBeanDefinition entityManager = getEntityManagerBeanDefinitionFor(config, null); + entityManager.setRole(BeanDefinition.ROLE_SUPPORT); + entityManager.setSynthetic(true); + entityManager.setPrimary(false); + entityManager.setAutowireCandidate(false); + + registry.registerBeanDefinition(entityManagerBeanName, entityManager); + } - String entityManagerBeanName = BeanDefinitionReaderUtils.uniqueBeanName("jpaSharedEM", registry); entityManagerRefs.put(config, entityManagerBeanName); - registry.registerBeanDefinition(entityManagerBeanName, entityManager); return entityManagerBeanName; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index 7cb65ab8a7..df78cffe78 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -29,10 +29,8 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.Ordered; -import org.springframework.data.jpa.util.BeanDefinitionUtils.*; import org.springframework.orm.jpa.SharedEntityManagerCreator; /** @@ -44,6 +42,7 @@ * * @author Oliver Gierke * @author Réda Housni Alaoui + * @author Mark Paluch */ public class EntityManagerBeanDefinitionRegistrarPostProcessor implements BeanFactoryPostProcessor, Ordered { @@ -59,7 +58,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) return; } - ConfigurableListableBeanFactory factory = (ConfigurableListableBeanFactory) beanFactory; + ConfigurableListableBeanFactory factory = beanFactory; for (EntityManagerFactoryBeanDefinition definition : getEntityManagerFactoryBeanDefinitions(factory)) { @@ -69,22 +68,27 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) continue; } + String entityManagerBeanName = "jpaSharedEM_AWC_" + definition.getBeanName(); BeanDefinitionRegistry definitionRegistry = (BeanDefinitionRegistry) definitionFactory; - BeanDefinitionBuilder builder = BeanDefinitionBuilder - .rootBeanDefinition("org.springframework.orm.jpa.SharedEntityManagerCreator"); - builder.setFactoryMethod("createSharedEntityManager"); - builder.addConstructorArgReference(definition.getBeanName()); + if (!beanFactory.containsBeanDefinition(entityManagerBeanName) + && !definitionRegistry.containsBeanDefinition(entityManagerBeanName)) { - AbstractBeanDefinition emBeanDefinition = builder.getRawBeanDefinition(); + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .rootBeanDefinition("org.springframework.orm.jpa.SharedEntityManagerCreator"); + builder.setFactoryMethod("createSharedEntityManager"); + builder.addConstructorArgReference(definition.getBeanName()); - emBeanDefinition.setPrimary(definition.getBeanDefinition().isPrimary()); - emBeanDefinition.addQualifier(new AutowireCandidateQualifier(Qualifier.class, definition.getBeanName())); - emBeanDefinition.setScope(definition.getBeanDefinition().getScope()); - emBeanDefinition.setSource(definition.getBeanDefinition().getSource()); - emBeanDefinition.setLazyInit(true); + AbstractBeanDefinition emBeanDefinition = builder.getRawBeanDefinition(); - BeanDefinitionReaderUtils.registerWithGeneratedName(emBeanDefinition, definitionRegistry); + emBeanDefinition.setPrimary(definition.getBeanDefinition().isPrimary()); + emBeanDefinition.addQualifier(new AutowireCandidateQualifier(Qualifier.class, definition.getBeanName())); + emBeanDefinition.setScope(definition.getBeanDefinition().getScope()); + emBeanDefinition.setSource(definition.getBeanDefinition().getSource()); + emBeanDefinition.setLazyInit(true); + + definitionRegistry.registerBeanDefinition(entityManagerBeanName, emBeanDefinition); + } } } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java index aa0da50512..3601db32e6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java @@ -33,6 +33,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Mark Paluch */ public class EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests { @@ -65,6 +66,24 @@ void discoversFactoryBeanReturningConcreteEntityManagerFactoryType() { assertThat(beanFactory.getBeanDefinitionCount()).isEqualTo(2); } + @Test // gh-2699 + void avoidsDuplicateBeanRegistrations() { + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(StubEntityManagerFactoryBean.class); + builder.addConstructorArgValue(SpecialEntityManagerFactory.class); + + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + beanFactory.registerBeanDefinition("factory", builder.getBeanDefinition()); + + beanFactory.registerBeanDefinition("jpaSharedEM_AWC_factory", + BeanDefinitionBuilder.rootBeanDefinition(Object.class).getBeanDefinition()); + + BeanFactoryPostProcessor processor = new EntityManagerBeanDefinitionRegistrarPostProcessor(); + processor.postProcessBeanFactory(beanFactory); + + assertThat(beanFactory.getBeanDefinitionCount()).isEqualTo(2); + } + interface SpecialEntityManagerFactory extends EntityManagerFactory {} static class StubEntityManagerFactoryBean extends LocalContainerEntityManagerFactoryBean { From 22badd4c84f7a4ad957ff23d5adce2846def1a8e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 18 Nov 2022 14:26:12 +0100 Subject: [PATCH 284/821] Prepare 3.0 GA (2022.0.0). See #2669 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index dcd0ac97a4..0bf8081a67 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.0.0-SNAPSHOT + 3.0.0 @@ -36,7 +36,7 @@ 4.5 8.0.31 42.5.0 - 3.0.0-SNAPSHOT + 3.0.0 0.10.3 org.hibernate @@ -225,8 +225,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-release + https://repo.spring.io/libs-release From de4af44466ddc39c870159d4eb6f0b595f47b927 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 18 Nov 2022 14:26:22 +0100 Subject: [PATCH 285/821] Release version 3.0 GA (2022.0.0). See #2669 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 0bf8081a67..ba3163e54c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 540834bb60..5d6990849c 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0-SNAPSHOT + 3.0.0 org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index bba8571672..fc3977feb9 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 8b8c12b780..d67baa009a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0-SNAPSHOT + 3.0.0 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0-SNAPSHOT + 3.0.0 ../pom.xml From f9dd15e187dd4ed1e0f1f3fa006b81932d0a5aa7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 18 Nov 2022 14:30:18 +0100 Subject: [PATCH 286/821] Prepare next development iteration. See #2669 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index ba3163e54c..126bfa9e6f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0 + 3.1.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 5d6990849c..db915d7c3b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.0.0 + 3.1.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.0.0 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index fc3977feb9..a5cb2f09b5 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index d67baa009a..95a83aac44 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.0.0 + 3.1.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.0.0 + 3.1.0-SNAPSHOT ../pom.xml From 0e448dca93baab5f66e499ad9454f876aa5f062f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 18 Nov 2022 14:30:20 +0100 Subject: [PATCH 287/821] After release cleanups. See #2669 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 126bfa9e6f..c94ae35cd5 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.0.0 + 3.1.0-SNAPSHOT @@ -36,7 +36,7 @@ 4.5 8.0.31 42.5.0 - 3.0.0 + 3.1.0-SNAPSHOT 0.10.3 org.hibernate @@ -225,8 +225,8 @@ - spring-libs-release - https://repo.spring.io/libs-release + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From a95d25aa8b1481689f798b6f735b918ca19dd8d9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 18 Nov 2022 15:31:10 +0100 Subject: [PATCH 288/821] Update CI properties. See #2707 --- ci/pipeline.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 1ab126263d..cf37b39c99 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,5 +1,5 @@ # Java versions -java.main.tag=17.0.4.1_1-jdk-focal +java.main.tag=17.0.5_8-jdk-focal # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} From f40b39c3ec1039c36f4545ee9a5f74c6451d850a Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 5 Dec 2022 11:47:14 -0600 Subject: [PATCH 289/821] Do not throw an exception for deleteById when no rows are found. Comply with Spring Data Commons' new policy of not throwing an EmptyResultDataAccessException during deleteById. Resolves: #2719. Related: https://github.com/spring-projects/spring-data-commons/issues/2651 --- .../support/SimpleJpaRepository.java | 11 +++++++---- .../support/SimpleJpaRepositoryUnitTests.java | 19 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 0927afc231..5e1662dbae 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -15,7 +15,12 @@ */ package org.springframework.data.jpa.repository.support; -import static org.springframework.data.jpa.repository.query.QueryUtils.*; +import static org.springframework.data.jpa.repository.query.QueryUtils.COUNT_QUERY_STRING; +import static org.springframework.data.jpa.repository.query.QueryUtils.DELETE_ALL_QUERY_BY_ID_STRING; +import static org.springframework.data.jpa.repository.query.QueryUtils.DELETE_ALL_QUERY_STRING; +import static org.springframework.data.jpa.repository.query.QueryUtils.applyAndBind; +import static org.springframework.data.jpa.repository.query.QueryUtils.getQueryString; +import static org.springframework.data.jpa.repository.query.QueryUtils.toOrders; import jakarta.persistence.EntityManager; import jakarta.persistence.LockModeType; @@ -42,7 +47,6 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -167,8 +171,7 @@ public void deleteById(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); - delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException( - String.format("No %s entity with id %s exists", entityInformation.getJavaType(), id), 1))); + findById(id).ifPresent(this::delete); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index 66291519aa..d0ad52aace 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -15,10 +15,13 @@ */ package org.springframework.data.jpa.repository.support; -import static java.util.Collections.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.jpa.domain.Specification.*; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.data.jpa.domain.Specification.where; import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; @@ -38,7 +41,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; -import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; @@ -51,6 +53,7 @@ * @author Thomas Darimont * @author Mark Paluch * @author Jens Schauder + * @author Greg Turnquist */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -122,10 +125,10 @@ void doesNotRetrieveCountWithOffsetAndResultsWithinPageSize() { verify(countQuery, never()).getSingleResult(); } - @Test // DATAJPA-177 - void throwsExceptionIfEntityToDeleteDoesNotExist() { + @Test // DATAJPA-177, gh-2719 + void doesNotThrowExceptionIfEntityToDeleteDoesNotExist() { - assertThatExceptionOfType(EmptyResultDataAccessException.class).isThrownBy(() -> repo.deleteById(4711)); + assertThatNoException().isThrownBy(() -> repo.deleteById(4711)); } @Test // DATAJPA-689, DATAJPA-696 From b0aa2b67546698fefc4218393ea9eebc01524c69 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 5 Dec 2022 12:28:31 -0600 Subject: [PATCH 290/821] Add additional null checks when applying hints. It's possible to apply hints and hit a NullPointerException. Resolves #2716. --- .../data/jpa/repository/support/SimpleJpaRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 5e1662dbae..17abebbd3d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -832,6 +832,10 @@ private TypedQuery applyRepositoryMethodMetadata(TypedQuery query) { private void applyQueryHints(Query query) { + if (metadata == null) { + return; + } + getQueryHints().withFetchGraphs(em).forEach(query::setHint); if (metadata.getComment() != null && provider.getCommentHintKey() != null) { @@ -852,6 +856,10 @@ private TypedQuery applyRepositoryMethodMetadataForCount(TypedQuery qu private void applyQueryHintsForCount(Query query) { + if (metadata == null) { + return; + } + getQueryHintsForCount().forEach(query::setHint); if (metadata.getComment() != null && provider.getCommentHintKey() != null) { From 06790c237799406a23e94f562aace835ce352884 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 19 Dec 2022 15:26:32 -0600 Subject: [PATCH 291/821] Handle nulls in stored procedure parameters properly. Hibernate 6.1 properly handles TypeParameterValue for general parameters, but not for temporal ones. So we must dereference in those scenarios. Related: #2544, https://github.com/hibernate/hibernate-orm/pull/5438 --- .../query/QueryParameterSetter.java | 32 ++-- .../MySqlStoredProcedureIntegrationTests.java | 19 +- ...stgresStoredProcedureIntegrationTests.java | 25 +-- ...ProcedureNullHandlingIntegrationTests.java | 166 ++++++++++++++++++ .../postgres-nullable-stored-procedures.sql | 41 +++++ 5 files changed, 251 insertions(+), 32 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java create mode 100644 spring-data-jpa/src/test/resources/scripts/postgres-nullable-stored-procedures.sql diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index e3c40f18a6..700a6fd4b9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -15,7 +15,12 @@ */ package org.springframework.data.jpa.repository.query; -import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.*; +import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.LENIENT; + +import jakarta.persistence.Parameter; +import jakarta.persistence.Query; +import jakarta.persistence.TemporalType; +import jakarta.persistence.criteria.ParameterExpression; import java.lang.reflect.Proxy; import java.util.Collections; @@ -25,13 +30,9 @@ import java.util.Set; import java.util.function.Function; -import jakarta.persistence.Parameter; -import jakarta.persistence.Query; -import jakarta.persistence.TemporalType; -import jakarta.persistence.criteria.ParameterExpression; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.hibernate.query.TypedParameterValue; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -79,19 +80,26 @@ class NamedOrIndexedQueryParameterSetter implements QueryParameterSetter { public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling) { - Object value = valueExtractor.apply(accessor); - if (temporalType != null) { + Object extractedValue = valueExtractor.apply(accessor); + + final Date value; + if (extractedValue instanceof TypedParameterValue) { + value = (Date) ((TypedParameterValue) extractedValue).getValue(); + } else { + value = (Date) extractedValue; + } + // One would think we can simply use parameter to identify the parameter we want to set. // But that does not work with list valued parameters. At least Hibernate tries to bind them by name. // TODO: move to using setParameter(Parameter, value) when https://hibernate.atlassian.net/browse/HHH-11870 is // fixed. if (parameter instanceof ParameterExpression) { - errorHandling.execute(() -> query.setParameter((Parameter) parameter, (Date) value, temporalType)); + errorHandling.execute(() -> query.setParameter((Parameter) parameter, value, temporalType)); } else if (query.hasNamedParameters() && parameter.getName() != null) { - errorHandling.execute(() -> query.setParameter(parameter.getName(), (Date) value, temporalType)); + errorHandling.execute(() -> query.setParameter(parameter.getName(), value, temporalType)); } else { Integer position = parameter.getPosition(); @@ -101,12 +109,14 @@ public void setParameter(BindableQuery query, JpaParametersParameterAccessor acc || query.registerExcessParameters() // || errorHandling == LENIENT)) { - errorHandling.execute(() -> query.setParameter(parameter.getPosition(), (Date) value, temporalType)); + errorHandling.execute(() -> query.setParameter(parameter.getPosition(), value, temporalType)); } } } else { + final Object value = valueExtractor.apply(accessor); + if (parameter instanceof ParameterExpression) { errorHandling.execute(() -> query.setParameter((Parameter) parameter, value)); } else if (query.hasNamedParameters() && parameter.getName() != null) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java index 4112d1825e..aa011c4323 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -16,8 +16,13 @@ package org.springframework.data.jpa.repository.procedures; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.NamedStoredProcedureQuery; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -25,13 +30,7 @@ import java.util.List; import java.util.Properties; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.NamedStoredProcedureQuery; import javax.sql.DataSource; -import jakarta.transaction.Transactional; import org.hibernate.dialect.MySQL8Dialect; import org.junit.jupiter.api.Test; @@ -54,6 +53,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.Transactional; import org.testcontainers.containers.MySQLContainer; import com.mysql.cj.jdbc.MysqlDataSource; @@ -154,7 +154,8 @@ void testEntityListFromNamedProcedure() { resultClasses = Employee.class) public static class Employee { - @Id @GeneratedValue private Integer id; + @Id + @GeneratedValue private Integer id; private String name; } @@ -194,7 +195,7 @@ public interface EmployeeRepositoryWithNoCursor extends JpaRepository container() { return new MySQLContainer<>("mysql:8.0.24") // diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index 5243cfce5a..cbd325a2e9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -16,8 +16,15 @@ package org.springframework.data.jpa.repository.procedures; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.NamedStoredProcedureQuery; +import jakarta.persistence.ParameterMode; +import jakarta.persistence.StoredProcedureParameter; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -26,15 +33,7 @@ import java.util.List; import java.util.Properties; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.NamedStoredProcedureQuery; -import jakarta.persistence.ParameterMode; -import jakarta.persistence.StoredProcedureParameter; import javax.sql.DataSource; -import jakarta.transaction.Transactional; import org.hibernate.dialect.PostgreSQL91Dialect; import org.junit.jupiter.api.Test; @@ -58,6 +57,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.Transactional; import org.testcontainers.containers.PostgreSQLContainer; /** @@ -159,7 +159,8 @@ void testEntityListFromNamedProcedure() { resultClasses = Employee.class) public static class Employee { - @Id @GeneratedValue private Integer id; + @Id + @GeneratedValue private Integer id; private String name; } @@ -197,11 +198,11 @@ public interface EmployeeRepositoryWithRefCursor extends JpaRepository container() { return new PostgreSQLContainer<>("postgres:9.6.12") // - .withUsername("postgres"); + .withUsername("postgres"); } @Bean diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java new file mode 100644 index 0000000000..3b7ea6e04b --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2015-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.procedures; + +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.Properties; +import java.util.UUID; + +import javax.sql.DataSource; + +import org.hibernate.dialect.PostgreSQL91Dialect; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.postgresql.ds.PGSimpleDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Temporal; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.jdbc.datasource.init.DataSourceInitializer; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.Transactional; +import org.testcontainers.containers.PostgreSQLContainer; + +/** + * Testcase to verify {@link org.springframework.jdbc.object.StoredProcedure}s properly handle null values. + * + * @author Greg Turnquist + */ +@Transactional +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = PostgresStoredProcedureNullHandlingIntegrationTests.Config.class) +public class PostgresStoredProcedureNullHandlingIntegrationTests { + + @Autowired TestModelRepository repository; + + @Test // 2544 + void invokingNullOnNonTemporalStoredProcedureParameterShouldWork() { + repository.countUuid(null); + } + + @Test // 2544 + void invokingNullOnTemporalStoredProcedureParameterShouldWork() { + repository.countLocalDate(null); + } + + @Data + @AllArgsConstructor + @NoArgsConstructor(access = AccessLevel.PROTECTED) + @Entity + public class TestModel { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) private long id; + private UUID uuid; + private Date date; + } + + @Transactional + public interface TestModelRepository extends JpaRepository { + + @Procedure("countByUuid") + void countUuid(UUID this_uuid); + + @Procedure("countByLocalDate") + void countLocalDate(@Temporal Date localDate); + } + + @EnableJpaRepositories(considerNestedRepositories = true, + includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = TestModelRepository.class)) + @EnableTransactionManagement + static class Config { + + @Bean(initMethod = "start", destroyMethod = "stop") + public PostgreSQLContainer container() { + + return new PostgreSQLContainer<>("postgres:9.6.12") // + .withUsername("postgres"); + } + + @Bean + public DataSource dataSource(PostgreSQLContainer container) { + + PGSimpleDataSource dataSource = new PGSimpleDataSource(); + dataSource.setUrl(container.getJdbcUrl()); + dataSource.setUser(container.getUsername()); + dataSource.setPassword(container.getPassword()); + + return dataSource; + } + + @Bean + public AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { + + LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); + factoryBean.setDataSource(dataSource); + factoryBean.setPersistenceUnitRootLocation("simple-persistence"); + factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + factoryBean.setPackagesToScan(this.getClass().getPackage().getName()); + + Properties properties = new Properties(); + properties.setProperty("hibernate.hbm2ddl.auto", "create"); + properties.setProperty("hibernate.dialect", PostgreSQL91Dialect.class.getCanonicalName()); + properties.setProperty("hibernate.proc.param_null_passing", "true"); + properties.setProperty("hibernate.globally_quoted_identifiers", "true"); + properties.setProperty("hibernate.globally_quoted_identifiers_skip_column_definitions", "true"); + factoryBean.setJpaProperties(properties); + + return factoryBean; + } + + @Bean + PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + return new JpaTransactionManager(entityManagerFactory); + } + + @Bean + DataSourceInitializer initializer(DataSource dataSource) { + + DataSourceInitializer initializer = new DataSourceInitializer(); + initializer.setDataSource(dataSource); + + ClassPathResource script = new ClassPathResource("scripts/postgres-nullable-stored-procedures.sql"); + ResourceDatabasePopulator populator = new ResourceDatabasePopulator(script); + populator.setSeparator(";;"); + initializer.setDatabasePopulator(populator); + + return initializer; + } + } +} diff --git a/spring-data-jpa/src/test/resources/scripts/postgres-nullable-stored-procedures.sql b/spring-data-jpa/src/test/resources/scripts/postgres-nullable-stored-procedures.sql new file mode 100644 index 0000000000..03a619176e --- /dev/null +++ b/spring-data-jpa/src/test/resources/scripts/postgres-nullable-stored-procedures.sql @@ -0,0 +1,41 @@ +CREATE TABLE test_model +( + ID numeric NOT NULL, + uuid UUID, + local_date DATE, + CONSTRAINT test_model_pk PRIMARY KEY (ID) +);; + +CREATE OR REPLACE FUNCTION countByUuid(this_uuid uuid) + RETURNS int + LANGUAGE 'plpgsql' +AS +$BODY$ +DECLARE + c integer; +BEGIN + SELECT count(*) + INTO c + FROM test_model + WHERE test_model.uuid = this_uuid; + RETURN c; +END; +$BODY$ +;; + +CREATE OR REPLACE FUNCTION countByLocalDate(this_local_date DATE) + RETURNS int + LANGUAGE 'plpgsql' +AS +$BODY$ +DECLARE + c integer; +BEGIN + SELECT count(*) + INTO c + FROM test_model + WHERE test_model.local_date = this_local_date; + RETURN c; +END; +$BODY$ +;; From f260d178fe15d4f6a58d26643445df1200002891 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 19 Dec 2022 16:00:32 -0600 Subject: [PATCH 292/821] Polishing. --- .../jpa/repository/query/QueryParameterSetter.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index 700a6fd4b9..8e0d0c2905 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -82,14 +82,11 @@ public void setParameter(BindableQuery query, JpaParametersParameterAccessor acc if (temporalType != null) { - Object extractedValue = valueExtractor.apply(accessor); + var extractedValue = valueExtractor.apply(accessor); - final Date value; - if (extractedValue instanceof TypedParameterValue) { - value = (Date) ((TypedParameterValue) extractedValue).getValue(); - } else { - value = (Date) extractedValue; - } + final Date value = (extractedValue instanceof TypedParameterValue typedParameterValue) + ? (Date) typedParameterValue.getValue() + : (Date) extractedValue; // One would think we can simply use parameter to identify the parameter we want to set. // But that does not work with list valued parameters. At least Hibernate tries to bind them by name. From 1604a22d18f7b357fa7a8cc2446b4319fbd2386b Mon Sep 17 00:00:00 2001 From: slabiakt <84189036+slabiakt@users.noreply.github.com> Date: Mon, 14 Nov 2022 13:47:22 +0100 Subject: [PATCH 293/821] Improve error message in ParameterBinder. Resolves #2700. --- .../data/jpa/repository/query/ParameterBinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index af76bb5476..6aa5b991b8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -33,7 +33,7 @@ */ public class ParameterBinder { - static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to use provide names for method parameters; Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters"; + static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to provide names for method parameters; Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters"; private final JpaParameters parameters; private final Iterable parameterSetters; From e737efde6bcbcf7f78ab583a4127c8f50d2c41fc Mon Sep 17 00:00:00 2001 From: Petr Strnad Date: Sat, 18 Jun 2022 13:16:36 +0200 Subject: [PATCH 294/821] Fix EntityGraphFactory to not overwrite prior subgraphs. Resolves #2527. --- .../support/EntityGraphFactory.java | 17 +++++++--- .../support/EntityGraphFactoryUnitTests.java | 31 ++++++++++++++----- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java index 9e6c35a7ed..f2c105fbcf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java @@ -15,18 +15,21 @@ */ package org.springframework.data.jpa.repository.support; -import java.util.Set; - import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; import jakarta.persistence.Subgraph; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import org.springframework.data.mapping.PropertyPath; /** * Factory class to create an {@link EntityGraph} from a collection of property paths. * * @author Jens Schauder + * @author Petr Strnad * @since 2.6 */ abstract class EntityGraphFactory { @@ -42,16 +45,22 @@ abstract class EntityGraphFactory { public static EntityGraph create(EntityManager entityManager, Class domainType, Set properties) { EntityGraph entityGraph = entityManager.createEntityGraph(domainType); + Map> existingSubgraphs = new HashMap<>(); for (String property : properties) { Subgraph current = null; + String currentFullPath = ""; for (PropertyPath path : PropertyPath.from(property, domainType)) { + currentFullPath += path.getSegment() + "."; + if (path.hasNext()) { - current = current == null ? entityGraph.addSubgraph(path.getSegment()) - : current.addSubgraph(path.getSegment()); + final Subgraph finalCurrent = current; + current = current == null + ? existingSubgraphs.computeIfAbsent(currentFullPath, k -> entityGraph.addSubgraph(path.getSegment())) + : existingSubgraphs.computeIfAbsent(currentFullPath, k -> finalCurrent.addSubgraph(path.getSegment())); continue; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java index 8dac763c79..c25caab816 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java @@ -15,15 +15,18 @@ */ package org.springframework.data.jpa.repository.support; -import static java.util.Arrays.*; -import static org.mockito.Mockito.*; - -import java.util.HashSet; +import static java.util.Arrays.asList; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; import jakarta.persistence.Subgraph; +import java.util.HashSet; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,6 +34,7 @@ * Unit tests for {@link EntityGraphFactory}. * * @author Jens Schauder + * @author Petr Strnad */ @SuppressWarnings("rawtypes") class EntityGraphFactoryUnitTests { @@ -45,8 +49,7 @@ void beforeEach() { when(em.createEntityGraph(DummyEntity.class)).thenReturn(entityGraph); } - // GH-2329 - @Test + @Test // GH-2329 void simpleSetOfPropertiesGetRegistered() { HashSet properties = new HashSet<>(asList("one", "two")); @@ -57,8 +60,7 @@ void simpleSetOfPropertiesGetRegistered() { verify(entityGraph).addAttributeNodes("two"); } - // GH-2329 - @Test + @Test // GH-2329 void setOfCompositePropertiesGetRegisteredPiecewise() { HashSet properties = new HashSet<>(asList("one.two", "eins.zwei.drei")); @@ -76,6 +78,19 @@ void setOfCompositePropertiesGetRegisteredPiecewise() { verify(zwei).addAttributeNodes("drei"); } + @Test // GH-2571 + void multipleSubNodesUnderSameParentNodeShouldWork() { + + HashSet properties = new HashSet<>(asList("one.one", "one.two")); + + entityGraph = EntityGraphFactory.create(em, DummyEntity.class, properties); + + verify(entityGraph).addSubgraph("one"); + Subgraph one = entityGraph.addSubgraph("one"); + verify(one).addAttributeNodes("one"); + verify(one).addAttributeNodes("two"); + } + private static class DummyEntity { DummyEntity one; DummyEntity two; From 864c7c454dac61eb602674c4123d84e63f23d766 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 2 Jan 2023 09:53:31 +0100 Subject: [PATCH 295/821] Extend license header copyright years to 2023. See #2749 --- .../data/envers/repository/config/EnableEnversRepositories.java | 2 +- .../repository/support/DefaultRevisionEntityInformation.java | 2 +- .../data/envers/repository/support/DefaultRevisionMetadata.java | 2 +- .../envers/repository/support/EnversRevisionRepository.java | 2 +- .../repository/support/EnversRevisionRepositoryFactoryBean.java | 2 +- .../envers/repository/support/EnversRevisionRepositoryImpl.java | 2 +- .../repository/support/ReflectionRevisionEntityInformation.java | 2 +- .../src/test/java/org/springframework/data/envers/Config.java | 2 +- .../repository/support/DefaultRevisionMetadataUnitTests.java | 2 +- .../support/EnversRevisionRepositoryImplUnitTests.java | 2 +- .../repository/support/QueryDslRepositoryIntegrationTests.java | 2 +- .../envers/repository/support/RepositoryIntegrationTests.java | 2 +- .../org/springframework/data/envers/sample/AbstractEntity.java | 2 +- .../java/org/springframework/data/envers/sample/Country.java | 2 +- .../data/envers/sample/CountryQueryDslRepository.java | 2 +- .../springframework/data/envers/sample/CountryRepository.java | 2 +- .../data/envers/sample/CustomRevisionEntity.java | 2 +- .../data/envers/sample/CustomRevisionListener.java | 2 +- .../java/org/springframework/data/envers/sample/License.java | 2 +- .../springframework/data/envers/sample/LicenseRepository.java | 2 +- .../java/org/springframework/data/envers/sample/QCountry.java | 2 +- .../data/jpa/convert/QueryByExamplePredicateBuilder.java | 2 +- .../data/jpa/convert/threeten/Jsr310JpaConverters.java | 2 +- .../org/springframework/data/jpa/domain/AbstractAuditable.java | 2 +- .../springframework/data/jpa/domain/AbstractPersistable.java | 2 +- .../main/java/org/springframework/data/jpa/domain/JpaSort.java | 2 +- .../java/org/springframework/data/jpa/domain/Specification.java | 2 +- .../data/jpa/domain/SpecificationComposition.java | 2 +- .../jpa/domain/support/AuditingBeanFactoryPostProcessor.java | 2 +- .../data/jpa/domain/support/AuditingEntityListener.java | 2 +- .../data/jpa/mapping/JpaMetamodelMappingContext.java | 2 +- .../springframework/data/jpa/mapping/JpaPersistentEntity.java | 2 +- .../data/jpa/mapping/JpaPersistentEntityImpl.java | 2 +- .../springframework/data/jpa/mapping/JpaPersistentProperty.java | 2 +- .../data/jpa/mapping/JpaPersistentPropertyImpl.java | 2 +- .../data/jpa/projection/CollectionAwareProjectionFactory.java | 2 +- .../jpa/provider/HibernateJpaParametersParameterAccessor.java | 2 +- .../org/springframework/data/jpa/provider/HibernateUtils.java | 2 +- .../org/springframework/data/jpa/provider/JpaClassUtils.java | 2 +- .../springframework/data/jpa/provider/PersistenceProvider.java | 2 +- .../org/springframework/data/jpa/provider/ProxyIdAccessor.java | 2 +- .../org/springframework/data/jpa/provider/QueryComment.java | 2 +- .../org/springframework/data/jpa/provider/QueryExtractor.java | 2 +- .../org/springframework/data/jpa/repository/EntityGraph.java | 2 +- .../org/springframework/data/jpa/repository/JpaContext.java | 2 +- .../org/springframework/data/jpa/repository/JpaRepository.java | 2 +- .../data/jpa/repository/JpaSpecificationExecutor.java | 2 +- .../main/java/org/springframework/data/jpa/repository/Lock.java | 2 +- .../main/java/org/springframework/data/jpa/repository/Meta.java | 2 +- .../java/org/springframework/data/jpa/repository/Modifying.java | 2 +- .../java/org/springframework/data/jpa/repository/Query.java | 2 +- .../org/springframework/data/jpa/repository/QueryHints.java | 2 +- .../org/springframework/data/jpa/repository/QueryRewriter.java | 2 +- .../java/org/springframework/data/jpa/repository/Temporal.java | 2 +- .../data/jpa/repository/aot/JpaRuntimeHints.java | 2 +- .../jpa/repository/cdi/BeanManagerQueryRewriterProvider.java | 2 +- .../data/jpa/repository/cdi/JpaRepositoryBean.java | 2 +- .../data/jpa/repository/cdi/JpaRepositoryExtension.java | 2 +- .../jpa/repository/config/AuditingBeanDefinitionParser.java | 2 +- .../data/jpa/repository/config/BeanDefinitionNames.java | 2 +- .../data/jpa/repository/config/EnableJpaAuditing.java | 2 +- .../data/jpa/repository/config/EnableJpaRepositories.java | 2 +- .../data/jpa/repository/config/InspectionClassLoader.java | 2 +- .../data/jpa/repository/config/JpaAuditingRegistrar.java | 2 +- .../config/JpaMetamodelMappingContextFactoryBean.java | 2 +- .../data/jpa/repository/config/JpaRepositoriesRegistrar.java | 2 +- .../jpa/repository/config/JpaRepositoryConfigExtension.java | 2 +- .../jpa/repository/config/JpaRepositoryNameSpaceHandler.java | 2 +- .../data/jpa/repository/query/AbstractJpaQuery.java | 2 +- .../data/jpa/repository/query/AbstractStringBasedJpaQuery.java | 2 +- .../jpa/repository/query/BeanFactoryQueryRewriterProvider.java | 2 +- .../data/jpa/repository/query/DeclaredQuery.java | 2 +- .../data/jpa/repository/query/DefaultJpaEntityMetadata.java | 2 +- .../data/jpa/repository/query/DefaultJpaQueryMethodFactory.java | 2 +- .../data/jpa/repository/query/DefaultQueryEnhancer.java | 2 +- .../data/jpa/repository/query/DelegatingQueryRewriter.java | 2 +- .../data/jpa/repository/query/EmptyDeclaredQuery.java | 2 +- .../data/jpa/repository/query/EscapeCharacter.java | 2 +- .../data/jpa/repository/query/ExpressionBasedStringQuery.java | 2 +- .../jpa/repository/query/InvalidJpaQueryMethodException.java | 2 +- .../data/jpa/repository/query/JSqlParserQueryEnhancer.java | 2 +- .../data/jpa/repository/query/JSqlParserUtils.java | 2 +- .../springframework/data/jpa/repository/query/Jpa21Utils.java | 2 +- .../data/jpa/repository/query/JpaCountQueryCreator.java | 2 +- .../data/jpa/repository/query/JpaEntityGraph.java | 2 +- .../data/jpa/repository/query/JpaEntityMetadata.java | 2 +- .../data/jpa/repository/query/JpaParameters.java | 2 +- .../jpa/repository/query/JpaParametersParameterAccessor.java | 2 +- .../data/jpa/repository/query/JpaQueryCreator.java | 2 +- .../data/jpa/repository/query/JpaQueryExecution.java | 2 +- .../data/jpa/repository/query/JpaQueryFactory.java | 2 +- .../data/jpa/repository/query/JpaQueryLookupStrategy.java | 2 +- .../data/jpa/repository/query/JpaQueryMethod.java | 2 +- .../data/jpa/repository/query/JpaQueryMethodFactory.java | 2 +- .../data/jpa/repository/query/JpaResultConverters.java | 2 +- .../org/springframework/data/jpa/repository/query/Meta.java | 2 +- .../springframework/data/jpa/repository/query/NamedQuery.java | 2 +- .../data/jpa/repository/query/NativeJpaQuery.java | 2 +- .../data/jpa/repository/query/ParameterBinder.java | 2 +- .../data/jpa/repository/query/ParameterBinderFactory.java | 2 +- .../data/jpa/repository/query/ParameterMetadataProvider.java | 2 +- .../data/jpa/repository/query/PartTreeJpaQuery.java | 2 +- .../springframework/data/jpa/repository/query/Procedure.java | 2 +- .../data/jpa/repository/query/ProcedureParameter.java | 2 +- .../data/jpa/repository/query/QueryEnhancer.java | 2 +- .../data/jpa/repository/query/QueryEnhancerFactory.java | 2 +- .../data/jpa/repository/query/QueryParameterSetter.java | 2 +- .../data/jpa/repository/query/QueryParameterSetterFactory.java | 2 +- .../data/jpa/repository/query/QueryRewriterProvider.java | 2 +- .../springframework/data/jpa/repository/query/QueryUtils.java | 2 +- .../data/jpa/repository/query/SimpleJpaQuery.java | 2 +- .../jpa/repository/query/StoredProcedureAttributeSource.java | 2 +- .../data/jpa/repository/query/StoredProcedureAttributes.java | 2 +- .../data/jpa/repository/query/StoredProcedureJpaQuery.java | 2 +- .../springframework/data/jpa/repository/query/StringQuery.java | 2 +- .../data/jpa/repository/support/CrudMethodMetadata.java | 2 +- .../jpa/repository/support/CrudMethodMetadataPostProcessor.java | 2 +- .../data/jpa/repository/support/DefaultJpaContext.java | 2 +- .../data/jpa/repository/support/DefaultQueryHints.java | 2 +- .../data/jpa/repository/support/EntityGraphFactory.java | 2 +- .../EntityManagerBeanDefinitionRegistrarPostProcessor.java | 2 +- .../jpa/repository/support/FetchableFluentQueryByExample.java | 2 +- .../jpa/repository/support/FetchableFluentQueryByPredicate.java | 2 +- .../repository/support/FetchableFluentQueryBySpecification.java | 2 +- .../data/jpa/repository/support/FluentQuerySupport.java | 2 +- .../data/jpa/repository/support/JpaEntityInformation.java | 2 +- .../jpa/repository/support/JpaEntityInformationSupport.java | 2 +- .../jpa/repository/support/JpaEvaluationContextExtension.java | 2 +- .../jpa/repository/support/JpaMetamodelEntityInformation.java | 2 +- .../jpa/repository/support/JpaPersistableEntityInformation.java | 2 +- .../data/jpa/repository/support/JpaRepositoryFactory.java | 2 +- .../data/jpa/repository/support/JpaRepositoryFactoryBean.java | 2 +- .../jpa/repository/support/JpaRepositoryImplementation.java | 2 +- .../data/jpa/repository/support/MutableQueryHints.java | 2 +- .../data/jpa/repository/support/QueryHintValue.java | 2 +- .../springframework/data/jpa/repository/support/QueryHints.java | 2 +- .../springframework/data/jpa/repository/support/Querydsl.java | 2 +- .../jpa/repository/support/QuerydslJpaPredicateExecutor.java | 2 +- .../data/jpa/repository/support/QuerydslJpaRepository.java | 2 +- .../data/jpa/repository/support/QuerydslRepositorySupport.java | 2 +- .../data/jpa/repository/support/SimpleJpaRepository.java | 2 +- .../support/ClasspathScanningPersistenceUnitPostProcessor.java | 2 +- .../data/jpa/support/MergingPersistenceUnitManager.java | 2 +- .../org/springframework/data/jpa/support/PageableUtils.java | 2 +- .../org/springframework/data/jpa/util/BeanDefinitionUtils.java | 2 +- .../springframework/data/jpa/util/HibernateProxyDetector.java | 2 +- .../java/org/springframework/data/jpa/util/JpaMetamodel.java | 2 +- .../springframework/data/jpa/util/JpaMetamodelCacheCleanup.java | 2 +- .../jpa/convert/QueryByExamplePredicateBuilderUnitTests.java | 2 +- .../data/jpa/convert/threeten/DateTimeSample.java | 2 +- .../convert/threeten/Jsr310JpaConvertersIntegrationTests.java | 2 +- .../data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java | 2 +- .../java/org/springframework/data/jpa/domain/JpaSortTests.java | 2 +- .../springframework/data/jpa/domain/SpecificationUnitTests.java | 2 +- .../data/jpa/domain/sample/AbstractAnnotatedAuditable.java | 2 +- .../data/jpa/domain/sample/AbstractMappedType.java | 2 +- .../org/springframework/data/jpa/domain/sample/Account.java | 2 +- .../org/springframework/data/jpa/domain/sample/Address.java | 2 +- .../data/jpa/domain/sample/AnnotatedAuditableUser.java | 2 +- .../data/jpa/domain/sample/AuditableEmbeddable.java | 2 +- .../springframework/data/jpa/domain/sample/AuditableEntity.java | 2 +- .../springframework/data/jpa/domain/sample/AuditableRole.java | 2 +- .../springframework/data/jpa/domain/sample/AuditableUser.java | 2 +- .../data/jpa/domain/sample/AuditorAwareStub.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Child.java | 2 +- .../springframework/data/jpa/domain/sample/ConcreteType1.java | 2 +- .../springframework/data/jpa/domain/sample/ConcreteType2.java | 2 +- .../data/jpa/domain/sample/CustomAbstractPersistable.java | 2 +- .../org/springframework/data/jpa/domain/sample/Customer.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Dummy.java | 2 +- .../data/jpa/domain/sample/EmbeddedIdExampleDepartment.java | 2 +- .../data/jpa/domain/sample/EmbeddedIdExampleEmployee.java | 2 +- .../data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java | 2 +- .../data/jpa/domain/sample/EmployeeWithName.java | 2 +- .../data/jpa/domain/sample/EntityWithAssignedId.java | 2 +- .../data/jpa/domain/sample/IdClassExampleDepartment.java | 2 +- .../data/jpa/domain/sample/IdClassExampleEmployee.java | 2 +- .../data/jpa/domain/sample/IdClassExampleEmployeePK.java | 2 +- .../org/springframework/data/jpa/domain/sample/Invoice.java | 2 +- .../org/springframework/data/jpa/domain/sample/InvoiceItem.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Item.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/ItemId.java | 2 +- .../org/springframework/data/jpa/domain/sample/ItemSite.java | 2 +- .../org/springframework/data/jpa/domain/sample/ItemSiteId.java | 2 +- .../org/springframework/data/jpa/domain/sample/MailMessage.java | 2 +- .../org/springframework/data/jpa/domain/sample/MailSender.java | 2 +- .../org/springframework/data/jpa/domain/sample/MailUser.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Order.java | 2 +- .../springframework/data/jpa/domain/sample/OrmXmlEntity.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Parent.java | 2 +- .../data/jpa/domain/sample/PersistableWithIdClass.java | 2 +- .../data/jpa/domain/sample/PersistableWithIdClassPK.java | 2 +- .../data/jpa/domain/sample/PersistableWithSingleIdClass.java | 2 +- .../data/jpa/domain/sample/PersistableWithSingleIdClassPK.java | 2 +- .../data/jpa/domain/sample/PrimitiveVersionProperty.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Role.java | 2 +- .../springframework/data/jpa/domain/sample/SampleEntity.java | 2 +- .../springframework/data/jpa/domain/sample/SampleEntityPK.java | 2 +- .../data/jpa/domain/sample/SampleWithPrimitiveId.java | 2 +- .../data/jpa/domain/sample/SampleWithTimestampVersion.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/Site.java | 2 +- .../java/org/springframework/data/jpa/domain/sample/User.java | 2 +- .../data/jpa/domain/sample/UserSpecifications.java | 2 +- .../data/jpa/domain/sample/UserWithOptionalField.java | 2 +- .../data/jpa/domain/sample/UserWithOptionalFieldRepository.java | 2 +- .../springframework/data/jpa/domain/sample/VersionedUser.java | 2 +- .../support/AbstractAttributeConverterIntegrationTests.java | 2 +- .../AnnotationAuditingBeanFactoryPostProcessorUnitTests.java | 2 +- .../support/AuditingBeanFactoryPostProcessorUnitTests.java | 2 +- .../data/jpa/domain/support/AuditingEntityListenerTests.java | 2 +- .../support/AuditingEntityWithEmbeddableListenerTests.java | 2 +- .../data/jpa/domain/support/AuditingNamespaceUnitTests.java | 2 +- .../domain/support/QueryByExampleWithOptionalEmptyTests.java | 2 +- .../infrastructure/EclipseLinkMetamodelIntegrationTests.java | 2 +- .../jpa/infrastructure/HibernateMetamodelIntegrationTests.java | 2 +- .../data/jpa/infrastructure/HibernateTestUtils.java | 2 +- .../data/jpa/infrastructure/MetamodelIntegrationTests.java | 2 +- .../jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java | 2 +- .../jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java | 2 +- .../data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java | 2 +- .../data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java | 2 +- .../data/jpa/provider/PersistenceProviderIntegrationTests.java | 2 +- .../data/jpa/provider/PersistenceProviderUnitTests.java | 2 +- .../jpa/repository/AbstractPersistableIntegrationTests.java | 2 +- .../data/jpa/repository/CrudMethodMetadataUnitTests.java | 2 +- .../repository/CustomAbstractPersistableIntegrationTests.java | 2 +- .../data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java | 2 +- .../jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java | 2 +- ...EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java | 2 +- .../jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java | 2 +- .../repository/EclipseLinkParentRepositoryIntegrationTests.java | 2 +- .../EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java | 2 +- .../repository/EclipseLinkStoredProcedureIntegrationTests.java | 2 +- .../jpa/repository/EclipseLinkUserRepositoryFinderTests.java | 2 +- .../EntityGraphRepositoryMethodsIntegrationTests.java | 2 +- .../jpa/repository/EntityWithAssignedIdIntegrationTests.java | 2 +- .../data/jpa/repository/JavaConfigUserRepositoryTests.java | 2 +- .../jpa/repository/MappedTypeRepositoryIntegrationTests.java | 2 +- .../data/jpa/repository/NamespaceUserRepositoryTests.java | 2 +- .../data/jpa/repository/ORMInfrastructureTests.java | 2 +- .../OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java | 2 +- .../jpa/repository/OpenJpaNamespaceUserRepositoryTests.java | 2 +- .../jpa/repository/OpenJpaParentRepositoryIntegrationTests.java | 2 +- .../OpenJpaRepositoryWithCompositeKeyIntegrationTests.java | 2 +- .../jpa/repository/OpenJpaStoredProcedureIntegrationTests.java | 2 +- .../data/jpa/repository/OpenJpaUserRepositoryFinderTests.java | 2 +- .../data/jpa/repository/ParentRepositoryIntegrationTests.java | 2 +- .../repository/QueryByExampleEclipseLinkIntegrationTests.java | 2 +- .../jpa/repository/QueryByExampleHibernateIntegrationTests.java | 2 +- .../data/jpa/repository/RedeclaringRepositoryMethodsTests.java | 2 +- .../data/jpa/repository/RepositoryWithCompositeKeyTests.java | 2 +- .../data/jpa/repository/RepositoryWithIdClassKeyTests.java | 2 +- .../data/jpa/repository/RoleRepositoryIntegrationTests.java | 2 +- .../org/springframework/data/jpa/repository/SPR8954Tests.java | 2 +- .../data/jpa/repository/SimpleJpaParameterBindingTests.java | 2 +- .../data/jpa/repository/StoredProcedureIntegrationTests.java | 2 +- .../data/jpa/repository/UserRepositoryFinderTests.java | 2 +- .../UserRepositoryStoredProcedureIntegrationTests.java | 2 +- .../data/jpa/repository/UserRepositoryTests.java | 2 +- .../data/jpa/repository/aot/JpaRuntimeHintsUnitTests.java | 2 +- .../data/jpa/repository/cdi/CdiExtensionIntegrationTests.java | 2 +- .../data/jpa/repository/cdi/EntityManagerFactoryProducer.java | 2 +- .../repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java | 2 +- .../jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java | 2 +- .../org/springframework/data/jpa/repository/cdi/Person.java | 2 +- .../org/springframework/data/jpa/repository/cdi/PersonDB.java | 2 +- .../data/jpa/repository/cdi/PersonRepository.java | 2 +- .../jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java | 2 +- .../jpa/repository/cdi/QualifiedCustomizedUserRepository.java | 2 +- .../repository/cdi/QualifiedCustomizedUserRepositoryBean.java | 2 +- .../repository/cdi/QualifiedCustomizedUserRepositoryCustom.java | 2 +- .../data/jpa/repository/cdi/QualifiedEntityManagerProducer.java | 2 +- .../data/jpa/repository/cdi/QualifiedFragment.java | 2 +- .../data/jpa/repository/cdi/QualifiedFragmentBean.java | 2 +- .../data/jpa/repository/cdi/QualifiedPersonRepository.java | 2 +- .../data/jpa/repository/cdi/RepositoryConsumer.java | 2 +- .../data/jpa/repository/cdi/SamplePersonRepository.java | 2 +- .../data/jpa/repository/cdi/SamplePersonRepositoryCustom.java | 2 +- .../data/jpa/repository/cdi/SamplePersonRepositoryImpl.java | 2 +- .../springframework/data/jpa/repository/cdi/Transactional.java | 2 +- .../data/jpa/repository/cdi/TransactionalInterceptor.java | 2 +- .../jpa/repository/cdi/UnqualifiedEntityManagerProducer.java | 2 +- .../data/jpa/repository/cdi/UnqualifiedPersonRepository.java | 2 +- .../org/springframework/data/jpa/repository/cdi/UserDB.java | 2 +- .../config/AbstractAuditingViaJavaConfigRepositoriesTests.java | 2 +- .../jpa/repository/config/AbstractRepositoryConfigTests.java | 2 +- .../config/AllowNestedRepositoriesRepositoryConfigTests.java | 2 +- .../repository/config/AuditingBeanDefinitionParserTests.java | 2 +- .../repository/config/CustomRepositoryFactoryConfigTests.java | 2 +- .../config/DefaultAuditingViaJavaConfigRepositoriesTests.java | 2 +- .../config/ExplicitAuditingViaJavaConfigRepositoriesTests.java | 2 +- .../data/jpa/repository/config/InfrastructureConfig.java | 2 +- .../jpa/repository/config/InspectionClassLoaderUnitTests.java | 2 +- .../jpa/repository/config/JpaAuditingRegistrarUnitTests.java | 2 +- .../config/JpaRepositoriesRegistrarIntegrationTests.java | 2 +- .../repository/config/JpaRepositoriesRegistrarUnitTests.java | 2 +- .../config/JpaRepositoryConfigDefinitionParserTests.java | 2 +- .../config/JpaRepositoryConfigExtensionUnitTests.java | 2 +- .../config/JpaRepositoryRegistrationAotProcessorUnitTests.java | 2 +- .../repository/config/NestedRepositoriesJavaConfigTests.java | 2 +- .../data/jpa/repository/config/QueryLookupStrategyTests.java | 2 +- .../data/jpa/repository/config/RepositoriesJavaConfigTests.java | 2 +- .../data/jpa/repository/config/RepositoryAutoConfigTests.java | 2 +- .../data/jpa/repository/config/RepositoryConfigTests.java | 2 +- .../data/jpa/repository/config/TypeFilterConfigTests.java | 2 +- .../data/jpa/repository/custom/CustomGenericJpaRepository.java | 2 +- .../repository/custom/CustomGenericJpaRepositoryFactory.java | 2 +- .../custom/CustomGenericJpaRepositoryFactoryBean.java | 2 +- .../data/jpa/repository/custom/CustomGenericRepository.java | 2 +- .../jpa/repository/custom/UserCustomExtendedRepository.java | 2 +- .../procedures/MySqlStoredProcedureIntegrationTests.java | 2 +- .../procedures/PostgresStoredProcedureIntegrationTests.java | 2 +- .../PostgresStoredProcedureNullHandlingIntegrationTests.java | 2 +- .../repository/projections/ProjectionJoinIntegrationTests.java | 2 +- .../jpa/repository/projections/ProjectionsIntegrationTests.java | 2 +- .../data/jpa/repository/query/AbstractJpaQueryTests.java | 2 +- .../query/AbstractStringBasedJpaQueryIntegrationTests.java | 2 +- .../query/CustomNonBindableJpaParametersIntegrationTests.java | 2 +- .../data/jpa/repository/query/DefaultQueryUtilsUnitTests.java | 2 +- .../data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java | 2 +- .../EclipseLinkParameterMetadataProviderIntegrationTests.java | 2 +- .../repository/query/EclipseLinkQueryUtilsIntegrationTests.java | 2 +- .../data/jpa/repository/query/EscapeCharacterUnitTests.java | 2 +- .../repository/query/ExpressionBasedStringQueryUnitTests.java | 2 +- .../data/jpa/repository/query/Jpa21UtilsTests.java | 2 +- .../data/jpa/repository/query/Jpa21UtilsUnitTests.java | 2 +- .../repository/query/JpaCountQueryCreatorIntegrationTests.java | 2 +- .../data/jpa/repository/query/JpaParametersUnitTests.java | 2 +- .../data/jpa/repository/query/JpaQueryExecutionUnitTests.java | 2 +- .../jpa/repository/query/JpaQueryLookupStrategyUnitTests.java | 2 +- .../data/jpa/repository/query/JpaQueryMethodUnitTests.java | 2 +- .../jpa/repository/query/JpaQueryRewriteIntegrationTests.java | 2 +- .../data/jpa/repository/query/LikeBindingUnitTests.java | 2 +- .../MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java | 2 +- .../MetaAnnotatedQueryMethodHibernateIntegrationTests.java | 2 +- .../jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java | 2 +- .../query/NamedOrIndexedQueryParameterSetterUnitTests.java | 2 +- .../data/jpa/repository/query/NamedQueryUnitTests.java | 2 +- .../data/jpa/repository/query/OpenJpaJpa21UtilsTests.java | 2 +- .../query/OpenJpaParameterMetadataProviderIntegrationTests.java | 2 +- .../jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java | 2 +- .../data/jpa/repository/query/ParameterBinderUnitTests.java | 2 +- .../jpa/repository/query/ParameterBindingParserUnitTests.java | 2 +- .../jpa/repository/query/ParameterExpressionProviderTests.java | 2 +- .../query/ParameterMetadataProviderIntegrationTests.java | 2 +- .../repository/query/ParameterMetadataProviderUnitTests.java | 2 +- .../jpa/repository/query/PartTreeJpaQueryIntegrationTests.java | 2 +- .../jpa/repository/query/QueryEnhancerFactoryUnitTests.java | 2 +- .../data/jpa/repository/query/QueryEnhancerUnitTests.java | 2 +- .../repository/query/QueryParameterSetterFactoryUnitTests.java | 2 +- .../data/jpa/repository/query/QueryUtilsIntegrationTests.java | 2 +- .../data/jpa/repository/query/QueryUtilsUnitTests.java | 2 +- .../query/QueryWithNullLikeHibernateIntegrationTests.java | 2 +- .../data/jpa/repository/query/SimpleJpaQueryUnitTests.java | 2 +- .../query/StoredProcedureAttributeSourceUnitTests.java | 2 +- .../repository/query/StoredProcedureAttributesUnitTests.java | 2 +- .../data/jpa/repository/query/StringQueryUnitTests.java | 2 +- .../data/jpa/repository/query/TupleConverterUnitTests.java | 2 +- .../jpa/repository/sample/AnnotatedAuditableUserRepository.java | 2 +- .../data/jpa/repository/sample/AuditableEntityRepository.java | 2 +- .../data/jpa/repository/sample/AuditableUserRepository.java | 2 +- .../data/jpa/repository/sample/CategoryRepository.java | 2 +- .../data/jpa/repository/sample/ClassWithNestedRepository.java | 2 +- .../data/jpa/repository/sample/ConcreteRepository1.java | 2 +- .../data/jpa/repository/sample/ConcreteRepository2.java | 2 +- .../repository/sample/CustomAbstractPersistableRepository.java | 2 +- .../data/jpa/repository/sample/DummyRepository.java | 2 +- .../jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java | 2 +- .../jpa/repository/sample/EmployeeRepositoryWithIdClass.java | 2 +- .../jpa/repository/sample/EntityWithAssignedIdRepository.java | 2 +- .../data/jpa/repository/sample/ItemRepository.java | 2 +- .../data/jpa/repository/sample/ItemSiteRepository.java | 2 +- .../data/jpa/repository/sample/MailMessageRepository.java | 2 +- .../data/jpa/repository/sample/MappedTypeRepository.java | 2 +- .../springframework/data/jpa/repository/sample/NameOnlyDto.java | 2 +- .../data/jpa/repository/sample/ParentRepository.java | 2 +- .../data/jpa/repository/sample/ProductRepository.java | 2 +- .../sample/RedeclaringRepositoryMethodsRepository.java | 2 +- .../RepositoryMethodsWithEntityGraphConfigRepository.java | 2 +- .../data/jpa/repository/sample/RoleRepository.java | 2 +- .../data/jpa/repository/sample/RoleRepositoryWithMeta.java | 2 +- .../data/jpa/repository/sample/SampleConfig.java | 2 +- .../jpa/repository/sample/SampleEvaluationContextExtension.java | 2 +- .../data/jpa/repository/sample/SiteRepository.java | 2 +- .../data/jpa/repository/sample/UserRepository.java | 2 +- .../data/jpa/repository/sample/UserRepositoryCustom.java | 2 +- .../data/jpa/repository/sample/UserRepositoryImpl.java | 2 +- .../CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java | 2 +- .../repository/support/DefaultJpaContextIntegrationTests.java | 2 +- .../data/jpa/repository/support/DefaultJpaContextUnitTests.java | 2 +- .../repository/support/DefaultJpaEntityMetadataUnitTest.java | 2 +- .../data/jpa/repository/support/DefaultQueryHintsTest.java | 2 +- .../support/DefaultTransactionDisablingIntegrationTests.java | 2 +- ...clipseLinkJpaMetamodelEntityInformationIntegrationTests.java | 2 +- .../jpa/repository/support/EclipseLinkJpaRepositoryTests.java | 2 +- .../jpa/repository/support/EclipseLinkProxyIdAccessorTests.java | 2 +- .../jpa/repository/support/EntityGraphFactoryUnitTests.java | 2 +- ...gerBeanDefinitionRegistrarPostProcessorIntegrationTests.java | 2 +- ...ityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java | 2 +- .../jpa/repository/support/EntityManagerFactoryRefTests.java | 2 +- .../repository/support/EntityManagerFactoryRefUnitTests.java | 2 +- .../support/FetchableFluentQueryByExampleUnitTests.java | 2 +- .../support/FetchableFluentQueryByPredicateUnitTests.java | 2 +- .../HibernateJpaMetamodelEntityInformationIntegrationTests.java | 2 +- .../JavaConfigDefaultTransactionDisablingIntegrationTests.java | 2 +- .../support/JpaEntityInformationSupportUnitTests.java | 2 +- .../support/JpaMetamodelEntityInformationIntegrationTests.java | 2 +- .../support/JpaMetamodelEntityInformationUnitTests.java | 2 +- .../support/JpaPersistableEntityInformationUnitTests.java | 2 +- ...RepositoryFactoryBeanEntityPathResolverIntegrationTests.java | 2 +- .../repository/support/JpaRepositoryFactoryBeanUnitTests.java | 2 +- .../jpa/repository/support/JpaRepositoryFactoryUnitTests.java | 2 +- .../data/jpa/repository/support/JpaRepositoryTests.java | 2 +- .../support/MailMessageRepositoryIntegrationTests.java | 2 +- .../data/jpa/repository/support/MutableQueryHintsUnitTests.java | 2 +- .../data/jpa/repository/support/OpenJpaJpaRepositoryTests.java | 2 +- .../OpenJpaMetamodelEntityInformationIntegrationTests.java | 2 +- .../jpa/repository/support/OpenJpaProxyIdAccessorTests.java | 2 +- .../support/QSimpleEntityPathResolverUnitTests_Sample.java | 2 +- .../data/jpa/repository/support/QuerydslIntegrationTests.java | 2 +- .../support/QuerydslJpaPredicateExecutorUnitTests.java | 2 +- .../data/jpa/repository/support/QuerydslJpaRepositoryTests.java | 2 +- .../support/QuerydslRepositorySupportIntegrationTests.java | 2 +- .../jpa/repository/support/QuerydslRepositorySupportTests.java | 2 +- .../jpa/repository/support/SimpleJpaRepositoryUnitTests.java | 2 +- .../jpa/repository/support/TransactionalRepositoryTests.java | 2 +- .../XmlConfigDefaultTransactionDisablingIntegrationTests.java | 2 +- .../ClasspathScanningPersistenceUnitPostProcessorUnitTests.java | 2 +- .../data/jpa/support/EntityManagerTestUtils.java | 2 +- .../jpa/support/MergingPersistenceUnitManagerUnitTests.java | 2 +- .../test/java/org/springframework/data/jpa/util/FixedDate.java | 2 +- .../org/springframework/data/jpa/util/HidingClassLoader.java | 2 +- .../data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java | 2 +- .../springframework/data/jpa/util/JpaMetamodelUnitTests.java | 2 +- 434 files changed, 434 insertions(+), 434 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java index 632ef5fa95..be1b9104b6 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/config/EnableEnversRepositories.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java index e295529fe5..32d2d52860 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java index bf7227bc94..2d9e813af9 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java index 9d63891480..ab861c1e5c 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java index 1006e3aa3c..bb83c0b60c 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index 537c667830..2309fe1ebd 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java index c441ef9542..8d047d4140 100644 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/ReflectionRevisionEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java b/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java index 0d30f435a4..b4d5fef0a1 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/Config.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java index 44008e5e87..b24b7f59c0 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/DefaultRevisionMetadataUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java index df9acf0953..bf63f7f034 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImplUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java index c60011a146..35725c0736 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/QueryDslRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java index 49d15bed9b..77a410219c 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java index 6868bff5a5..caf5355dec 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java index ce6e02de2c..a2818be011 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java index cab7f48f13..625c87ad47 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryQueryDslRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java index 7a9f0fee84..b9e3b72ed2 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CountryRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionEntity.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionEntity.java index a475f1cba9..a5064f870f 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionEntity.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionListener.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionListener.java index 897b439e39..9aa2b7c5fe 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionListener.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/CustomRevisionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java index d9f68949b5..32ccae2967 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java index e090011094..fe0008b244 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/LicenseRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java index c218e551ea..4db8632567 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/QCountry.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java index 6949090e4c..2f0c162936 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java index 78d7d403d8..712f91c8f7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java index 2c4e1122dd..acd2cac848 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java index de1355b35c..0a21277c0d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index 5adc6fab85..e8f8b57b86 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java index c656cfa9f6..a5b39ef017 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java index 70c864126b..10b9b5948d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java index d76ca704ca..684138d739 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java index af68cf47c9..501f536472 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index a42e772660..8138692776 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java index 8feb18c024..1d3717806c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index e55d44a230..f21bd5ee8c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java index c23744af15..23f1c036c7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index 6ecb5270e3..a3991e9fa6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java index da3b398451..5892f1243b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/projection/CollectionAwareProjectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index 38eba8d73d..6b9f2628c2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java index 2056db61d8..92983f1343 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java index ab68486d8e..771979c56c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index bb821b1d51..6468bcaf06 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java index 7690829620..b0d34ffe9f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryComment.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryComment.java index 0f50048286..21f7266117 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryComment.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryComment.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java index f2429b0107..192a76eb7e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java index 1e520c8f03..79de53344b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/EntityGraph.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java index b233ab2f93..c08dc09aa1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index b1425ef8bb..062c41ea67 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index 0ba4d03716..e7529f6e20 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java index cd0389a6e9..90502d4fa0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Lock.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Meta.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Meta.java index c5ec418fb2..08c01b840a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Meta.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Meta.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java index a31288d070..691bde2729 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Modifying.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java index 6a5aadaf18..d626507f24 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Query.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java index 9ec19baa38..e0cc0a764f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java index 426a08df6c..39d6373db9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/QueryRewriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java index f94a2d5168..d855eac2b0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/Temporal.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java index 8141aa4791..b336e7cb1d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java index d83a578b01..98ea3fd27e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/BeanManagerQueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index 9073a843f9..7704285a32 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index 9cdb3bba1e..9a218c4abc 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java index 79379625b7..8ea304200a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java index 322488aad4..657231f51f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/BeanDefinitionNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java index b462df485f..6224012c5f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java index fa2241dc78..42bddab4ff 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaRepositories.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java index 4a5d3954f5..d9a8cb51e7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/InspectionClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java index 2116c5789a..4dad83e2ad 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index 1d3d10b0fe..6d8d1c01af 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java index d1ba38f816..5ec3433582 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index bafde60780..dc47c51c96 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java index d9ba90bd20..86cf772e10 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryNameSpaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 3e3dbd7aad..70ec15d0e6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index 64e61530f6..e3a2ff1ce8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java index bf34780a42..52eab20fe2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BeanFactoryQueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java index 2359260dab..500e64d1ac 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java index de2d8416a7..52ee85a4a2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java index 62851a2d79..27310fbe8d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaQueryMethodFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java index 7dd4f9ce2a..53a07bf6f9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java index 27aec0f9ab..86b81b78fb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DelegatingQueryRewriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java index a2cf998401..dd37c45081 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java index 840ea8a767..a6d1e18d9b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EscapeCharacter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index 8bb25aa6c6..837e22da2d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java index d3a9e4f8c3..9169df4ab2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/InvalidJpaQueryMethodException.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 0efa084c1e..7a44873ac4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java index 4fbf0bf9ca..02b8b47116 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java index 30152a169b..a2d721302b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index 29e43080f0..0d4bda7faa 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java index 759a143e28..a678415454 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java index 609c5ed054..3c4fb690ed 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index 329c01335d..428c0ec18d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java index 7870023405..fb1ff1b8b6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 16074ed2cb..b6def0ee40 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 0080670756..6ee5ec163a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 63b0bffe60..8a91b5ea5c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index 7fcab82bd5..8083f49979 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index 883e600217..ed566ba52c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java index 135d890692..2df2bc0bca 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethodFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java index 77dba961c9..0fcbd763d9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java index 73eafc218d..a3e07cf14d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index b2b8a66d8a..45442a4c49 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java index 205e7d1f23..0cff04d2c4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NativeJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 6aa5b991b8..9298308d64 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java index 77ebedf3d8..2ff2bc46dd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 0176db19f1..b2186abef4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 7ec72f9565..8bfbb72a49 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java index db62ce736d..152d882cba 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Procedure.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java index 0c67cf159c..a9aa3c4dd1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ProcedureParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java index 1c470ae671..9f571acb16 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancer.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java index 8365122cdd..d329251649 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index 8e0d0c2905..23ff9deed1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index 1b21b0f8eb..5cc137d820 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java index ed955d9d51..523f790d5e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryRewriterProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index cdd49c6241..5707caec26 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index c2e23738d5..8fcef0a14e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java index 8d321db926..1207264c4f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java index 40cfcd2443..755c211d63 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index e7a5b02f21..24c87d1ebe 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 712a27871d..32b5e24301 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java index e5c2dbf6a7..359df17cbb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 006e883ff9..3128fed675 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java index 4f0bb1dc00..50aacac514 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultJpaContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java index 67d19a5509..58a844bfdb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/DefaultQueryHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java index f2c105fbcf..626e42444d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index df78cffe78..f4e953576d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java index 8e27ab733b..d1ddea66ba 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index 9eea021af0..61268e6087 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java index 1183936e74..74d2d67afd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java index 8b9c352ca0..4ac26874a6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java index 71345b64d7..f8b438ff82 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java index 9c94edbc44..2f38648277 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java index f9495ae9e6..5344096578 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEvaluationContextExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index 7689bf2d6b..fa64f1cc51 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java index 1672d5e53f..ad005cd374 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 12adf8f2b6..f135f1e76d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index f866ef06b1..d5dac56cdc 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java index c9d7962fb5..bbe56cfd12 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java index a7b292626a..7cd8f049fa 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/MutableQueryHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java index 7052de795d..db7728aec0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHintValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java index 8f4ea196b8..24407f10ba 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QueryHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index cef0b06d17..1f4c5883e7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 1de39e2c53..2396e39ae3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index d082e65a68..484c61e3f9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java index 89a49edab9..15075375e4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 17abebbd3d..dfb77272d6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java index 77b91782d2..0a85edba48 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index 40d8224aa3..fb60cdfac5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java index 932879629c..a878528564 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/PageableUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java index dfa6ca5258..779be14457 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java index 0653837d5b..e7954163e4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/HibernateProxyDetector.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java index e34c940ebf..5d1b4ea7e6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java index 17075fa707..2de7d78a07 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanup.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java index 1d9fec4104..51b3563190 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java index 8023c2720e..df2d2f84f0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/DateTimeSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java index f51723d949..9a6455f5a2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java index 9988d26c97..41a9c1041b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java index 574cb39126..b0e2566b80 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java index 8f08e7d6f7..bc725ffffd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java index 60224d7ffe..7c66e46e20 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractAnnotatedAuditable.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java index 213fb2a869..0cf8abfe72 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java index 93019b766e..a9075f91c0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Account.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java index cae480d065..c274774dda 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Address.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java index f52c5af12b..c1ca014284 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AnnotatedAuditableUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEmbeddable.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEmbeddable.java index c66b7222c5..0f4c5419a5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEmbeddable.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEmbeddable.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEntity.java index fa294462d8..a3d06862bb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEntity.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java index 009470503a..af822032aa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableRole.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java index 6d0ce9f632..3da837852f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java index 63570f18a4..a5eb7dacdc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java index 8e3b002dd1..618b06e30d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Child.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java index ec5504a018..a0022b9c24 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType1.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java index efacee7266..0b84a3345c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ConcreteType2.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java index 11a678a22d..63b16ecfcd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/CustomAbstractPersistable.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java index 632d3a45d9..cfdace6c9d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Customer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java index 48a5fc228e..d5832473d0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Dummy.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java index 73507abc66..211c310dc3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleDepartment.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java index af25ce426f..f456a377ca 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployee.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java index fde9a12754..9de2969fd7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmbeddedIdExampleEmployeePK.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java index 3770598407..ddcb19e0c5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java index 649449dd77..f0c0b9ba7a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EntityWithAssignedId.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java index 7c69bfd6f9..0b79ff07fe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleDepartment.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java index 5499a052ba..0c0351cfe5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployee.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java index 3469136741..b599df89ff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/IdClassExampleEmployeePK.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java index 61c9fb9cef..c4e525c15c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Invoice.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java index 43206a0652..8082cdbb8c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/InvoiceItem.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java index 3b7bf6004f..c2364a298b 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java index 13e6b3f792..441bf23f8e 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemId.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java index f465908470..9e8f5db1ff 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSite.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java index 148d71b9db..681aca4c9c 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/ItemSiteId.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java index b27cc536d1..6e61bfdd00 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java index 5957026622..c8a86ec46c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java index 9ff6e812f7..8cca98e177 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/MailUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java index 5fc14ce2b6..807e437249 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Order.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java index d7f1246c12..6321041472 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OrmXmlEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java index baa2e9784d..0703624c1e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Parent.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java index afa47f03e3..2cd9649722 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java index b76d35eb78..72415a8d37 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithIdClassPK.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java index 80ca79d417..c8db41dd6e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java index 9397153d51..caddb1da0b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PersistableWithSingleIdClassPK.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java index 0d75871baa..b548629335 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/PrimitiveVersionProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java index 82efbac73b..b3262d4c7f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Role.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java index 654838802c..9237a22ce7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java index e3cacb1243..8cddfe601b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleEntityPK.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java index c5979f58c6..116e8a1336 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithPrimitiveId.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java index 7ca9ccef61..f0d359653c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithTimestampVersion.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java index 2e5361aaef..69f0e06020 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Site.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java index 4521b97926..f61e269d68 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java index 5e0b710127..451b532c81 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java index f11eef3f2b..f38eeae33f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java index edf5f9d2ea..9313ad2a69 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java index 68ee1c9685..71fe80ceb4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/VersionedUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java index 22628d794f..4ce334ac2e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AbstractAttributeConverterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java index da8e9a80b0..99162149ad 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java index a3c4c48498..dcf2544bd7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java index fa545395b8..8c9fbb1163 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java index 8f94078e5f..91bab5b4e0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java index 1d870a5b8f..96e7c18beb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java index 23bc7a115a..05da8ab442 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java index e45e92937d..347e58fb42 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/EclipseLinkMetamodelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java index 96273a6d96..c339a3f11d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateMetamodelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java index 5e4cfb9159..289d38d833 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java index 67156d04d4..06833828c0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/MetamodelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java index d4ff975cf5..c4b6c20aa0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/OpenJpaMetamodelIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java index 5f50b9f843..127abcedd7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java index 406837bf75..033b05c68f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index 42cff4e0ce..813f2deb86 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java index 7d2b088265..392b5b3ad9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java index f7159719f7..9e97707026 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java index 789dca91ef..83e5b43f5b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java index 84236f7d22..c16bacba55 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CrudMethodMetadataUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java index 72107459b5..5873cd8342 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java index c171a0f58b..ce71d824cf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomEclipseLinkJpaVendorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java index 8201238284..46ce40e5e1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomHsqlHibernateJpaVendorAdaptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java index 45bc1e056d..2a993852a6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkEntityGraphRepositoryMethodsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java index fa58802e68..69950ec477 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java index 6262abce8c..6b1f752e5e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkParentRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java index 7854036800..4cbe8e72c5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkRepositoryWithCompositeKeyIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java index bf06c43e8b..94dca00327 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java index d20bede602..0caf735d4e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index ba817878b5..10310a9bbd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java index 0848bd6a18..f68e0e9309 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java index 0a5018adcd..f25f016bf7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/JavaConfigUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java index bee615f112..dca475e643 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java index 2d333fb388..1115aab9fb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java index 0ab0674303..326443e5a8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java index 8dd4f9f7d6..77b1d3faf8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaEntityGraphRepositoryMethodsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java index f062fc4bff..64ad5f65cd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java index 6183bfd0ac..da5b96e262 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaParentRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java index f4d458a512..49973afcfd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaRepositoryWithCompositeKeyIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java index 5886b0b6cf..5bedb64e6d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java index 15ae9bb62c..54fbb08a7a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaUserRepositoryFinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java index bcafd2e896..1aa33e504b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java index 0016bc8523..2a119d713f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java index 510cf4abb0..846fd157a9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java index 51138c8abd..5ce3a472b8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index 657993cb52..3ec47cb8f1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java index 9c4cb8d593..25a74ca8f6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java index caf33c3a1c..261ab919e4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java index e6a83ade5e..543b7d788c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java index 8ddf03b933..3c1e77f825 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java index 65a9ac41d1..f7ff27211e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 2046127efa..36715fa12e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java index 8ec50cc883..2fbc17e058 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 4d32e8b5c0..b912aa2aaa 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 @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHintsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHintsUnitTests.java index c457ffcf80..0caebfd4f0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHintsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHintsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java index 574682081c..d1f4ee7cea 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java index bbddb6e31e..70c9fcaa8a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/EntityManagerFactoryProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java index 596b46d90c..4a34bebe9f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java index 09b53c9a4f..0e558984df 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java index 398c1e8a84..7e88b21b9e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Person.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java index bb927f5c49..a560aebcd9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonDB.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java index 389c7b730e..697c7a2eb7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/PersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java index b03959c617..2c0962aa93 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedCdiConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java index bb0794c4c5..68136b1327 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java index 93860c7397..bc2e36f79a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java index faa112a39b..6d8a357ed7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedCustomizedUserRepositoryCustom.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java index 07840ca545..4d560b6d0e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedEntityManagerProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java index 0dfc7bbed0..071f8c2fbc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java index beb2578c5d..745be1f475 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedFragmentBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java index c8c033a6fe..1719334d54 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/QualifiedPersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java index 2dc0b545f3..92d6bf803a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/RepositoryConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java index 70e9c97889..29c3d918fb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java index 5433db8803..37dee09c38 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryCustom.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java index 18c23fb0b0..09034da6b2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/SamplePersonRepositoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java index cb7fc0aefe..1dc4d5131e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/Transactional.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java index 1dcdea9c62..58bc728b3e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/TransactionalInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java index f17b285ff6..13a1abfa11 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedEntityManagerProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java index 5b5631ffcd..c09176cb99 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UnqualifiedPersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java index 130d7e5870..3d2d19ccd6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/UserDB.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java index 3dfa45e7dd..2b732fe822 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java index 5545b1e708..6712a4c1b6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java index 42c85d02fb..27ff1eb7a9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java index bd36fdf8e1..cdfbdb3525 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java index 8a15d3b208..6d260f8554 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java index e4d59b8249..7ed717490a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java index 24e39fe37a..45757f3fbe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java index ac6e21b36a..a4f76aadbb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java index 03e3db11ba..9be877e6b6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/InspectionClassLoaderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java index f61c5d5947..94f8df900c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrarUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java index 585e28d600..52512d743c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java index 5b10d4f1f1..e1d8718fbd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java index e5c45f9d35..e4c8ae3906 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java index a3e4ac9eab..65388887f9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java index 86e0824ab2..8ce62c12c5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java index 88bcd9b890..23ebca9de6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java index cb661bae29..288119bd4f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java index 80633bb843..a9a33c549b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java index c87614255b..2b1ebf5e77 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryAutoConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java index a64b77948f..0b7de2111c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java index 1dfd1e00fb..7e8c948524 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/TypeFilterConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java index 3c7f560a2b..6e2864583a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java index 0d9671113e..c065168a01 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java index 5bcd29793f..59e4e9fed6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericJpaRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java index 9087f904ea..a45e8796b2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/CustomGenericRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java index 1ab2b1137c..a1d7c9d3d6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/custom/UserCustomExtendedRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java index aa011c4323..eec0cdaa2b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index cbd325a2e9..c812307f53 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java index 3b7ea6e04b..6f494ef696 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java index e188672ef0..c3465f431c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java index 0117b57691..e638fc2ad6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index 9f06195de0..0320570ce8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index 33abbeebb9..a36e9821cc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java index 5be0d3be7f..7e9333b85c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java index 828f8a835b..89dba5b884 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java index 98f75756af..4a5612b2ca 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkJpa21UtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java index 1b8539ef55..f0874820bf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkParameterMetadataProviderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java index d3edf8552a..cad29766ff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkQueryUtilsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java index 1edd78e9a7..bc96b58546 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EscapeCharacterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index 677cbc4f25..874656158a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java index e2192de93d..fe74cd245b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java index 19ec8cdad5..59b54f3a0f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java index ef48b68fc0..49fca35ec6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java index 57c702a575..14d3fe3749 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java index 179038c88f..7be1ec4d9e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index d95ca23060..8eb9037fc0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 10579e2971..564493d7ae 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java index 37497195e7..6b9f02acf7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java index a18391405b..555aa23d96 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java index e5390cabb1..b14b5530dc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java index cbde086ad1..3c1d96152e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java index b2ecca217d..f4dd7f81eb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java index 9d59b2b503..9440a81b51 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java index 0bd6ff3cbe..8e70800e97 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java index b15353b52f..cada4feefa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaJpa21UtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java index af2dc6e670..9ea90dda3e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaParameterMetadataProviderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java index 203ba22602..84548cd2d0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/OpenJpaQueryUtilsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java index acaf70cd0b..0ad1439f67 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java index 742dcf045a..e108b12a12 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java index c8a1ddeada..f3b16833c2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java index 5923b36610..85f2816992 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java index fd3ba4a7cf..e30d742b76 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index b002b07bea..f5c3faf8bf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License import org.springframework.aop.framework.Advised; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java index a6248e5a6c..4f342f51eb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 c082840c2a..de3da09f59 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 @@ -1,5 +1,5 @@ /* - * Copyright 2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java index 832dbc0ffd..b10df30d37 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index 11776da9fd..2846fe01c4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 431569d1f3..45ce3b75e8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java index c8b24b0497..710b4f927a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 8ebb5a4e8c..5be979d1c6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java index 76d56669df..41d9b18643 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSourceUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java index ea9698f5cc..a99f650449 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributesUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 4746f27106..9203e9dbbd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java index b3c3f60abf..7df85fefc7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java index da986021f0..edc8d5b58b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AnnotatedAuditableUserRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableEntityRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableEntityRepository.java index 574e1abc9c..6d262c8e8c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableEntityRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableEntityRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java index 004f131ae2..97934d8988 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/AuditableUserRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java index fce45c4a2b..f60ae35d20 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CategoryRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java index da3c8954f5..3cf8aeaf83 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ClassWithNestedRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java index 39dcd73955..64953384f1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository1.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java index 692aa9d700..6c6204f77a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ConcreteRepository2.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java index d8466e756e..33862092fc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/CustomAbstractPersistableRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java index 1c43328396..494f6c23e9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/DummyRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java index e57839669c..6f7397cdbb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithEmbeddedId.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java index 86c75537c8..305cb58a04 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EmployeeRepositoryWithIdClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java index 1a6e5eeeee..65ef93b6f3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/EntityWithAssignedIdRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java index 8afa950a6f..bbf051e231 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java index 0914eb6cec..bc83f28f00 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemSiteRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java index bb321fc3f9..b25896d67e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java index 2d8e7f006f..2197cd6242 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java index 295d142eb9..543fa28386 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/NameOnlyDto.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java index 0992c61d78..e7d7e6cdcf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ParentRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java index de874a708c..62347f9d03 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ProductRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java index a605423e40..f641c5426a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RedeclaringRepositoryMethodsRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java index b48b1ec23b..a534783754 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RepositoryMethodsWithEntityGraphConfigRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java index d161811112..da75265653 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepositoryWithMeta.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepositoryWithMeta.java index 79b4dbe13a..cbe728591a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepositoryWithMeta.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/RoleRepositoryWithMeta.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java index fb1ad9c33c..321d6149df 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java index 1e207955ca..cfa7ade47f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SampleEvaluationContextExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java index fccba7a7df..5a7043188e 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/SiteRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 the original author or authors. + * Copyright 2016-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 7bf350f9f2..6ff229d87c 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 @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java index d345ba7135..261c15b5ad 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryCustom.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java index 4757e2a543..54814034b9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepositoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java index 1eab934632..e23474fb3f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java index ebe084f093..92013297d6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java index 9eda1e8c66..869956a6c1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java index 28b8b9b789..091ea0cd34 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java index bc6bdb9e01..69febf025a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultQueryHintsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java index 62cdfad6cf..14b204c5a3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultTransactionDisablingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java index 3c0a2bf828..6be61b9cde 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaMetamodelEntityInformationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java index bb16ec76c5..3f3d5e30fc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkJpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java index 6d41f58577..0b5d5ec1a7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EclipseLinkProxyIdAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java index c25caab816..c332ad70dc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityGraphFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the original author or authors. + * Copyright 2021-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java index 5776d82b09..e9dc44d405 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java index 3601db32e6..15eebed827 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java index 79fcc98a88..41f9b4d22f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java index 3073ad20e0..cc46d5953c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java index 81b469b1ea..ca6c80f2fe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java index a3bb811baa..0691c6e87a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2022 the original author or authors. + * Copyright 2022-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java index 4a12e8463e..f11a30b9a7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java index 06b2145e30..e2ce1d06a3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JavaConfigDefaultTransactionDisablingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java index 40806e1208..099c770ffd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java index 14bab1fd1d..1545a21ae8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java index a3730c19f8..b9ef143792 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java index 298b310bb0..0eacdec578 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java index 41348a2de4..345a71e15d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java index a60e1dd1cc..02cc100030 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index c632917f72..68274c672a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java index 9b5860e1d2..6d46876e6e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java index 64673e79ef..2306611c4c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java index bd66b3d4d3..2605070d80 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MutableQueryHintsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java index d76fa2555f..f7eebf3921 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaJpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java index 76da1e834a..6413ada7aa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaMetamodelEntityInformationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java index 8122464dcb..b6b270bf67 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/OpenJpaProxyIdAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java index a229178ff3..d6a613c84c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QSimpleEntityPathResolverUnitTests_Sample.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java index b980d68f15..8fd8ac83b0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index ca9b6d4976..996ccdf10a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java index e09cab8fc3..bdd8a87fe9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java index cddc3c153d..8a14108d93 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java index afcdb8e428..cfd307544b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java index d0ad52aace..65ecc0c62d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java index 9739d2606e..a138b2562b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java index a9dcaf95e7..0205bd0fbc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/XmlConfigDefaultTransactionDisablingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java index 19edebe0e4..9c95e51e50 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java index be55fbf169..7ebba2e1a3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/EntityManagerTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java index 3c1729a353..3855c234af 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2022 the original author or authors. + * Copyright 2011-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java index 44cb4dd28e..b025cea32d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/FixedDate.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HidingClassLoader.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HidingClassLoader.java index f68a87220b..e9046a2428 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HidingClassLoader.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HidingClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java index d1175edb7d..ec1c3f13bd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java index 5efaa467ac..db7b6edd3c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2022 the original author or authors. + * Copyright 2018-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From c510e28fa930ed4c2fef48f5b4ab943727898a76 Mon Sep 17 00:00:00 2001 From: Krzysztof Krason Date: Sat, 31 Dec 2022 14:51:00 +0100 Subject: [PATCH 296/821] Refactored AssertJ assertions into more readable ones. Resolves #2746. --- .../support/RepositoryIntegrationTests.java | 2 +- .../Jsr310JpaConvertersIntegrationTests.java | 4 +- .../support/AuditingEntityListenerTests.java | 10 +-- .../PersistenceProviderIntegrationTests.java | 3 +- ...lipseLinkNamespaceUserRepositoryTests.java | 4 +- ...raphRepositoryMethodsIntegrationTests.java | 27 ++++--- .../MappedTypeRepositoryIntegrationTests.java | 7 +- .../OpenJpaNamespaceUserRepositoryTests.java | 3 +- .../ParentRepositoryIntegrationTests.java | 13 +-- .../RedeclaringRepositoryMethodsTests.java | 5 +- .../RepositoryWithCompositeKeyTests.java | 3 +- .../RepositoryWithIdClassKeyTests.java | 3 +- .../RoleRepositoryIntegrationTests.java | 9 +-- .../data/jpa/repository/SPR8954Tests.java | 6 +- .../SimpleJpaParameterBindingTests.java | 5 +- .../StoredProcedureIntegrationTests.java | 13 +-- .../repository/UserRepositoryFinderTests.java | 7 +- .../jpa/repository/UserRepositoryTests.java | 81 +++++++++---------- .../cdi/CdiExtensionIntegrationTests.java | 5 +- .../cdi/JpaRepositoryExtensionUnitTests.java | 3 +- ...uditingViaJavaConfigRepositoriesTests.java | 5 +- ...RepositoryConfigDefinitionParserTests.java | 3 +- .../query/AbstractJpaQueryTests.java | 9 ++- .../jpa/repository/query/Jpa21UtilsTests.java | 19 ++--- .../JpaQueryRewriteIntegrationTests.java | 3 +- .../PartTreeJpaQueryIntegrationTests.java | 3 +- .../query/QueryEnhancerUnitTests.java | 4 +- .../query/QueryUtilsIntegrationTests.java | 3 +- .../query/SimpleJpaQueryUnitTests.java | 7 +- .../query/StringQueryUnitTests.java | 2 +- .../EntityManagerFactoryRefUnitTests.java | 7 +- ...PersistableEntityInformationUnitTests.java | 3 +- .../JpaRepositoryFactoryUnitTests.java | 5 +- .../support/JpaRepositoryTests.java | 10 +-- ...QuerydslJpaPredicateExecutorUnitTests.java | 9 ++- .../support/QuerydslJpaRepositoryTests.java | 9 ++- .../QuerydslRepositorySupportTests.java | 17 ++-- .../support/TransactionalRepositoryTests.java | 7 +- ...ergingPersistenceUnitManagerUnitTests.java | 15 ++-- ...MetamodelCacheCleanupIntegrationTests.java | 3 +- 40 files changed, 189 insertions(+), 167 deletions(-) diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java index 77a410219c..5acf652424 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java @@ -47,6 +47,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = Config.class) @@ -192,7 +193,6 @@ void findsDeletedRevisions() { assertThat(revisions).hasSize(2); assertThat(revisions.getLatestRevision().getEntity()) // - .isNotNull() // .extracting(c -> c.name, c -> c.code) // .containsExactly(null, null); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java index 9a6455f5a2..eaa9c9b71e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java @@ -16,7 +16,7 @@ package org.springframework.data.jpa.convert.threeten; import static org.assertj.core.api.Assertions.*; -import static org.junit.Assume.*; +import static org.assertj.core.api.Assumptions.*; import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; import java.time.Instant; @@ -51,7 +51,7 @@ public class Jsr310JpaConvertersIntegrationTests extends AbstractAttributeConver @Test // DATAJPA-650, DATAJPA-1631 void usesJsr310JpaConverters() { - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); DateTimeSample sample = new DateTimeSample(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java index 8c9fbb1163..f3a5a20477 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java @@ -18,7 +18,6 @@ import static org.assertj.core.api.Assertions.*; import java.time.LocalDateTime; -import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -42,6 +41,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:auditing/auditing-entity-listener.xml") @@ -58,14 +58,14 @@ public class AuditingEntityListenerTests { private static void assertDatesSet(Auditable auditable) { - assertThat(auditable.getCreatedDate().isPresent()).isTrue(); - assertThat(auditable.getLastModifiedDate().isPresent()).isTrue(); + assertThat(auditable.getCreatedDate()).isPresent(); + assertThat(auditable.getLastModifiedDate()).isPresent(); } private static void assertUserIsAuditor(AuditableUser user, Auditable auditable) { - assertThat(auditable.getCreatedBy()).isEqualTo(Optional.of(user)); - assertThat(auditable.getLastModifiedBy()).isEqualTo(Optional.of(user)); + assertThat(auditable.getCreatedBy()).contains(user); + assertThat(auditable.getLastModifiedBy()).contains(user); } @BeforeEach diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java index 392b5b3ad9..65d69a079d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java @@ -45,6 +45,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration @@ -75,7 +76,7 @@ public Void doInTransaction(TransactionStatus status) { ProxyIdAccessor accessor = PersistenceProvider.fromEntityManager(em); assertThat(accessor.shouldUseAccessorFor(product)).isTrue(); - assertThat(accessor.getIdentifierFrom(product).toString()).isEqualTo((Object) product.getId().toString()); + assertThat(accessor.getIdentifierFrom(product)).hasToString(product.getId().toString()); return null; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java index 69950ec477..57e3b08502 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java @@ -33,6 +33,7 @@ * @author Jens Schauder * @author Moritz Becker * @author Andrey Kovalev + * @author Krzysztof Krason */ @ContextConfiguration(value = "classpath:eclipselink.xml") @Disabled("hsqldb seems to hang on this test class without leaving a surefire report") @@ -66,8 +67,7 @@ void queryProvidesCorrectNumberOfParametersForNativeQuery() { Query query = em.createNativeQuery("select 1 from User where firstname=? and lastname=?"); assertThat(query.getParameters()).describedAs( - "Due to a bug eclipse has size 0; If this is no longer the case the special code path triggered in NamedOrIndexedQueryParameterSetter.registerExcessParameters can be removed") - .hasSize(0); + "Due to a bug eclipse has size 0; If this is no longer the case the special code path triggered in NamedOrIndexedQueryParameterSetter.registerExcessParameters can be removed").isEmpty(); } /** diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index 10310a9bbd..f404ef89f4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.repository; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; import jakarta.persistence.EntityManager; @@ -29,7 +30,6 @@ import java.util.List; import org.assertj.core.api.SoftAssertions; -import org.junit.Assume; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -55,6 +55,7 @@ * @author Jocelyn Ntakpe * @author Christoph Strobl * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:config/namespace-autoconfig-context.xml") @@ -96,14 +97,14 @@ void setup() { @Test // DATAJPA-612 void shouldRespectConfiguredJpaEntityGraph() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); List result = repository.findAll(); - assertThat(result.size()).isEqualTo(3); + assertThat(result).hasSize(3); assertThat(util.isLoaded(result.get(0), "roles")).isTrue(); assertThat(result.get(0)).isEqualTo(tom); } @@ -111,7 +112,7 @@ void shouldRespectConfiguredJpaEntityGraph() { @Test // DATAJPA-689 void shouldRespectConfiguredJpaEntityGraphInFindOne() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); @@ -127,7 +128,7 @@ void shouldRespectConfiguredJpaEntityGraphInFindOne() { @Test // DATAJPA-696 void shouldRespectInferFetchGraphFromMethodName() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); @@ -143,7 +144,7 @@ void shouldRespectInferFetchGraphFromMethodName() { @Test // DATAJPA-696 void shouldRespectDynamicFetchGraphForGetOneWithAttributeNamesById() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); @@ -169,7 +170,7 @@ void shouldRespectDynamicFetchGraphForGetOneWithAttributeNamesById() { @Test // DATAJPA-790, DATAJPA-1087 void shouldRespectConfiguredJpaEntityGraphWithPaginationAndQueryDslPredicates() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); @@ -177,7 +178,7 @@ void shouldRespectConfiguredJpaEntityGraphWithPaginationAndQueryDslPredicates() Page page = repository.findAll(QUser.user.firstname.isNotNull(), PageRequest.of(0, 100)); List result = page.getContent(); - assertThat(result.size()).isEqualTo(3); + assertThat(result).hasSize(3); assertThat(util.isLoaded(result.get(0), "roles")).isTrue(); assertThat(result.get(0)).isEqualTo(tom); } @@ -185,7 +186,7 @@ void shouldRespectConfiguredJpaEntityGraphWithPaginationAndQueryDslPredicates() @Test // DATAJPA-1207 void shouldRespectConfiguredJpaEntityGraphWithPaginationAndSpecification() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); @@ -197,7 +198,7 @@ void shouldRespectConfiguredJpaEntityGraphWithPaginationAndSpecification() { List result = page.getContent(); - assertThat(result.size()).isEqualTo(3); + assertThat(result).hasSize(3); assertThat(util.isLoaded(result.get(0), "roles")).isTrue(); assertThat(result.get(0)).isEqualTo(tom); } @@ -205,7 +206,7 @@ void shouldRespectConfiguredJpaEntityGraphWithPaginationAndSpecification() { @Test // DATAJPA-1041 void shouldRespectNamedEntitySubGraph() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); @@ -230,7 +231,7 @@ void shouldRespectNamedEntitySubGraph() { @Test // DATAJPA-1041 void shouldRespectMultipleSubGraphForSameAttributeWithDynamicFetchGraph() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); @@ -256,7 +257,7 @@ void shouldRespectMultipleSubGraphForSameAttributeWithDynamicFetchGraph() { @Disabled // likely broken due to the fixes made for HHH-15391 void shouldCreateDynamicGraphWithMultipleLevelsOfSubgraphs() { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); em.flush(); em.clear(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java index dca475e643..7ae8878ec4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java @@ -46,6 +46,7 @@ * * @author Thomas Darimont * @author Jens Schauder + * @author Krzysztof Krason */ @Transactional @ExtendWith(SpringExtension.class) @@ -66,8 +67,8 @@ void supportForExpressionBasedQueryMethods() { List concretes1 = concreteRepository1.findAllByAttribute1("foo"); List concretes2 = concreteRepository2.findAllByAttribute1("foo"); - assertThat(concretes1.size()).isEqualTo(1); - assertThat(concretes2.size()).isEqualTo(1); + assertThat(concretes1).hasSize(1); + assertThat(concretes2).hasSize(1); } @Test // DATAJPA-424 @@ -79,7 +80,7 @@ void supportForPaginationCustomQueryMethodsWithEntityExpression() { Page page = concreteRepository2.findByAttribute1Custom("foo", PageRequest.of(0, 10, Sort.Direction.DESC, "attribute1")); - assertThat(page.getNumberOfElements()).isEqualTo(1); + assertThat(page.getNumberOfElements()).isOne(); } @Test // DATAJPA-1535 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java index 64ad5f65cd..5c14e8c505 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java @@ -40,6 +40,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ContextConfiguration("classpath:openjpa.xml") class OpenJpaNamespaceUserRepositoryTests extends NamespaceUserRepositoryTests { @@ -78,7 +79,7 @@ void queryUsingIn() { query.setParameter(parameter, Arrays.asList(1, 2)); List resultList = query.getResultList(); - assertThat(resultList.size()).isEqualTo(2); + assertThat(resultList).hasSize(2); } /** diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java index 1aa33e504b..b834d9ffbe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java @@ -44,6 +44,7 @@ /** * @author Jens Schauder + * @author Krzysztof Krason */ @Transactional @ExtendWith(SpringExtension.class) @@ -76,11 +77,11 @@ public Predicate toPredicate(Root root, CriteriaQuery query, Criteria List content = page.getContent(); - assertThat(content.size()).isEqualTo(3); + assertThat(content).hasSize(3); assertThat(page.getSize()).isEqualTo(5); - assertThat(page.getNumber()).isEqualTo(0); + assertThat(page.getNumber()).isZero(); assertThat(page.getTotalElements()).isEqualTo(3L); - assertThat(page.getTotalPages()).isEqualTo(1); + assertThat(page.getTotalPages()).isOne(); } @Test // DATAJPA-287 @@ -99,13 +100,13 @@ public Predicate toPredicate(Root root, CriteriaQuery query, Criteria // according to the initial setup there should be // 3 parents which children collection is not empty - assertThat(content.size()).isEqualTo(3); + assertThat(content).hasSize(3); assertThat(page.getSize()).isEqualTo(5); - assertThat(page.getNumber()).isEqualTo(0); + assertThat(page.getNumber()).isZero(); // we get here wrong total elements number since // count query doesn't take into account the distinct marker of query assertThat(page.getTotalElements()).isEqualTo(3L); - assertThat(page.getTotalPages()).isEqualTo(1); + assertThat(page.getTotalPages()).isOne(); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java index 5ce3a472b8..56dcac5ef0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java @@ -36,6 +36,7 @@ /** * @author Thomas Darimont * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SampleConfig.class) @@ -62,7 +63,7 @@ void adjustedWellKnownPagedFindAllMethodShouldReturnOnlyTheUserWithFirstnameOliv Page page = repository.findAll(PageRequest.of(0, 2)); - assertThat(page.getNumberOfElements()).isEqualTo(1); + assertThat(page.getNumberOfElements()).isOne(); assertThat(page.getContent().get(0).getFirstname()).isEqualTo("Oliver"); } @@ -74,6 +75,6 @@ void adjustedWllKnownFindAllMethodShouldReturnAnEmptyList() { List result = repository.findAll(); - assertThat(result.isEmpty()).isTrue(); + assertThat(result).isEmpty(); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index 3ec47cb8f1..4397fad77b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -50,6 +50,7 @@ * @author Mark Paluch * @author Jens Schauder * @author Ernst-Jan van der Laan + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SampleConfig.class) @@ -127,7 +128,7 @@ void shouldSupportFindAllWithPageableAndEntityWithIdClass() { Page page = employeeRepositoryWithIdClass.findAll(PageRequest.of(0, 1)); assertThat(page).isNotNull(); - assertThat(page.getTotalElements()).isEqualTo(1L); + assertThat(page.getTotalElements()).isOne(); } @Test // DATAJPA-2414 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java index 25a74ca8f6..861b04256e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java @@ -44,6 +44,7 @@ * * @author Mark Paluch * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = RepositoryWithIdClassKeyTests.TestConfig.class) @@ -72,7 +73,7 @@ void shouldSaveAndLoadEntitiesWithDerivedIdentities() { .findById(new ItemSiteId(new ItemId(item.getId(), item.getManufacturerId()), site.getId())); assertThat(loaded).isNotNull(); - assertThat(loaded.isPresent()).isTrue(); + assertThat(loaded).isPresent(); } @Configuration diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java index 261ab919e4..9226371bc2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java @@ -17,8 +17,6 @@ import static org.assertj.core.api.Assertions.*; -import java.util.Optional; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -36,6 +34,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = { "classpath:application-context.xml" }) @@ -63,7 +62,7 @@ void updatesRole() { ReflectionTestUtils.setField(reference, "name", "USER"); repository.save(reference); - assertThat(repository.findById(result.getId())).isEqualTo(Optional.of(reference)); + assertThat(repository.findById(result.getId())).contains(reference); } @Test // DATAJPA-509 @@ -72,7 +71,7 @@ void shouldUseExplicitlyConfiguredEntityNameInOrmXmlInCountQueries() { Role reference = new Role("ADMIN"); repository.save(reference); - assertThat(repository.count()).isEqualTo(1L); + assertThat(repository.count()).isOne(); } @Test // DATAJPA-509 @@ -90,6 +89,6 @@ void shouldUseExplicitlyConfiguredEntityNameInDerivedCountQueries() { Role reference = new Role("ADMIN"); reference = repository.save(reference); - assertThat(repository.countByName(reference.getName())).isEqualTo(1L); + assertThat(repository.countByName(reference.getName())).isOne(); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java index 543b7d788c..380b79a598 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java @@ -17,7 +17,6 @@ import static org.assertj.core.api.Assertions.*; -import java.util.Arrays; import java.util.Map; import org.junit.jupiter.api.Test; @@ -32,6 +31,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; /** * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:config/namespace-application-context.xml") @@ -46,9 +46,9 @@ void canAccessRepositoryFactoryInformationFactoryBeans() { Map repoFactories = context .getBeansOfType(RepositoryFactoryInformation.class); - assertThat(repoFactories.size()).isGreaterThan(0); + assertThat(repoFactories).isNotEmpty(); assertThat(repoFactories.keySet()).contains("&userRepository"); assertThat(repoFactories.get("&userRepository")).isInstanceOf(JpaRepositoryFactoryBean.class); - assertThat(Arrays.asList(context.getBeanNamesForType(UserRepository.class))).contains("userRepository"); + assertThat(context.getBeanNamesForType(UserRepository.class)).contains("userRepository"); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java index 3c1e77f825..cade171637 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java @@ -41,6 +41,7 @@ /** * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:application-context.xml" @@ -71,7 +72,7 @@ void bindArray() { query.setParameter(parameter, new String[] { "Dave", "Carter" }); List result = query.getResultList(); - assertThat(result.isEmpty()).isFalse(); + assertThat(result).isNotEmpty(); } @Test @@ -94,7 +95,7 @@ void bindCollection() { query.setParameter(parameter, Arrays.asList("Dave")); List result = query.getResultList(); - assertThat(result.isEmpty()).isFalse(); + assertThat(result).isNotEmpty(); assertThat(result.get(0)).isEqualTo(user); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java index f7ff27211e..a74176f7d6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -16,7 +16,7 @@ package org.springframework.data.jpa.repository; import static org.assertj.core.api.Assertions.*; -import static org.junit.Assume.*; +import static org.assertj.core.api.Assumptions.*; import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; import java.util.List; @@ -47,6 +47,7 @@ * @author Oliver Gierke * @author Jens Schauder * @author Gabriel Basilio + * @author Krzysztof Krason * @see scripts/schema-stored-procedures.sql for procedure definitions. */ @Transactional @@ -61,7 +62,7 @@ public class StoredProcedureIntegrationTests { @BeforeEach void setup() { - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); } @Test // DATAJPA-652 @@ -86,7 +87,7 @@ void shouldExecuteAdHocProcedureWith1InputAnd1OutputParameterWithResultSet() { List dummies = repository.adHocProcedureWith1InputAnd1OutputParameterWithResultSet("FOO"); assertThat(dummies).isNotNull(); - assertThat(dummies.size()).isEqualTo(3); + assertThat(dummies).hasSize(3); } @Test // DATAJPA-652 @@ -96,7 +97,7 @@ void shouldExecuteAdHocProcedureWith1InputAnd1OutputParameterWithResultSetWithUp List dummies = repository.adHocProcedureWith1InputAnd1OutputParameterWithResultSetWithUpdate("FOO"); assertThat(dummies).isNotNull(); - assertThat(dummies.size()).isEqualTo(3); + assertThat(dummies).hasSize(3); } @Test // DATAJPA-652 @@ -126,7 +127,7 @@ void shouldExecuteProcedureWith1InputAnd1OutputParameterWithResultSet() { List dummies = repository.procedureWith1InputAnd1OutputParameterWithResultSet("FOO"); assertThat(dummies).isNotNull(); - assertThat(dummies.size()).isEqualTo(3); + assertThat(dummies).hasSize(3); } @Test // DATAJPA-652 @@ -136,7 +137,7 @@ void shouldExecuteProcedureWith1InputAnd1OutputParameterWithResultSetWithUpdate( List dummies = repository.procedureWith1InputAnd1OutputParameterWithResultSetWithUpdate("FOO"); assertThat(dummies).isNotNull(); - assertThat(dummies.size()).isEqualTo(3); + assertThat(dummies).hasSize(3); } @Test // DATAJPA-652 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 36715fa12e..d4b35a2c32 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -49,6 +49,7 @@ * Integration test for executing finders, thus testing various query lookup strategies. * * @author Oliver Gierke + * @author Krzysztof Krason * @see QueryLookupStrategy */ @ExtendWith(SpringExtension.class) @@ -128,7 +129,7 @@ void executesPagingMethodToPageCorrectly() { Page page = userRepository.findByLastname(PageRequest.of(0, 1), "Matthews"); - assertThat(page.getNumberOfElements()).isEqualTo(1); + assertThat(page.getNumberOfElements()).isOne(); assertThat(page.getTotalElements()).isEqualTo(2L); assertThat(page.getTotalPages()).isEqualTo(2); } @@ -145,7 +146,7 @@ void executesInKeywordForPageCorrectly() { Page page = userRepository.findByFirstnameIn(PageRequest.of(0, 1), "Dave", "Oliver August"); - assertThat(page.getNumberOfElements()).isEqualTo(1); + assertThat(page.getNumberOfElements()).isOne(); assertThat(page.getTotalElements()).isEqualTo(2L); assertThat(page.getTotalPages()).isEqualTo(2); } @@ -214,7 +215,7 @@ void executesQueryToSliceWithUnpaged() { assertThat(slice).containsExactlyInAnyOrder(dave, oliver); assertThat(slice.getNumberOfElements()).isEqualTo(2); - assertThat(slice.hasNext()).isEqualTo(false); + assertThat(slice.hasNext()).isFalse(); } @Test // DATAJPA-830 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 b912aa2aaa..bdf553e4e8 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 @@ -84,6 +84,7 @@ * @author Daniel Shuy * @author Simon Paradies * @author Geoffrey Deremetz + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @@ -297,7 +298,7 @@ void executesManipulatingQuery() { repository.renameAllUsersTo("newLastname"); long expected = repository.count(); - assertThat(repository.findByLastname("newLastname").size()).isEqualTo(Long.valueOf(expected).intValue()); + assertThat(repository.findByLastname("newLastname")).hasSize(Long.valueOf(expected).intValue()); } @Test @@ -446,7 +447,7 @@ void testUsesQueryAnnotation() { void testExecutionOfProjectingMethod() { flushTestUsers(); - assertThat(repository.countWithFirstname("Oliver")).isEqualTo(1L); + assertThat(repository.countWithFirstname("Oliver")).isOne(); } @Test @@ -512,7 +513,7 @@ void executesCombinedSpecificationsWithPageableCorrectly() { Specification spec1 = userHasFirstname("Oliver").or(userHasLastname("Arrasz")); Page users1 = repository.findAll(spec1, PageRequest.of(0, 1)); - assertThat(users1.getSize()).isEqualTo(1); + assertThat(users1.getSize()).isOne(); assertThat(users1.hasPrevious()).isFalse(); assertThat(users1.getTotalElements()).isEqualTo(2L); @@ -521,7 +522,7 @@ void executesCombinedSpecificationsWithPageableCorrectly() { userHasLastname("Arrasz")); Page users2 = repository.findAll(spec2, PageRequest.of(0, 1)); - assertThat(users2.getSize()).isEqualTo(1); + assertThat(users2.getSize()).isOne(); assertThat(users2.hasPrevious()).isFalse(); assertThat(users2.getTotalElements()).isEqualTo(2L); @@ -702,7 +703,7 @@ void readsPageWithGroupByClauseCorrectly() { flushTestUsers(); Page result = repository.findByLastnameGrouped(PageRequest.of(0, 10)); - assertThat(result.getTotalPages()).isEqualTo(1); + assertThat(result.getTotalPages()).isOne(); } @Test @@ -811,7 +812,7 @@ void allowsExecutingPageableMethodWithUnpagedArgument() { assertThat(repository.findByFirstname("Oliver", null)).containsOnly(firstUser); Page page = repository.findByFirstnameIn(Pageable.unpaged(), "Oliver"); - assertThat(page.getNumberOfElements()).isEqualTo(1); + assertThat(page.getNumberOfElements()).isOne(); assertThat(page.getContent()).contains(firstUser); page = repository.findAll(Pageable.unpaged()); @@ -826,7 +827,7 @@ void executesNativeQueryForNonEntitiesCorrectly() { List result = repository.findOnesByNativeQuery(); - assertThat(result.size()).isEqualTo(4); + assertThat(result).hasSize(4); assertThat(result).contains(1); } @@ -895,7 +896,7 @@ void ordersByReferencedEntityCorrectly() { Page all = repository.findAll(PageRequest.of(0, 10, Sort.by("manager.id"))); - assertThat(all.getContent().isEmpty()).isFalse(); + assertThat(all.getContent()).isNotEmpty(); } @Test // DATAJPA-252 @@ -920,7 +921,7 @@ public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBu } }, PageRequest.of(0, 20, Sort.by("manager.lastname"))); - assertThat(page.getNumberOfElements()).isEqualTo(1); + assertThat(page.getNumberOfElements()).isOne(); assertThat(page).containsOnly(firstUser); } @@ -968,7 +969,7 @@ void executesDerivedCountQueryToLong() { flushTestUsers(); - assertThat(repository.countByLastname("Matthews")).isEqualTo(1L); + assertThat(repository.countByLastname("Matthews")).isOne(); } @Test // DATAJPA-231 @@ -976,7 +977,7 @@ void executesDerivedCountQueryToInt() { flushTestUsers(); - assertThat(repository.countUsersByFirstname("Dave")).isEqualTo(1); + assertThat(repository.countUsersByFirstname("Dave")).isOne(); } @Test // DATAJPA-231 @@ -984,8 +985,8 @@ void executesDerivedExistsQuery() { flushTestUsers(); - assertThat(repository.existsByLastname("Matthews")).isEqualTo(true); - assertThat(repository.existsByLastname("Hans Peter")).isEqualTo(false); + assertThat(repository.existsByLastname("Matthews")).isTrue(); + assertThat(repository.existsByLastname("Hans Peter")).isFalse(); } @Test // DATAJPA-332, DATAJPA-1168 @@ -1049,8 +1050,8 @@ void shouldSupportModifyingQueryWithVarArgs() { fourthUser.getId()); long expectedCount = repository.count(); - assertThat(repository.findByActiveFalse().size()).isEqualTo((int) expectedCount); - assertThat(repository.findByActiveTrue().size()).isEqualTo(0); + assertThat(repository.findByActiveFalse()).hasSize((int) expectedCount); + assertThat(repository.findByActiveTrue()).isEmpty(); } @Test // DATAJPA-405 @@ -1269,7 +1270,7 @@ void deleteByShouldRemoveElementsMatchingDerivedQuery() { flushTestUsers(); repository.deleteByLastname(firstUser.getLastname()); - assertThat(repository.countByLastname(firstUser.getLastname())).isEqualTo(0L); + assertThat(repository.countByLastname(firstUser.getLastname())).isZero(); } @Test // DATAJPA-460 @@ -1277,7 +1278,7 @@ void deleteByShouldReturnNumberOfEntitiesRemovedIfReturnTypeIsLong() { flushTestUsers(); - assertThat(repository.removeByLastname(firstUser.getLastname())).isEqualTo(1L); + assertThat(repository.removeByLastname(firstUser.getLastname())).isOne(); } @Test // DATAJPA-460 @@ -1285,7 +1286,7 @@ void deleteByShouldReturnZeroInCaseNoEntityHasBeenRemovedAndReturnTypeIsNumber() flushTestUsers(); - assertThat(repository.removeByLastname("bubu")).isEqualTo(0L); + assertThat(repository.removeByLastname("bubu")).isZero(); } @Test // DATAJPA-460 @@ -1310,7 +1311,7 @@ void findBinaryDataByIdJpaQl() throws Exception { byte[] result = repository.findBinaryDataByIdNative(firstUser.getId()); - assertThat(result.length).isEqualTo(data.length); + assertThat(result).hasSameSizeAs(data); assertThat(result).isEqualTo(data); } @@ -1325,7 +1326,7 @@ void findBinaryDataByIdNative() throws Exception { byte[] result = repository.findBinaryDataByIdNative(firstUser.getId()); assertThat(result).isEqualTo(data); - assertThat(result.length).isEqualTo(data.length); + assertThat(result).hasSameSizeAs(data); } @Test // DATAJPA-456 @@ -1337,7 +1338,7 @@ void findPaginatedExplicitQueryWithCountQueryProjection() { Page result = repository.findAllByFirstnameLike("", PageRequest.of(0, 10)); - assertThat(result.getContent().size()).isEqualTo(3); + assertThat(result.getContent()).hasSize(3); } @Test // DATAJPA-456 @@ -1347,7 +1348,7 @@ void findPaginatedNamedQueryWithCountQueryProjection() { Page result = repository.findByNamedQueryAndCountProjection("Gierke", PageRequest.of(0, 10)); - assertThat(result.getContent().size()).isEqualTo(1); + assertThat(result.getContent()).hasSize(1); } @Test // DATAJPA-551 @@ -1484,7 +1485,7 @@ void pageableQueryReportsTotalFromCount() { assertThat(firstPage.getTotalElements()).isEqualTo(4L); Page secondPage = repository.findAll(PageRequest.of(10, 10)); - assertThat(secondPage.getContent()).hasSize(0); + assertThat(secondPage.getContent()).isEmpty(); assertThat(secondPage.getTotalElements()).isEqualTo(4L); } @@ -1495,8 +1496,8 @@ void invokesQueryWithWrapperType() { Optional result = repository.findOptionalByEmailAddress("gierke@synyx.de"); - assertThat(result.isPresent()).isEqualTo(true); - assertThat(result.get()).isEqualTo(firstUser); + assertThat(result).isPresent(); + assertThat(result).contains(firstUser); } @Test // DATAJPA-564 @@ -1686,7 +1687,7 @@ void findByEmptyCollectionOfStrings() { flushTestUsers(); List users = repository.findByAttributesIn(new HashSet<>()); - assertThat(users).hasSize(0); + assertThat(users).isEmpty(); } @Test // DATAJPA-606 @@ -1695,7 +1696,7 @@ void findByEmptyCollectionOfIntegers() { flushTestUsers(); List users = repository.findByAgeIn(Collections.emptyList()); - assertThat(users).hasSize(0); + assertThat(users).isEmpty(); } @Test // GH-2013 @@ -1707,7 +1708,7 @@ void findByCollectionWithPageable() { assertThat(userPage).hasSize(2); assertThat(userPage.getTotalElements()).isEqualTo(2); - assertThat(userPage.getTotalPages()).isEqualTo(1); + assertThat(userPage.getTotalPages()).isOne(); assertThat(userPage.getContent()).containsExactlyInAnyOrder(firstUser, secondUser); } @@ -1720,7 +1721,7 @@ void findByCollectionWithPageRequest() { assertThat(userPage).hasSize(2); assertThat(userPage.getTotalElements()).isEqualTo(2); - assertThat(userPage.getTotalPages()).isEqualTo(1); + assertThat(userPage.getTotalPages()).isOne(); assertThat(userPage.getContent()).containsExactlyInAnyOrder(firstUser, secondUser); } @@ -1730,7 +1731,7 @@ void findByEmptyArrayOfIntegers() { flushTestUsers(); List users = repository.queryByAgeIn(new Integer[0]); - assertThat(users).hasSize(0); + assertThat(users).isEmpty(); } @Test // DATAJPA-606 @@ -2022,7 +2023,7 @@ void findAllByExampleWithPageable() { Page users = repository.findAll(example, PageRequest.of(0, 10, Sort.by(DESC, "age"))); assertThat(users.getSize()).isEqualTo(10); - assertThat(users.hasNext()).isEqualTo(true); + assertThat(users.hasNext()).isTrue(); assertThat(users.getTotalElements()).isEqualTo(100L); } @@ -2513,7 +2514,7 @@ void countByExampleWithExcludedAttributes() { Example example = Example.of(prototype, matching().withIgnorePaths("createdAt")); long count = repository.count(example); - assertThat(count).isEqualTo(1L); + assertThat(count).isOne(); } @Test // DATAJPA-218 @@ -2527,7 +2528,7 @@ void existsByExampleWithExcludedAttributes() { Example example = Example.of(prototype, matching().withIgnorePaths("createdAt")); boolean exists = repository.exists(example); - assertThat(exists).isEqualTo(true); + assertThat(exists).isTrue(); } @Test // GH-2368 @@ -2541,7 +2542,7 @@ void existsByExampleNegative() { Example example = Example.of(prototype, matching().withIgnorePaths("createdAt")); boolean exists = repository.exists(example); - assertThat(exists).isEqualTo(false); + assertThat(exists).isFalse(); } @Test // DATAJPA-905 @@ -2552,7 +2553,7 @@ void executesPagedSpecificationSettingAnOrder() { Page result = repository.findAll(userHasLastnameLikeWithSort("e"), PageRequest.of(0, 1)); assertThat(result.getTotalElements()).isEqualTo(2L); - assertThat(result.getNumberOfElements()).isEqualTo(1); + assertThat(result.getNumberOfElements()).isOne(); assertThat(result.getContent().get(0)).isEqualTo(thirdUser); } @@ -2679,7 +2680,7 @@ void executeNativeQueryWithPage() { .containsExactly("Dave", "Joachim", "kevin"); assertThat(secondPage.getTotalElements()).isEqualTo(4L); - assertThat(secondPage.getNumberOfElements()).isEqualTo(1); + assertThat(secondPage.getNumberOfElements()).isOne(); assertThat(secondPage.getContent()) // .extracting(User::getFirstname) // .containsExactly("Oliver"); @@ -2704,7 +2705,7 @@ void executeNativeQueryWithPageWorkaround() { .containsExactly("Dave", "Joachim", "kevin"); assertThat(secondPage.getTotalElements()).isEqualTo(4L); - assertThat(secondPage.getNumberOfElements()).isEqualTo(1); + assertThat(secondPage.getNumberOfElements()).isOne(); assertThat(secondPage.getContent()) // .containsExactly("Oliver"); @@ -2981,8 +2982,6 @@ void insertStatementModifyingQueryWorks() { List all = repository.findAll(); assertThat(all) // - .isNotNull() // - .isNotEmpty() // .hasSize(5) // .map(User::getLastname) // .contains("Gierke", "Arrasz", "Matthews", "raymond", "K"); @@ -2996,8 +2995,6 @@ void insertStatementModifyingQueryWithParamsWorks() { List all = repository.findAll(); assertThat(all) // - .isNotNull() // - .isNotEmpty() // .hasSize(5) // .map(User::getLastname) // .contains("Gierke", "Arrasz", "Matthews", "raymond", testLastName); @@ -3009,7 +3006,6 @@ void mergeWithNativeStatement() { flushTestUsers(); assertThat(repository.findById(firstUser.getId())) // - .isPresent() // .map(User::getAge).contains(28); // when @@ -3017,7 +3013,6 @@ void mergeWithNativeStatement() { // then assertThat(repository.findById(firstUser.getId())) // - .isPresent() // .map(User::getAge).contains(30); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java index d1f4ee7cea..114896a889 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java @@ -37,6 +37,7 @@ * @author Oliver Gierke * @author Mark Paluch * @author Jens Schauder + * @author Krzysztof Krason */ class CdiExtensionIntegrationTests { @@ -83,7 +84,7 @@ void saveAndFindAll() { void returnOneFromCustomImpl() { RepositoryConsumer repositoryConsumer = container.select(RepositoryConsumer.class).get(); - assertThat(repositoryConsumer.returnOne()).isEqualTo(1); + assertThat(repositoryConsumer.returnOne()).isOne(); } @Test // DATAJPA-584, DATAJPA-1180 @@ -97,6 +98,6 @@ void useQualifiedCustomizedUserRepo() { void useQualifiedFragmentUserRepo() { RepositoryConsumer repositoryConsumer = container.select(RepositoryConsumer.class).get(); - assertThat(repositoryConsumer.returnOneUserDB()).isEqualTo(1); + assertThat(repositoryConsumer.returnOneUserDB()).isOne(); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java index 0e558984df..67d0a0c638 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java @@ -37,6 +37,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ class JpaRepositoryExtensionUnitTests { @@ -48,7 +49,7 @@ private static void assertEntityManagerRegistered(JpaRepositoryExtension extensi Map, Bean> entityManagers = (Map, Bean>) ReflectionTestUtils .getField(extension, "entityManagers"); - assertThat(entityManagers.size()).isEqualTo(1); + assertThat(entityManagers).hasSize(1); assertThat(entityManagers.values()).contains(em); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java index 2b732fe822..1d0c3c360e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java @@ -54,6 +54,7 @@ * @author Thomas Darimont * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @Transactional @@ -123,8 +124,8 @@ void shouldAllowUseOfDynamicSpelParametersInUpdateQueries() { for (AuditableUser user : users) { assertThat(user.getFirstname()).isEqualTo(user.getFirstname().toUpperCase()); - assertThat(user.getLastModifiedBy()).isEqualTo(Optional.of(thomas)); - assertThat(user.getLastModifiedDate()).isEqualTo(Optional.of(now)); + assertThat(user.getLastModifiedBy()).contains(thomas); + assertThat(user.getLastModifiedDate()).contains(now); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java index e4c8ae3906..ed5832cf46 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java @@ -29,6 +29,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ class JpaRepositoryConfigDefinitionParserTests { @@ -44,6 +45,6 @@ void getsTransactionManagerSet() { PropertyValue transactionManager = definition.getPropertyValues().getPropertyValue("transactionManager"); assertThat(transactionManager).isNotNull(); - assertThat(transactionManager.getValue().toString()).isEqualTo("transactionManager-2"); + assertThat(transactionManager.getValue()).hasToString("transactionManager-2"); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index 0320570ce8..6fb53f1487 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jpa.repository.query; +import static org.assertj.core.api.Assumptions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; @@ -29,7 +30,6 @@ import jakarta.persistence.QueryHint; import jakarta.persistence.TypedQuery; -import org.junit.Assume; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -53,6 +53,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Mark Paluch + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -118,7 +119,7 @@ void addsLockingModeToQueryObject() throws Exception { @Transactional void shouldAddEntityGraphHintForFetch() throws Exception { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); JpaQueryMethod queryMethod = getMethod("findAll"); @@ -134,7 +135,7 @@ void shouldAddEntityGraphHintForFetch() throws Exception { @Transactional void shouldAddEntityGraphHintForLoad() throws Exception { - Assume.assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); JpaQueryMethod queryMethod = getMethod("getById", Integer.class); @@ -190,7 +191,7 @@ protected Query doCreateQuery(JpaParametersParameterAccessor accessor) { @Override protected TypedQuery doCreateCountQuery(JpaParametersParameterAccessor accessor) { - return (TypedQuery) countQuery; + return countQuery; } } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java index fe74cd245b..926bf69183 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.repository.query; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; import static org.junit.Assume.*; import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; @@ -32,6 +33,7 @@ import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.Assertions; +import org.assertj.core.api.Assumptions; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -50,6 +52,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @@ -61,7 +64,7 @@ public class Jpa21UtilsTests { @Test // DATAJPA-1041, DATAJPA-1075 void shouldCreateGraphWithoutSubGraphCorrectly() { - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); EntityGraph graph = em.createEntityGraph(User.class); Jpa21Utils.configureFetchGraphFrom( @@ -77,7 +80,7 @@ void shouldCreateGraphWithoutSubGraphCorrectly() { @Test // DATAJPA-1041, DATAJPA-1075 void shouldCreateGraphWithMultipleSubGraphCorrectly() { - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); EntityGraph graph = em.createEntityGraph(User.class); Jpa21Utils.configureFetchGraphFrom(new JpaEntityGraph("name", EntityGraphType.FETCH, @@ -94,7 +97,7 @@ void shouldCreateGraphWithMultipleSubGraphCorrectly() { @Test // DATAJPA-1041, DATAJPA-1075 void shouldCreateGraphWithDeepSubGraphCorrectly() { - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); EntityGraph graph = em.createEntityGraph(User.class); Jpa21Utils.configureFetchGraphFrom(new JpaEntityGraph("name", EntityGraphType.FETCH, @@ -116,7 +119,7 @@ void shouldCreateGraphWithDeepSubGraphCorrectly() { @Test // DATAJPA-1041, DATAJPA-1075 void shouldIgnoreIntermedeateSubGraphNodesThatAreNotNeeded() { - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); EntityGraph graph = em.createEntityGraph(User.class); Jpa21Utils.configureFetchGraphFrom(new JpaEntityGraph("name", EntityGraphType.FETCH, new String[] { "roles", @@ -138,7 +141,7 @@ void shouldIgnoreIntermedeateSubGraphNodesThatAreNotNeeded() { @Test // DATAJPA-1041, DATAJPA-1075 void orderOfSubGraphsShouldNotMatter() { - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); EntityGraph graph = em.createEntityGraph(User.class); Jpa21Utils.configureFetchGraphFrom(new JpaEntityGraph("name", EntityGraphType.FETCH, new String[] { @@ -159,7 +162,7 @@ void orderOfSubGraphsShouldNotMatter() { @Test // DATAJPA-1041, DATAJPA-1075 void errorsOnUnknownProperties() { - assumeTrue(currentEntityManagerIsAJpa21EntityManager(em)); + assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); assertThatExceptionOfType(Exception.class).isThrownBy(() -> Jpa21Utils.configureFetchGraphFrom( new JpaEntityGraph("name", EntityGraphType.FETCH, new String[] { "¯\\_(ツ)_/¯" }), @@ -256,7 +259,6 @@ AttributeNodeAssert terminatesGraphWith(String... nodeNames) { Assertions.assertThat(attributeNode.getSubgraphs()) // .describedAs( String.format("Leaf properties %s could not be found; The node does not have any subgraphs", nodes)) // - .isNotNull() // .isNotEmpty(); Subgraph graph = attributeNode.getSubgraphs().values().iterator().next(); @@ -292,7 +294,6 @@ AttributeNodeAssert hasSubgraphs(String... subgraphNames) { Assertions.assertThat(attributeNode.getSubgraphs()) // .describedAs( String.format("Subgraphs %s could not be found; The node does not have any subgraphs", subgraphs)) // - .isNotNull() // .isNotEmpty(); Subgraph graph = attributeNode.getSubgraphs().values().iterator().next(); @@ -313,7 +314,7 @@ AttributeNodeAssert hasSubgraphs(String... subgraphNames) { attributeNode.getAttributeName()); softly.assertThat(node.getSubgraphs()) // - .describedAs(notSubGraph).isNotNull() // + .describedAs(notSubGraph) // .isNotEmpty(); } }); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java index 6b9f02acf7..53d70f6db0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java @@ -46,6 +46,7 @@ * Unit tests for repository with {@link Query} and {@link QueryRewrite}. * * @author Greg Turnquist + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration @@ -156,7 +157,7 @@ void counting() { assertThat(repository.count()).isEqualTo(3); assertThat(repository.countDistinctByLastname("Baggins")).isEqualTo(2); - assertThat(repository.countDistinctByLastname("Gamgee")).isEqualTo(1); + assertThat(repository.countDistinctByLastname("Gamgee")).isOne(); } public interface UserRepositoryWithRewriter diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index f5c3faf8bf..92eaa3a801 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -55,6 +55,7 @@ * @author Mark Paluch * @author Michael Cramer * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -120,7 +121,7 @@ void shouldLimitExistsProjectionQueries() throws Exception { Query query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { "Matthews" })); - assertThat(query.getMaxResults()).isEqualTo(1); + assertThat(query.getMaxResults()).isOne(); } @Test // DATAJPA-920 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 de3da09f59..db9b42375d 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 @@ -37,6 +37,7 @@ * * @author Diego Krupitza * @author Geoffrey Deremetz + * @author Krzysztof Krason */ class QueryEnhancerUnitTests { @@ -678,8 +679,7 @@ void detectsJoinAliasesCorrectly(String queryString, List aliases) { Set nonNativeJoinAliases = getEnhancer(nonNativeQuery).getJoinAliases(); assertThat(nonNativeJoinAliases).containsAll(nativeJoinAliases); - assertThat(nativeJoinAliases) // - .hasSize(aliases.size()) // + assertThat(nativeJoinAliases).hasSameSizeAs(aliases) // .containsAll(aliases); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index 2846fe01c4..d030562690 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -66,6 +66,7 @@ * @author Jens Schauder * @author Patrice Blanchardie * @author Diego Krupitza + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -222,7 +223,7 @@ void doesNotCreateJoinForOptionalAssociationWithoutFurtherNavigation() { QueryUtils.toExpressionRecursively(root, PropertyPath.from("manager", User.class)); - assertThat(getNonInnerJoins(root)).hasSize(0); + assertThat(getNonInnerJoins(root)).isEmpty(); } @Test // DATAJPA-401 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 5be979d1c6..17da9ba759 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -64,6 +64,7 @@ * @author Tom Hombergs * @author Mark Paluch * @author Greg Turnquist + * @author Krzysztof Krason */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -147,7 +148,7 @@ void discoversNativeQuery() throws Exception { queryMethod.getAnnotatedQuery(), null, QueryRewriter.IdentityQueryRewriter.INSTANCE, EVALUATION_CONTEXT_PROVIDER); - assertThat(jpaQuery instanceof NativeJpaQuery).isTrue(); + assertThat(jpaQuery).isInstanceOf(NativeJpaQuery.class); when(em.createNativeQuery(anyString(), eq(User.class))).thenReturn(query); when(metadata.getReturnedDomainClass(method)).thenReturn((Class) User.class); @@ -190,14 +191,14 @@ void validatesAndRejectsCountQueryIfPagingMethod() throws Exception { void createsASimpleJpaQueryFromAnnotation() throws Exception { RepositoryQuery query = createJpaQuery(SampleRepository.class.getMethod("findByAnnotatedQuery")); - assertThat(query instanceof SimpleJpaQuery).isTrue(); + assertThat(query).isInstanceOf(SimpleJpaQuery.class); } @Test void createsANativeJpaQueryFromAnnotation() throws Exception { RepositoryQuery query = createJpaQuery(SampleRepository.class.getMethod("findNativeByLastname", String.class)); - assertThat(query instanceof NativeJpaQuery).isTrue(); + assertThat(query).isInstanceOf(NativeJpaQuery.class); } @Test // DATAJPA-757 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 9203e9dbbd..ad3df88e98 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -502,7 +502,7 @@ void questionMarkInStringLiteral() { softly.assertThat(query.getQueryString()).isEqualTo(queryString); softly.assertThat(query.hasParameterBindings()).isFalse(); - softly.assertThat(query.getParameterBindings()).hasSize(0); + softly.assertThat(query.getParameterBindings()).isEmpty(); softly.assertAll(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java index cc46d5953c..88e612a71e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java @@ -33,8 +33,9 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ -class EntityManagerFactoryRefUnitTests { +public class EntityManagerFactoryRefUnitTests { @Test @Disabled @@ -46,7 +47,7 @@ void repositoriesGetTheSecondEntityManagerFactoryInjected2() { BeanDefinition bean = factory.getBeanDefinition("userRepository"); Object value = getPropertyValue(bean, "entityManager"); - assertThat(value instanceof RuntimeBeanNameReference).isTrue(); + assertThat(value).isInstanceOf(RuntimeBeanNameReference.class); BeanDefinition emCreator = (BeanDefinition) value; BeanReference reference = getConstructorBeanReference(emCreator, 0); @@ -61,7 +62,7 @@ private Object getPropertyValue(BeanDefinition definition, String propertyName) private BeanReference getConstructorBeanReference(BeanDefinition definition, int index) { Object value = definition.getConstructorArgumentValues().getIndexedArgumentValues().get(index).getValue(); - assertThat(value instanceof BeanReference).isTrue(); + assertThat(value).isInstanceOf(BeanReference.class); return (BeanReference) value; } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java index 0eacdec578..97c1f9357d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java @@ -40,6 +40,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -79,7 +80,7 @@ void usesPersistableMethodsForIsNewAndGetId() { foo.id = 1L; assertThat(entityInformation.isNew(foo)).isTrue(); - assertThat(entityInformation.getId(foo)).isEqualTo(1L); + assertThat(entityInformation.getId(foo)).isOne(); } @SuppressWarnings("serial") diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index 68274c672a..e3a4735121 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -52,6 +52,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -87,8 +88,6 @@ public JpaEntityInformation getEntityInformation(Class domainC /** * Assert that the instance created for the standard configuration is a valid {@code UserRepository}. - * - * @throws Exception */ @Test void setsUpBasicInstanceCorrectly() { @@ -118,7 +117,7 @@ void capturesMissingCustomImplementationAndProvidesInterfacename() { try { factory.getRepository(SampleRepository.class); } catch (IllegalArgumentException e) { - assertThat(e.getMessage().contains(SampleRepository.class.getName())).isTrue(); + assertThat(e.getMessage()).contains(SampleRepository.class.getName()); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java index 6d46876e6e..d4adb9df04 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java @@ -23,7 +23,6 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; -import java.util.Optional; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; @@ -46,6 +45,7 @@ * @author Thomas Darimont * @author Jens Schauder * @author Greg Turnquist + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @@ -70,12 +70,12 @@ void testCrudOperationsForCompoundKeyEntity() { SampleEntity entity = new SampleEntity("foo", "bar"); repository.saveAndFlush(entity); assertThat(repository.existsById(new SampleEntityPK("foo", "bar"))).isTrue(); - assertThat(repository.count()).isEqualTo(1L); - assertThat(repository.findById(new SampleEntityPK("foo", "bar"))).isEqualTo(Optional.of(entity)); + assertThat(repository.count()).isOne(); + assertThat(repository.findById(new SampleEntityPK("foo", "bar"))).contains(entity); repository.deleteAll(Arrays.asList(entity)); repository.flush(); - assertThat(repository.count()).isEqualTo(0L); + assertThat(repository.count()).isZero(); } @Test // DATAJPA-50 @@ -89,7 +89,7 @@ void executesCrudOperationsForEntityWithIdClass() { PersistableWithIdClassPK id = new PersistableWithIdClassPK(entity.getFirst(), entity.getSecond()); - assertThat(idClassRepository.findById(id)).isEqualTo(Optional.of(entity)); + assertThat(idClassRepository.findById(id)).contains(entity); } @Test // DATAJPA-266 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index 996ccdf10a..e19dda73f2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -64,6 +64,7 @@ * @author Christoph Strobl * @author Malte Mauelshagen * @author Greg Turnquist + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @@ -269,9 +270,9 @@ void shouldSupportSortByOperatorWithDateExpressions() { @Test // DATAJPA-665 void shouldSupportExistsWithPredicate() { - assertThat(predicateExecutor.exists(user.firstname.eq("Dave"))).isEqualTo(true); - assertThat(predicateExecutor.exists(user.firstname.eq("Unknown"))).isEqualTo(false); - assertThat(predicateExecutor.exists((Predicate) null)).isEqualTo(true); + assertThat(predicateExecutor.exists(user.firstname.eq("Dave"))).isTrue(); + assertThat(predicateExecutor.exists(user.firstname.eq("Unknown"))).isFalse(); + assertThat(predicateExecutor.exists((Predicate) null)).isTrue(); } @Test // DATAJPA-679 @@ -307,7 +308,7 @@ void pageableQueryReportsTotalFromCount() { assertThat(firstPage.getTotalElements()).isEqualTo(3L); Page secondPage = predicateExecutor.findAll(user.dateOfBirth.isNull(), PageRequest.of(10, 10)); - assertThat(secondPage.getContent()).hasSize(0); + assertThat(secondPage.getContent()).isEmpty(); assertThat(secondPage.getTotalElements()).isEqualTo(3L); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java index bdd8a87fe9..5a5dbd63f6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java @@ -58,6 +58,7 @@ * @author Christoph Strobl * @author Malte Mauelshagen * @author Greg Turnquist + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @@ -271,9 +272,9 @@ void shouldSupportSortByOperatorWithDateExpressions() { @Test // DATAJPA-665 void shouldSupportExistsWithPredicate() { - assertThat(repository.exists(user.firstname.eq("Dave"))).isEqualTo(true); - assertThat(repository.exists(user.firstname.eq("Unknown"))).isEqualTo(false); - assertThat(repository.exists((Predicate) null)).isEqualTo(true); + assertThat(repository.exists(user.firstname.eq("Dave"))).isTrue(); + assertThat(repository.exists(user.firstname.eq("Unknown"))).isFalse(); + assertThat(repository.exists((Predicate) null)).isTrue(); } @Test // DATAJPA-679 @@ -309,7 +310,7 @@ void pageableQueryReportsTotalFromCount() { assertThat(firstPage.getTotalElements()).isEqualTo(3L); Page secondPage = repository.findAll(user.dateOfBirth.isNull(), PageRequest.of(10, 10)); - assertThat(secondPage.getContent()).hasSize(0); + assertThat(secondPage.getContent()).isEmpty(); assertThat(secondPage.getTotalElements()).isEqualTo(3L); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java index cfd307544b..61a47b7a98 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java @@ -38,6 +38,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @@ -70,11 +71,11 @@ void setup() { void readsUsersCorrectly() { List result = repository.findUsersByLastname("Matthews"); - assertThat(result.size()).isEqualTo(1); + assertThat(result).hasSize(1); assertThat(result.get(0)).isEqualTo(dave); result = repository.findUsersByLastname("Beauford"); - assertThat(result.size()).isEqualTo(1); + assertThat(result).hasSize(1); assertThat(result.get(0)).isEqualTo(carter); } @@ -85,13 +86,13 @@ void updatesUsersCorrectly() { assertThat(updates).isEqualTo(2L); List result = repository.findUsersByLastname("Matthews"); - assertThat(result.size()).isEqualTo(0); + assertThat(result).isEmpty(); result = repository.findUsersByLastname("Beauford"); - assertThat(result.size()).isEqualTo(0); + assertThat(result).isEmpty(); result = repository.findUsersByLastname("Foo"); - assertThat(result.size()).isEqualTo(2); + assertThat(result).hasSize(2); assertThat(result).contains(dave, carter); } @@ -99,13 +100,13 @@ void updatesUsersCorrectly() { void deletesAllWithLastnameCorrectly() { long updates = repository.deleteAllWithLastname("Matthews"); - assertThat(updates).isEqualTo(1L); + assertThat(updates).isOne(); List result = repository.findUsersByLastname("Matthews"); - assertThat(result.size()).isEqualTo(0); + assertThat(result).isEmpty(); result = repository.findUsersByLastname("Beauford"); - assertThat(result.size()).isEqualTo(1); + assertThat(result).hasSize(1); assertThat(result.get(0)).isEqualTo(carter); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java index a138b2562b..fa4453d359 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java @@ -37,6 +37,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:config/namespace-autoconfig-context.xml", "classpath:tx-manager.xml" }) @@ -61,21 +62,21 @@ void tearDown() { void simpleManipulatingOperation() { repository.saveAndFlush(new User("foo", "bar", "foo@bar.de")); - assertThat(transactionManager.getTransactionRequests()).isEqualTo(1); + assertThat(transactionManager.getTransactionRequests()).isOne(); } @Test void unannotatedFinder() { repository.findByEmailAddress("foo@bar.de"); - assertThat(transactionManager.getTransactionRequests()).isEqualTo(0); + assertThat(transactionManager.getTransactionRequests()).isZero(); } @Test void invokeTransactionalFinder() { repository.findByAnnotatedQuery("foo@bar.de"); - assertThat(transactionManager.getTransactionRequests()).isEqualTo(1); + assertThat(transactionManager.getTransactionRequests()).isOne(); } @Test diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java index 3855c234af..8777d6b7ff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java @@ -40,6 +40,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Krzysztof Krason */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -64,15 +65,15 @@ void addsUrlFromOldPUItoNewOne() throws MalformedURLException { void mergesManagedClassesCorrectly() { MergingPersistenceUnitManager manager = new MergingPersistenceUnitManager(); - manager.setPersistenceXmlLocations(new String[] { "classpath:org/springframework/data/jpa/support/persistence.xml", - "classpath:org/springframework/data/jpa/support/persistence2.xml" }); + manager.setPersistenceXmlLocations("classpath:org/springframework/data/jpa/support/persistence.xml", + "classpath:org/springframework/data/jpa/support/persistence2.xml"); manager.preparePersistenceUnitInfos(); PersistenceUnitInfo info = manager.obtainPersistenceUnitInfo("pu"); - assertThat(info.getManagedClassNames().size()).isEqualTo(2); + assertThat(info.getManagedClassNames()).hasSize(2); assertThat(info.getManagedClassNames()).contains(User.class.getName(), Role.class.getName()); - assertThat(info.getMappingFileNames().size()).isEqualTo(2); + assertThat(info.getMappingFileNames()).hasSize(2); assertThat(info.getMappingFileNames()).contains("foo.xml", "bar.xml"); } @@ -87,7 +88,7 @@ void addsOldPersistenceUnitRootUrlIfDifferentFromNewOne() throws MalformedURLExc MergingPersistenceUnitManager manager = new MergingPersistenceUnitManager(); manager.postProcessPersistenceUnitInfo(newInfo, oldInfo); - assertThat(newInfo.getJarFileUrls().size()).isEqualTo(1); + assertThat(newInfo.getJarFileUrls()).hasSize(1); assertThat(newInfo.getJarFileUrls()).contains(oldInfo.getPersistenceUnitRootUrl()); } @@ -101,7 +102,7 @@ void doesNotAddNewPuRootUrlIfNull() throws MalformedURLException { MergingPersistenceUnitManager manager = new MergingPersistenceUnitManager(); manager.postProcessPersistenceUnitInfo(newInfo, oldInfo); - assertThat(newInfo.getJarFileUrls().isEmpty()).isTrue(); + assertThat(newInfo.getJarFileUrls()).isEmpty(); } @Test @@ -116,7 +117,7 @@ void doesNotAddNewPuRootUrlIfAlreadyOnTheListOfJarFileUrls() throws MalformedURL MergingPersistenceUnitManager manager = new MergingPersistenceUnitManager(); manager.postProcessPersistenceUnitInfo(newInfo, oldInfo); - assertThat(newInfo.getJarFileUrls().size()).isEqualTo(1); + assertThat(newInfo.getJarFileUrls()).hasSize(1); assertThat(newInfo.getJarFileUrls()).contains(oldInfo.getPersistenceUnitRootUrl()); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java index ec1c3f13bd..42e401759a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java @@ -37,6 +37,7 @@ * Integration tests for {@link JpaMetamodelCacheCleanup}. * * @author Oliver Gierke + * @author Krzysztof Krason */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -71,7 +72,7 @@ void registersCleanupBeanAsNonLazy() { String[] cleanupBeanNames = beanFactory.getBeanNamesForType(JpaMetamodelCacheCleanup.class); - assertThat(cleanupBeanNames.length).isEqualTo(1); + assertThat(cleanupBeanNames).hasSize(1); assertThat(beanFactory.getBeanDefinition(cleanupBeanNames[0]).isLazyInit()).isFalse(); } } From e917cbf02cde24fecb49fcd2bbf6cfd41ec40757 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 24 Jan 2023 09:50:39 -0600 Subject: [PATCH 297/821] Polishing. Related: #2746. --- .../support/RepositoryIntegrationTests.java | 13 ++-- .../Jsr310JpaConvertersIntegrationTests.java | 12 ++-- .../support/AuditingEntityListenerTests.java | 3 +- .../PersistenceProviderIntegrationTests.java | 4 +- ...lipseLinkNamespaceUserRepositoryTests.java | 12 ++-- ...raphRepositoryMethodsIntegrationTests.java | 6 +- .../MappedTypeRepositoryIntegrationTests.java | 8 +-- .../OpenJpaNamespaceUserRepositoryTests.java | 19 +++--- .../ParentRepositoryIntegrationTests.java | 10 +-- .../RedeclaringRepositoryMethodsTests.java | 3 +- .../RepositoryWithCompositeKeyTests.java | 6 +- .../RepositoryWithIdClassKeyTests.java | 3 +- .../RoleRepositoryIntegrationTests.java | 3 +- .../data/jpa/repository/SPR8954Tests.java | 4 +- .../SimpleJpaParameterBindingTests.java | 15 ++--- .../StoredProcedureIntegrationTests.java | 10 +-- .../repository/UserRepositoryFinderTests.java | 6 +- .../jpa/repository/UserRepositoryTests.java | 32 +++++++-- .../cdi/CdiExtensionIntegrationTests.java | 10 +-- .../cdi/JpaRepositoryExtensionUnitTests.java | 13 ++-- ...uditingViaJavaConfigRepositoriesTests.java | 9 ++- ...RepositoryConfigDefinitionParserTests.java | 2 +- .../query/AbstractJpaQueryTests.java | 18 ++--- .../jpa/repository/query/Jpa21UtilsTests.java | 19 +++--- .../JpaQueryRewriteIntegrationTests.java | 3 +- .../PartTreeJpaQueryIntegrationTests.java | 10 +-- .../query/QueryEnhancerUnitTests.java | 66 +++++++++---------- .../query/QueryUtilsIntegrationTests.java | 20 +++--- .../query/SimpleJpaQueryUnitTests.java | 13 ++-- .../query/StringQueryUnitTests.java | 3 +- .../EntityManagerFactoryRefUnitTests.java | 5 +- ...PersistableEntityInformationUnitTests.java | 14 ++-- .../JpaRepositoryFactoryUnitTests.java | 29 ++++---- .../support/JpaRepositoryTests.java | 7 +- ...QuerydslJpaPredicateExecutorUnitTests.java | 4 +- .../support/QuerydslJpaRepositoryTests.java | 3 +- .../QuerydslRepositorySupportTests.java | 8 +-- .../support/TransactionalRepositoryTests.java | 10 +-- ...ergingPersistenceUnitManagerUnitTests.java | 12 ++-- ...MetamodelCacheCleanupIntegrationTests.java | 5 +- 40 files changed, 230 insertions(+), 222 deletions(-) diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java index 5acf652424..1ee0d26b1f 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java @@ -15,8 +15,10 @@ */ package org.springframework.data.envers.repository.support; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.history.RevisionMetadata.RevisionType.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.history.RevisionMetadata.RevisionType.DELETE; +import static org.springframework.data.history.RevisionMetadata.RevisionType.INSERT; +import static org.springframework.data.history.RevisionMetadata.RevisionType.UPDATE; import java.util.Arrays; import java.util.HashSet; @@ -27,7 +29,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -53,10 +54,8 @@ @ContextConfiguration(classes = Config.class) class RepositoryIntegrationTests { - @Autowired - LicenseRepository licenseRepository; - @Autowired - CountryRepository countryRepository; + @Autowired LicenseRepository licenseRepository; + @Autowired CountryRepository countryRepository; @BeforeEach void setUp() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java index eaa9c9b71e..60a2d9d4a0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java @@ -15,9 +15,12 @@ */ package org.springframework.data.jpa.convert.threeten; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; -import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.currentEntityManagerIsAJpa21EntityManager; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import java.time.Instant; import java.time.LocalDate; @@ -26,9 +29,6 @@ import java.time.ZoneId; import java.time.temporal.ChronoUnit; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; - import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.domain.support.AbstractAttributeConverterIntegrationTests; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java index f3a5a20477..0d1f3f1052 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java @@ -15,14 +15,13 @@ */ package org.springframework.data.jpa.domain.support; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDateTime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Auditable; import org.springframework.data.jpa.domain.sample.AnnotatedAuditableUser; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java index 65d69a079d..5d86d05f77 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java @@ -15,14 +15,13 @@ */ package org.springframework.data.jpa.provider; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import jakarta.persistence.EntityManager; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; @@ -60,6 +59,7 @@ public class PersistenceProviderIntegrationTests { @BeforeEach void setUp() { + this.product = products.save(new Product()); this.category = categories.save(new Category(product)); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java index 57e3b08502..90e3bfcfc5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java @@ -15,13 +15,12 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import jakarta.persistence.Query; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; - import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.test.context.ContextConfiguration; @@ -43,8 +42,7 @@ class EclipseLinkNamespaceUserRepositoryTests extends NamespaceUserRepositoryTes * Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=422450 is resolved. */ @Override - void sortByAssociationPropertyShouldUseLeftOuterJoin() { - } + void sortByAssociationPropertyShouldUseLeftOuterJoin() {} /** * Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=422450 is resolved. @@ -67,7 +65,8 @@ void queryProvidesCorrectNumberOfParametersForNativeQuery() { Query query = em.createNativeQuery("select 1 from User where firstname=? and lastname=?"); assertThat(query.getParameters()).describedAs( - "Due to a bug eclipse has size 0; If this is no longer the case the special code path triggered in NamedOrIndexedQueryParameterSetter.registerExcessParameters can be removed").isEmpty(); + "Due to a bug eclipse has size 0; If this is no longer the case the special code path triggered in NamedOrIndexedQueryParameterSetter.registerExcessParameters can be removed") + .isEmpty(); } /** @@ -112,8 +111,7 @@ void findByEmptyArrayOfIntegers() {} * Ignores the test. Reconsider once https://bugs.eclipse.org/bugs/show_bug.cgi?id=533240 is fixed. */ @Override - void findByAgeWithEmptyArrayOfIntegersOrFirstName() { - } + void findByAgeWithEmptyArrayOfIntegersOrFirstName() {} /** * Ignores the test. Reconsider once https://bugs.eclipse.org/bugs/show_bug.cgi?id=533240 is fixed. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index f404ef89f4..20726dc212 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -15,9 +15,9 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; -import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.currentEntityManagerIsAJpa21EntityManager; import jakarta.persistence.EntityManager; import jakarta.persistence.Persistence; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java index 7ae8878ec4..9d6a87ad6a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java @@ -15,15 +15,15 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; - -import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import jakarta.persistence.EntityManager; +import java.util.List; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java index 5c14e8c505..a3d130a7cb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/OpenJpaNamespaceUserRepositoryTests.java @@ -15,11 +15,8 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -29,6 +26,10 @@ import jakarta.persistence.criteria.ParameterExpression; import jakarta.persistence.criteria.Root; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.data.jpa.domain.sample.User; @@ -50,12 +51,8 @@ class OpenJpaNamespaceUserRepositoryTests extends NamespaceUserRepositoryTests { @Test void checkQueryValidationWithOpenJpa() { - assertThatThrownBy(() -> em.createQuery("something absurd")) - .isInstanceOf(RuntimeException.class); - - assertThatThrownBy(() -> em.createNamedQuery("not available")) - .isInstanceOf(RuntimeException.class); - + assertThatThrownBy(() -> em.createQuery("something absurd")).isInstanceOf(RuntimeException.class); + assertThatThrownBy(() -> em.createNamedQuery("not available")).isInstanceOf(RuntimeException.class); } /** diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java index b834d9ffbe..d64e7124c1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java @@ -15,10 +15,7 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; - -import java.util.List; -import java.util.Set; +import static org.assertj.core.api.Assertions.assertThat; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; @@ -26,10 +23,12 @@ import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import java.util.List; +import java.util.Set; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -86,6 +85,7 @@ public Predicate toPredicate(Root root, CriteriaQuery query, Criteria @Test // DATAJPA-287 void testWithJoin() { + Page page = repository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java index 56dcac5ef0..e49c76b2e3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java @@ -15,14 +15,13 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index 4397fad77b..077d886da6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -15,14 +15,14 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.persistence.EntityManager; import java.util.Arrays; import java.util.Collections; import java.util.List; -import jakarta.persistence.EntityManager; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java index 861b04256e..e17c20b2b5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java @@ -15,13 +15,12 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java index 9226371bc2..d071a6c4d1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java @@ -15,11 +15,10 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.repository.sample.RoleRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java index 380b79a598..b59d52d57a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java @@ -15,13 +15,12 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.data.jpa.repository.sample.UserRepository; @@ -29,6 +28,7 @@ import org.springframework.data.repository.core.support.RepositoryFactoryInformation; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; + /** * @author Jens Schauder * @author Krzysztof Krason diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java index cade171637..c3db4bb143 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SimpleJpaParameterBindingTests.java @@ -15,11 +15,7 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -29,10 +25,13 @@ import jakarta.persistence.criteria.ParameterExpression; import jakarta.persistence.criteria.Root; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.data.jpa.domain.sample.User; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -45,8 +44,8 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:application-context.xml" - // , "classpath:eclipselink.xml" - // , "classpath:openjpa.xml" +// , "classpath:eclipselink.xml" +// , "classpath:openjpa.xml" }) @Transactional class SimpleJpaParameterBindingTests { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java index a74176f7d6..e053a43a10 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -15,15 +15,15 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; -import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; - -import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.currentEntityManagerIsAJpa21EntityManager; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index d4b35a2c32..13b4c3b336 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -15,8 +15,10 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.domain.Sort.Direction.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.springframework.data.domain.Sort.Direction.ASC; +import static org.springframework.data.domain.Sort.Direction.DESC; import jakarta.persistence.EntityManager; 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 bdf553e4e8..96d6a48729 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 @@ -16,14 +16,22 @@ package org.springframework.data.jpa.repository; import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.data.domain.Example.of; -import static org.springframework.data.domain.ExampleMatcher.*; +import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; +import static org.springframework.data.domain.ExampleMatcher.StringMatcher; +import static org.springframework.data.domain.ExampleMatcher.matching; import static org.springframework.data.domain.Sort.Direction.ASC; import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.jpa.domain.Specification.not; import static org.springframework.data.jpa.domain.Specification.where; -import static org.springframework.data.jpa.domain.sample.UserSpecifications.*; +import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasAgeLess; +import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstname; +import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstnameLike; +import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasLastname; +import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasLastnameLikeWithSort; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -34,7 +42,14 @@ import jakarta.persistence.criteria.Root; import lombok.Data; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; @@ -48,7 +63,14 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.domain.*; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java index 114896a889..d21fa167dc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/CdiExtensionIntegrationTests.java @@ -15,20 +15,20 @@ */ package org.springframework.data.jpa.repository.cdi; -import static org.assertj.core.api.Assertions.*; - -import java.util.Set; +import static org.assertj.core.api.Assertions.assertThat; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.se.SeContainer; import jakarta.enterprise.inject.se.SeContainerInitializer; import jakarta.enterprise.inject.spi.Bean; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Integration tests for Spring Data JPA CDI extension. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java index 67d0a0c638..573d8fc939 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtensionUnitTests.java @@ -15,8 +15,13 @@ */ package org.springframework.data.jpa.repository.cdi; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.ProcessBean; +import jakarta.persistence.EntityManager; import java.lang.annotation.Annotation; import java.lang.reflect.Type; @@ -24,10 +29,6 @@ import java.util.Map; import java.util.Set; -import jakarta.enterprise.inject.spi.Bean; -import jakarta.enterprise.inject.spi.ProcessBean; -import jakarta.persistence.EntityManager; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.test.util.ReflectionTestUtils; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java index 1d0c3c360e..40579aff16 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java @@ -15,8 +15,10 @@ */ package org.springframework.data.jpa.repository.config; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import jakarta.persistence.EntityManager; import java.time.LocalDateTime; import java.time.ZoneId; @@ -25,14 +27,11 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; -import jakarta.persistence.EntityManager; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java index ed5832cf46..6eafa307c4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigDefinitionParserTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.config; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; import org.springframework.beans.PropertyValue; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index 6fb53f1487..b64e34bb9f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -15,13 +15,13 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assumptions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; - -import java.lang.reflect.Method; -import java.util.List; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.currentEntityManagerIsAJpa21EntityManager; import jakarta.persistence.EntityManager; import jakarta.persistence.LockModeType; @@ -30,10 +30,12 @@ import jakarta.persistence.QueryHint; import jakarta.persistence.TypedQuery; +import java.lang.reflect.Method; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.EntityGraph; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java index 926bf69183..f878687c5d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java @@ -15,10 +15,14 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; -import static org.junit.Assume.*; -import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assumptions.assumeThat; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.currentEntityManagerIsAJpa21EntityManager; + +import jakarta.persistence.AttributeNode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Subgraph; import java.util.ArrayList; import java.util.Arrays; @@ -26,18 +30,11 @@ import java.util.Iterator; import java.util.List; -import jakarta.persistence.AttributeNode; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.EntityManager; -import jakarta.persistence.Subgraph; - import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.Assertions; -import org.assertj.core.api.Assumptions; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java index 53d70f6db0..90c8701283 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import java.util.HashMap; import java.util.List; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 92eaa3a801..3be03263a0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -17,7 +17,9 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -136,8 +138,7 @@ void shouldSelectAliasedIdForExistsProjectionQueries() throws Exception { assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).contains(".id from User as"); } - @Test // DATAJPA-1074 - @Disabled // HHH-15432 + @Test // DATAJPA-1074, HHH-15432 void isEmptyCollection() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("findByRolesIsEmpty"); @@ -148,8 +149,7 @@ void isEmptyCollection() throws Exception { assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).endsWith("roles is empty"); } - @Test // DATAJPA-1074 - @Disabled // HHH-15432 + @Test // DATAJPA-1074, HHH-15432 void isNotEmptyCollection() throws Exception { JpaQueryMethod queryMethod = getQueryMethod("findByRolesIsNotEmpty"); 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 db9b42375d..5c1137aea2 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 @@ -15,7 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.Arrays; import java.util.Collections; @@ -99,13 +101,11 @@ void createsCountQueryForQueriesWithSubSelects() { @Test void createsCountQueryForAliasesCorrectly() { - assertCountQuery("select u from User as u", "select count(u) from User as u", true); } @Test void allowsShortJpaSyntax() { - assertCountQuery(SIMPLE_QUERY, COUNT_QUERY, false); } @@ -143,9 +143,10 @@ void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { StringQuery query = new StringQuery("select p from Person p left join p.address address", true); - endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("address.city")), "order by address.city asc"); - endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("address.city", "lastname"), "p"), - "order by address.city asc, p.lastname asc"); + assertThat(getEnhancer(query).applySorting(Sort.by("address.city"))) + .endsWithIgnoringCase("order by address.city asc"); + assertThat(getEnhancer(query).applySorting(Sort.by("address.city", "lastname"), "p")) + .endsWithIgnoringCase("order by address.city asc, p.lastname asc"); } @Test // DATAJPA-252 @@ -153,8 +154,8 @@ void extendsExistingOrderByClausesCorrectly() { StringQuery query = new StringQuery("select p from Person p order by p.lastname asc", true); - endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("firstname"), "p"), - "order by p.lastname asc, p.firstname asc"); + assertThat(getEnhancer(query).applySorting(Sort.by("firstname"), "p")) + .endsWithIgnoringCase("order by p.lastname asc, p.firstname asc"); } @Test // DATAJPA-296 @@ -164,7 +165,7 @@ void appliesIgnoreCaseOrderingCorrectly() { StringQuery query = new StringQuery("select p from Person p", true); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by lower(p.firstname) asc"); + assertThat(getEnhancer(query).applySorting(sort, "p")).endsWithIgnoringCase("order by lower(p.firstname) asc"); } @Test // DATAJPA-296 @@ -174,7 +175,8 @@ void appendsIgnoreCaseOrderingCorrectly() { StringQuery query = new StringQuery("select p from Person p order by p.lastname asc", true); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by p.lastname asc, lower(p.firstname) asc"); + assertThat(getEnhancer(query).applySorting(sort, "p")) + .endsWithIgnoringCase("order by p.lastname asc, lower(p.firstname) asc"); } @Test // DATAJPA-342 @@ -217,7 +219,7 @@ void findsExistingOrderByIndependentOfCase() { StringQuery originalQuery = new StringQuery("select p from Person p ORDER BY p.firstname", true); String query = getEnhancer(originalQuery).applySorting(sort, "p"); - endsIgnoringCase(query, "ORDER BY p.firstname, p.lastname asc"); + assertThat(query).endsWithIgnoringCase("ORDER BY p.firstname, p.lastname asc"); } @Test // DATAJPA-409 @@ -245,7 +247,7 @@ void detectsAliasesInPlainJoins() { StringQuery query = new StringQuery("select p from Customer c join c.productOrder p where p.delay = true", true); Sort sort = Sort.by("p.lineItems"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "c"), "order by p.lineItems asc"); + assertThat(getEnhancer(query).applySorting(sort, "c")).endsWithIgnoringCase("order by p.lineItems asc"); } @Test // DATAJPA-736 @@ -279,7 +281,8 @@ void doesPrefixPropertyWithNative() { StringQuery query = new StringQuery("Select * from Cat c join Dog d", true); Sort sort = Sort.by("dPropertyStartingWithJoinAlias"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "c"), "order by c.dPropertyStartingWithJoinAlias asc"); + assertThat(getEnhancer(query).applySorting(sort, "c")) + .endsWithIgnoringCase("order by c.dPropertyStartingWithJoinAlias asc"); } @Test // DATAJPA-938 @@ -324,7 +327,7 @@ void doesNotQualifySortIfNoAliasDetectedNative() { StringQuery query = new StringQuery("Select * from mytable where ?1 is null", true); - endsIgnoringCase(getEnhancer(query).applySorting(Sort.by("firstname")), "order by firstname asc"); + assertThat(getEnhancer(query).applySorting(Sort.by("firstname"))).endsWithIgnoringCase("order by firstname asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -344,7 +347,7 @@ void doesNotPrefixUnsafeJpaSortFunctionCalls() { JpaSort sort = JpaSort.unsafe("sum(foo)"); StringQuery query = new StringQuery("select p from Person p", true); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "p"), "order by sum(foo) asc"); + assertThat(getEnhancer(query).applySorting(sort, "p")).endsWithIgnoringCase("order by sum(foo) asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -354,7 +357,7 @@ void doesNotPrefixMultipleAliasedFunctionCalls() { true); Sort sort = Sort.by("avgPrice", "sumStocks"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by avgPrice asc, sumStocks asc"); + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWithIgnoringCase("order by avgPrice asc, sumStocks asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -363,7 +366,7 @@ void doesNotPrefixSingleAliasedFunctionCalls() { StringQuery query = new StringQuery("SELECT AVG(m.price) AS avgPrice FROM Magazine m", true); Sort sort = Sort.by("avgPrice"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by avgPrice asc"); + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWithIgnoringCase("order by avgPrice asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -372,7 +375,7 @@ void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { StringQuery query = new StringQuery("SELECT AVG(m.price) AS avgPrice FROM Magazine m", true); Sort sort = Sort.by("someOtherProperty"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by m.someOtherProperty asc"); + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWithIgnoringCase("order by m.someOtherProperty asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -381,7 +384,7 @@ void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainsAl StringQuery query = new StringQuery("SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m", true); Sort sort = Sort.by("name", "avgPrice"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by m.name asc, avgPrice asc"); + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWithIgnoringCase("order by m.name asc, avgPrice asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -390,7 +393,7 @@ void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() { StringQuery query = new StringQuery("SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m", true); Sort sort = Sort.by("trimmedName"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by trimmedName asc"); + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWithIgnoringCase("order by trimmedName asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -399,7 +402,7 @@ void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() { StringQuery query = new StringQuery("SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m", true); Sort sort = Sort.by("extendedName"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by extendedName asc"); + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWithIgnoringCase("order by extendedName asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -408,7 +411,7 @@ void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { StringQuery query = new StringQuery("SELECT AVG(m.price) AS avg_price FROM Magazine m", true); Sort sort = Sort.by("avg_price"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by avg_price asc"); + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWithIgnoringCase("order by avg_price asc"); } @Test // DATAJPA-965, DATAJPA-970 @@ -434,7 +437,7 @@ void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpa StringQuery query = new StringQuery("SELECT AVG( m.price ) AS avgPrice FROM Magazine m", true); Sort sort = Sort.by("avgPrice"); - endsIgnoringCase(getEnhancer(query).applySorting(sort, "m"), "order by avgPrice asc"); + assertThat(getEnhancer(query).applySorting(sort, "m")).endsWithIgnoringCase("order by avgPrice asc"); } @Test // DATAJPA-1000 @@ -452,7 +455,8 @@ void discoversCorrectAliasForJoinFetch() { @Test // DATAJPA-1171 void doesNotContainStaticClauseInExistsQuery() { - endsIgnoringCase(QueryUtils.getExistsQueryString("entity", "x", Collections.singleton("id")), "WHERE x.id = :id"); + assertThat(QueryUtils.getExistsQueryString("entity", "x", Collections.singleton("id"))) + .endsWithIgnoringCase("WHERE x.id = :id"); } @Test // DATAJPA-1363 @@ -512,7 +516,7 @@ void appliesSortCorrectlyForFieldAliases() { String fullQuery = getEnhancer(query).applySorting(sort); - endsIgnoringCase(fullQuery, "order by authorName asc"); + assertThat(fullQuery).endsWithIgnoringCase("order by authorName asc"); } @Test // GH-2280 @@ -538,7 +542,7 @@ void appliesSortCorrectlyForFunctionAliases() { String fullQuery = getEnhancer(query).applySorting(sort); - endsIgnoringCase(fullQuery, "order by title asc"); + assertThat(fullQuery).endsWithIgnoringCase("order by title asc"); } @Test // DATAJPA-1061 @@ -551,7 +555,7 @@ void appliesSortCorrectlyForSimpleField() { String fullQuery = getEnhancer(query).applySorting(sort); - endsIgnoringCase(fullQuery, "order by m.price asc"); + assertThat(fullQuery).endsWithIgnoringCase("order by m.price asc"); } @Test @@ -664,6 +668,7 @@ void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { @Test void createsCountQueriesCorrectlyForCapitalLetter() { + assertCountQuery("SELECT u FROM User u where u.foo.bar = ?", "select count(u) FROM User u where u.foo.bar = ?", true); } @@ -977,13 +982,6 @@ private static void assertCountQuery(StringQuery originalQuery, String countQuer assertThat(getEnhancer(originalQuery).createCountQueryFor()).isEqualToIgnoringCase(countQuery); } - private static void endsIgnoringCase(String original, String endWithIgnoreCase) { - - // https://github.com/assertj/assertj-core/pull/2451 - // can be removed when upgrading to version 3.23.0 assertJ - assertThat(original.toUpperCase()).endsWith(endWithIgnoreCase.toUpperCase()); - } - private static QueryEnhancer getEnhancer(DeclaredQuery query) { return QueryEnhancerFactory.forQuery(query); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index d030562690..7fd84f3b74 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -15,15 +15,11 @@ */ package org.springframework.data.jpa.repository.query; -import static java.util.Collections.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import jakarta.persistence.Entity; import jakarta.persistence.EntityManager; @@ -43,6 +39,12 @@ import jakarta.persistence.spi.PersistenceProviderResolver; import jakarta.persistence.spi.PersistenceProviderResolverHolder; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 17da9ba759..2c5653e161 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -15,9 +15,15 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; @@ -37,7 +43,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index ad3df88e98..836a293132 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import java.util.Arrays; import java.util.List; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java index 88e612a71e..433497fdd7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import jakarta.persistence.EntityManagerFactory; @@ -46,7 +46,7 @@ void repositoriesGetTheSecondEntityManagerFactoryInjected2() { reader.loadBeanDefinitions(new ClassPathResource("multiple-entity-manager-context.xml")); BeanDefinition bean = factory.getBeanDefinition("userRepository"); - Object value = getPropertyValue(bean, "entityManager"); + Object value = bean.getPropertyValues().getPropertyValue("entityManager").getValue(); assertThat(value).isInstanceOf(RuntimeBeanNameReference.class); BeanDefinition emCreator = (BeanDefinition) value; @@ -55,7 +55,6 @@ void repositoriesGetTheSecondEntityManagerFactoryInjected2() { } private Object getPropertyValue(BeanDefinition definition, String propertyName) { - return definition.getPropertyValues().getPropertyValue(propertyName).getValue(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java index 97c1f9357d..db6684898b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; @@ -24,6 +24,7 @@ import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.Type; +import lombok.Getter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -86,17 +87,10 @@ void usesPersistableMethodsForIsNewAndGetId() { @SuppressWarnings("serial") class Foo implements Persistable { - Long id; - - @Override - public Long getId() { - - return id; - } + @Getter Long id; @Override public boolean isNew() { - return id != null; } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index e3a4735121..cecd3f0c08 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -15,17 +15,19 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.io.Serializable; -import java.util.Optional; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.Mockito.when; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.metamodel.Metamodel; +import java.io.IOException; +import java.io.Serializable; +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,7 +35,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.aop.framework.Advised; import org.springframework.core.OverridingClassLoader; import org.springframework.data.jpa.domain.sample.User; @@ -62,7 +63,8 @@ public class JpaRepositoryFactoryUnitTests { @Mock EntityManager entityManager; @Mock Metamodel metamodel; - @Mock @SuppressWarnings("rawtypes") JpaEntityInformation entityInformation; + @Mock + @SuppressWarnings("rawtypes") JpaEntityInformation entityInformation; @Mock EntityManagerFactory emf; @BeforeEach @@ -91,7 +93,6 @@ public JpaEntityInformation getEntityInformation(Class domainC */ @Test void setsUpBasicInstanceCorrectly() { - assertThat(factory.getRepository(SimpleSampleRepository.class)).isNotNull(); } @@ -109,7 +110,6 @@ void allowsCallingOfObjectMethods() { * Asserts that the factory recognized configured predicateExecutor classes that contain custom method but no custom * implementation could be found. Furthremore the exception has to contain the name of the predicateExecutor interface * as for a large predicateExecutor configuration it's hard to find out where this error occured. - * */ @Test void capturesMissingCustomImplementationAndProvidesInterfacename() { @@ -125,6 +125,7 @@ void capturesMissingCustomImplementationAndProvidesInterfacename() { void handlesRuntimeExceptionsCorrectly() { SampleRepository repository = factory.getRepository(SampleRepository.class, new SampleCustomRepositoryImpl()); + assertThatIllegalArgumentException().isThrownBy(repository::throwingRuntimeException); } @@ -132,6 +133,7 @@ void handlesRuntimeExceptionsCorrectly() { void handlesCheckedExceptionsCorrectly() { SampleRepository repository = factory.getRepository(SampleRepository.class, new SampleCustomRepositoryImpl()); + assertThatExceptionOfType(IOException.class).isThrownBy(repository::throwingCheckedException); } @@ -149,8 +151,8 @@ void createsProxyWithCustomBaseClass() { void usesConfiguredRepositoryBaseClass() { factory.setRepositoryBaseClass(CustomJpaRepository.class); - SampleRepository repository = factory.getRepository(SampleRepository.class); + assertThat(((Advised) repository).getTargetClass()).isEqualTo(CustomJpaRepository.class); } @@ -158,10 +160,9 @@ void usesConfiguredRepositoryBaseClass() { void crudMethodMetadataPostProcessorUsesBeanClassLoader() { ClassLoader classLoader = new OverridingClassLoader(ClassUtils.getDefaultClassLoader()); - factory.setBeanClassLoader(classLoader); - Object processor = ReflectionTestUtils.getField(factory, "crudMethodMetadataPostProcessor"); + assertThat(ReflectionTestUtils.getField(processor, "classLoader")).isEqualTo((Object) classLoader); } @@ -208,13 +209,11 @@ private class SampleCustomRepositoryImpl implements SampleCustomRepository { @Override public void throwingRuntimeException() { - throw new IllegalArgumentException("You lose"); } @Override public void throwingCheckedException() throws IOException { - throw new IOException("You lose"); } }; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java index d4adb9df04..e0f7a22982 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -69,12 +69,14 @@ void testCrudOperationsForCompoundKeyEntity() { SampleEntity entity = new SampleEntity("foo", "bar"); repository.saveAndFlush(entity); + assertThat(repository.existsById(new SampleEntityPK("foo", "bar"))).isTrue(); assertThat(repository.count()).isOne(); assertThat(repository.findById(new SampleEntityPK("foo", "bar"))).contains(entity); repository.deleteAll(Arrays.asList(entity)); repository.flush(); + assertThat(repository.count()).isZero(); } @@ -128,6 +130,7 @@ void deleteAllByIdInBatch() { repository .deleteAllByIdInBatch(Arrays.asList(new SampleEntityPK("one", "eins"), new SampleEntityPK("three", "drei"))); + assertThat(repository.findAll()).containsExactly(two); } @@ -143,7 +146,7 @@ void deleteAllByIdInBatchShouldConvertAnIterableToACollection() { /** * Wrap a {@link List} inside an {@link Iterable} to verify that {@link SimpleJpaRepository} can properly convert a * pure {@link Iterable} to a {@link Collection}. - **/ + */ Iterable ids = new Iterable() { private List ids = Arrays.asList(new SampleEntityPK("one", "eins"), diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index e19dda73f2..a0eb72430a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -519,6 +520,7 @@ void findByFluentPredicateWithComplexPropertyPathsDoesntLoadsRequestedPaths() { } private interface UserProjectionInterfaceBased { + String getFirstname(); Set getRoles(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java index 5a5dbd63f6..ff51a4d861 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepositoryTests.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java index 61a47b7a98..9757948c56 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java @@ -15,17 +15,17 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.*; - -import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.data.jpa.domain.sample.QUser; import org.springframework.data.jpa.domain.sample.User; import org.springframework.test.context.ContextConfiguration; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java index fa4453d359..8abc28074a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/TransactionalRepositoryTests.java @@ -15,13 +15,12 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.repository.sample.UserRepository; @@ -48,13 +47,11 @@ public class TransactionalRepositoryTests { @BeforeEach void setUp() { - transactionManager.resetCount(); } @AfterEach void tearDown() { - repository.deleteAll(); } @@ -102,13 +99,11 @@ public static class DelegatingTransactionManager implements PlatformTransactionM private TransactionDefinition definition; public DelegatingTransactionManager(PlatformTransactionManager txManager) { - this.txManager = txManager; } @Override public void commit(TransactionStatus status) throws TransactionException { - txManager.commit(status); } @@ -122,12 +117,10 @@ public TransactionStatus getTransaction(TransactionDefinition definition) throws } int getTransactionRequests() { - return transactionRequests; } public TransactionDefinition getDefinition() { - return definition; } @@ -139,7 +132,6 @@ public void resetCount() { @Override public void rollback(TransactionStatus status) throws TransactionException { - txManager.rollback(status); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java index 8777d6b7ff..91e08024f7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/MergingPersistenceUnitManagerUnitTests.java @@ -15,22 +15,22 @@ */ package org.springframework.data.jpa.support; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.persistence.spi.PersistenceUnitInfo; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; -import jakarta.persistence.spi.PersistenceUnitInfo; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.User; import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo; @@ -66,7 +66,7 @@ void mergesManagedClassesCorrectly() { MergingPersistenceUnitManager manager = new MergingPersistenceUnitManager(); manager.setPersistenceXmlLocations("classpath:org/springframework/data/jpa/support/persistence.xml", - "classpath:org/springframework/data/jpa/support/persistence2.xml"); + "classpath:org/springframework/data/jpa/support/persistence2.xml"); manager.preparePersistenceUnitInfos(); PersistenceUnitInfo info = manager.obtainPersistenceUnitInfo("pu"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java index 42e401759a..51d2e3cfce 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/JpaMetamodelCacheCleanupIntegrationTests.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.util; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; import jakarta.persistence.metamodel.Metamodel; @@ -26,7 +26,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.support.GenericApplicationContext; import org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension; From 91e88681330d5e1457a2590c91b381328eba9f5d Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 24 Jan 2023 11:20:45 -0600 Subject: [PATCH 298/821] Use default visibility for test classes when possible. Related: #2746. --- .../convert/threeten/Jsr310JpaConvertersIntegrationTests.java | 2 +- .../data/jpa/domain/support/AuditingEntityListenerTests.java | 2 +- .../support/AuditingEntityWithEmbeddableListenerTests.java | 2 +- .../domain/support/QueryByExampleWithOptionalEmptyTests.java | 2 +- .../data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java | 2 +- .../HibernateJpaParametersParameterAccessorUnitTests.java | 2 +- .../jpa/provider/PersistenceProviderIntegrationTests.java | 2 +- .../jpa/repository/AbstractPersistableIntegrationTests.java | 2 +- .../repository/CustomAbstractPersistableIntegrationTests.java | 2 +- .../EntityGraphRepositoryMethodsIntegrationTests.java | 2 +- .../jpa/repository/EntityWithAssignedIdIntegrationTests.java | 2 +- .../jpa/repository/MappedTypeRepositoryIntegrationTests.java | 2 +- .../data/jpa/repository/NamespaceUserRepositoryTests.java | 2 +- .../data/jpa/repository/ORMInfrastructureTests.java | 2 +- .../data/jpa/repository/ParentRepositoryIntegrationTests.java | 2 +- .../repository/QueryByExampleEclipseLinkIntegrationTests.java | 2 +- .../repository/QueryByExampleHibernateIntegrationTests.java | 2 +- .../jpa/repository/RedeclaringRepositoryMethodsTests.java | 2 +- .../data/jpa/repository/RepositoryWithCompositeKeyTests.java | 2 +- .../data/jpa/repository/RepositoryWithIdClassKeyTests.java | 2 +- .../data/jpa/repository/RoleRepositoryIntegrationTests.java | 2 +- .../org/springframework/data/jpa/repository/SPR8954Tests.java | 2 +- .../data/jpa/repository/StoredProcedureIntegrationTests.java | 2 +- .../data/jpa/repository/UserRepositoryFinderTests.java | 2 +- .../UserRepositoryStoredProcedureIntegrationTests.java | 2 +- .../data/jpa/repository/UserRepositoryTests.java | 2 +- .../cdi/JpaQueryRewriterWithCdiIntegrationTests.java | 2 +- .../config/AllowNestedRepositoriesRepositoryConfigTests.java | 2 +- .../repository/config/CustomRepositoryFactoryConfigTests.java | 2 +- .../config/JpaRepositoriesRegistrarIntegrationTests.java | 2 +- .../repository/config/NestedRepositoriesJavaConfigTests.java | 2 +- .../data/jpa/repository/config/QueryLookupStrategyTests.java | 2 +- .../jpa/repository/config/RepositoriesJavaConfigTests.java | 2 +- .../procedures/MySqlStoredProcedureIntegrationTests.java | 2 +- .../procedures/PostgresStoredProcedureIntegrationTests.java | 2 +- .../PostgresStoredProcedureNullHandlingIntegrationTests.java | 4 ++-- .../projections/ProjectionJoinIntegrationTests.java | 2 +- .../repository/projections/ProjectionsIntegrationTests.java | 2 +- .../data/jpa/repository/query/AbstractJpaQueryTests.java | 2 +- .../query/AbstractStringBasedJpaQueryIntegrationTests.java | 2 +- .../query/CustomNonBindableJpaParametersIntegrationTests.java | 2 +- .../data/jpa/repository/query/Jpa21UtilsTests.java | 2 +- .../query/JpaCountQueryCreatorIntegrationTests.java | 2 +- .../jpa/repository/query/JpaQueryLookupStrategyUnitTests.java | 2 +- .../data/jpa/repository/query/JpaQueryMethodUnitTests.java | 2 +- .../jpa/repository/query/JpaQueryRewriteIntegrationTests.java | 2 +- .../MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java | 2 +- .../MetaAnnotatedQueryMethodHibernateIntegrationTests.java | 2 +- .../repository/query/MetaAnnotatedQueryMethodUnitTests.java | 2 +- .../data/jpa/repository/query/ParameterBinderUnitTests.java | 2 +- .../repository/query/PartTreeJpaQueryIntegrationTests.java | 2 +- .../data/jpa/repository/query/QueryUtilsIntegrationTests.java | 2 +- .../query/QueryWithNullLikeHibernateIntegrationTests.java | 2 +- .../data/jpa/repository/query/TupleConverterUnitTests.java | 2 +- .../repository/support/DefaultJpaContextIntegrationTests.java | 2 +- .../repository/support/DefaultJpaEntityMetadataUnitTest.java | 2 +- ...rBeanDefinitionRegistrarPostProcessorIntegrationTests.java | 2 +- ...yManagerBeanDefinitionRegistrarPostProcessorUnitTests.java | 2 +- .../jpa/repository/support/EntityManagerFactoryRefTests.java | 2 +- .../repository/support/EntityManagerFactoryRefUnitTests.java | 2 +- ...ibernateJpaMetamodelEntityInformationIntegrationTests.java | 2 +- .../support/JpaEntityInformationSupportUnitTests.java | 2 +- .../jpa/repository/support/JpaRepositoryFactoryUnitTests.java | 2 +- .../support/MailMessageRepositoryIntegrationTests.java | 2 +- .../support/QuerydslRepositorySupportIntegrationTests.java | 2 +- .../repository/support/QuerydslRepositorySupportTests.java | 2 +- ...lasspathScanningPersistenceUnitPostProcessorUnitTests.java | 2 +- 67 files changed, 68 insertions(+), 68 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java index 60a2d9d4a0..9dd0fd14bc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/threeten/Jsr310JpaConvertersIntegrationTests.java @@ -44,7 +44,7 @@ */ @ContextConfiguration @Transactional -public class Jsr310JpaConvertersIntegrationTests extends AbstractAttributeConverterIntegrationTests { +class Jsr310JpaConvertersIntegrationTests extends AbstractAttributeConverterIntegrationTests { @PersistenceContext EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java index 0d1f3f1052..5bef8dba3b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java @@ -46,7 +46,7 @@ @ContextConfiguration("classpath:auditing/auditing-entity-listener.xml") @Transactional @DirtiesContext -public class AuditingEntityListenerTests { +class AuditingEntityListenerTests { @Autowired AuditableUserRepository repository; @Autowired AnnotatedAuditableUserRepository annotatedUserRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java index 91bab5b4e0..c1bc9ba919 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityWithEmbeddableListenerTests.java @@ -37,7 +37,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:auditing/auditing-entity-with-embeddable-listener.xml") -public class AuditingEntityWithEmbeddableListenerTests { +class AuditingEntityWithEmbeddableListenerTests { @Autowired AuditableEntityRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java index 05da8ab442..737295a9c5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java @@ -40,7 +40,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class QueryByExampleWithOptionalEmptyTests { +class QueryByExampleWithOptionalEmptyTests { @Autowired UserWithOptionalFieldRepository repository; UserWithOptionalField user; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index 813f2deb86..d72c2abf41 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -55,7 +55,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class JpaPersistentPropertyImplUnitTests { +class JpaPersistentPropertyImplUnitTests { @Mock Metamodel model; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java index 68cbc79fa2..fa5810927e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java @@ -22,7 +22,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:hjppa-test.xml") -public class HibernateJpaParametersParameterAccessorUnitTests { +class HibernateJpaParametersParameterAccessorUnitTests { @Autowired private EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java index 5d86d05f77..c8251966ff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/PersistenceProviderIntegrationTests.java @@ -48,7 +48,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class PersistenceProviderIntegrationTests { +public abstract class PersistenceProviderIntegrationTests { @Autowired CategoryRepository categories; @Autowired ProductRepository products; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java index 83e5b43f5b..ad5e5430b4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/AbstractPersistableIntegrationTests.java @@ -41,7 +41,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = { "classpath:config/namespace-autoconfig-context.xml" }) -public class AbstractPersistableIntegrationTests { +class AbstractPersistableIntegrationTests { @Autowired CustomAbstractPersistableRepository repository; @Autowired EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java index 5873cd8342..55bb30ecc8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/CustomAbstractPersistableIntegrationTests.java @@ -35,7 +35,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = { "classpath:config/namespace-autoconfig-context.xml" }) -public class CustomAbstractPersistableIntegrationTests { +class CustomAbstractPersistableIntegrationTests { @Autowired CustomAbstractPersistableRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java index 20726dc212..d9f105b45a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityGraphRepositoryMethodsIntegrationTests.java @@ -60,7 +60,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:config/namespace-autoconfig-context.xml") @Transactional -public class EntityGraphRepositoryMethodsIntegrationTests { +class EntityGraphRepositoryMethodsIntegrationTests { @Autowired EntityManager em; @Autowired RepositoryMethodsWithEntityGraphConfigRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java index f68e0e9309..af52cedec9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EntityWithAssignedIdIntegrationTests.java @@ -33,7 +33,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:config/namespace-application-context.xml") @Transactional -public class EntityWithAssignedIdIntegrationTests { +class EntityWithAssignedIdIntegrationTests { @Autowired EntityWithAssignedIdRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java index 9d6a87ad6a..e124a05857 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/MappedTypeRepositoryIntegrationTests.java @@ -51,7 +51,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SampleConfig.class) -public class MappedTypeRepositoryIntegrationTests { +class MappedTypeRepositoryIntegrationTests { @Autowired ConcreteRepository1 concreteRepository1; @Autowired ConcreteRepository2 concreteRepository2; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java index 1115aab9fb..19dd1fefc6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/NamespaceUserRepositoryTests.java @@ -32,7 +32,7 @@ * @author Eberhard Wolff */ @ContextConfiguration(locations = "classpath:config/namespace-application-context.xml", inheritLocations = false) -public class NamespaceUserRepositoryTests extends UserRepositoryTests { +class NamespaceUserRepositoryTests extends UserRepositoryTests { @Autowired ListableBeanFactory beanFactory; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java index 326443e5a8..8effdd191e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ORMInfrastructureTests.java @@ -33,7 +33,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = "classpath:infrastructure.xml") -public class ORMInfrastructureTests { +class ORMInfrastructureTests { @Autowired ApplicationContext context; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java index d64e7124c1..b149498d9f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/ParentRepositoryIntegrationTests.java @@ -48,7 +48,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:config/namespace-application-context.xml") -public class ParentRepositoryIntegrationTests { +class ParentRepositoryIntegrationTests { @Autowired ParentRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java index 2a119d713f..b0abec19ee 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java @@ -43,7 +43,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:eclipselink.xml", "classpath:config/namespace-application-context.xml" }) @Transactional -public class QueryByExampleEclipseLinkIntegrationTests { +class QueryByExampleEclipseLinkIntegrationTests { @Autowired RoleRepository repository; @Autowired EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java index 846fd157a9..5d7c257f28 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java @@ -43,7 +43,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:hibernate.xml", "classpath:config/namespace-application-context.xml" }) @Transactional -public class QueryByExampleHibernateIntegrationTests { +class QueryByExampleHibernateIntegrationTests { @Autowired RoleRepository repository; @Autowired EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java index e49c76b2e3..10aa730845 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RedeclaringRepositoryMethodsTests.java @@ -40,7 +40,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SampleConfig.class) @Transactional -public class RedeclaringRepositoryMethodsTests { +class RedeclaringRepositoryMethodsTests { @Autowired RedeclaringRepositoryMethodsRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java index 077d886da6..362ac02cc5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithCompositeKeyTests.java @@ -55,7 +55,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SampleConfig.class) @Transactional -public class RepositoryWithCompositeKeyTests { +class RepositoryWithCompositeKeyTests { @Autowired EmployeeRepositoryWithIdClass employeeRepositoryWithIdClass; @Autowired EmployeeRepositoryWithEmbeddedId employeeRepositoryWithEmbeddedId; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java index e17c20b2b5..314998dcb9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java @@ -48,7 +48,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = RepositoryWithIdClassKeyTests.TestConfig.class) @Transactional -public class RepositoryWithIdClassKeyTests { +class RepositoryWithIdClassKeyTests { @Autowired private SiteRepository siteRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java index d071a6c4d1..8c39855de2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RoleRepositoryIntegrationTests.java @@ -38,7 +38,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = { "classpath:application-context.xml" }) @Transactional -public class RoleRepositoryIntegrationTests { +class RoleRepositoryIntegrationTests { @Autowired RoleRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java index b59d52d57a..0824c9ffc1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/SPR8954Tests.java @@ -35,7 +35,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:config/namespace-application-context.xml") -public class SPR8954Tests { +class SPR8954Tests { @Autowired ApplicationContext context; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java index e053a43a10..63ac1d0fcc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/StoredProcedureIntegrationTests.java @@ -53,7 +53,7 @@ @Transactional @ContextConfiguration(classes = StoredProcedureIntegrationTests.TestConfig.class) @ExtendWith(SpringExtension.class) -public class StoredProcedureIntegrationTests { +class StoredProcedureIntegrationTests { private static final String NOT_SUPPORTED = "Stored procedures with REF_CURSOR are currently not supported by HSQL dialect"; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 13b4c3b336..721256d614 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -57,7 +57,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = "classpath:config/namespace-application-context.xml") @Transactional -public class UserRepositoryFinderTests { +class UserRepositoryFinderTests { @Autowired UserRepository userRepository; @Autowired RoleRepository roleRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java index 2fbc17e058..719a7b8794 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryStoredProcedureIntegrationTests.java @@ -47,7 +47,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @Transactional -public class UserRepositoryStoredProcedureIntegrationTests { +class UserRepositoryStoredProcedureIntegrationTests { @Autowired UserRepository repository; @PersistenceContext EntityManager em; 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 96d6a48729..f555d1baa6 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 @@ -111,7 +111,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @Transactional -public class UserRepositoryTests { +class UserRepositoryTests { @PersistenceContext EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java index 4a34bebe9f..1a257f2a4e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/cdi/JpaQueryRewriterWithCdiIntegrationTests.java @@ -46,7 +46,7 @@ * * @author Greg Turnquist */ -public class JpaQueryRewriterWithCdiIntegrationTests { +class JpaQueryRewriterWithCdiIntegrationTests { private static SeContainer container; private static Log LOGGER = LogFactory.getLog(CdiExtensionIntegrationTests.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java index 27ff1eb7a9..ad0e47b2f0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/AllowNestedRepositoriesRepositoryConfigTests.java @@ -30,7 +30,7 @@ * @author Jens Schauder */ @ContextConfiguration(locations = "classpath:config/namespace-nested-repositories-application-context.xml") -public class AllowNestedRepositoriesRepositoryConfigTests extends AbstractRepositoryConfigTests { +class AllowNestedRepositoriesRepositoryConfigTests extends AbstractRepositoryConfigTests { @Autowired NestedUserRepository fooRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java index 6d260f8554..49cbb73c42 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java @@ -42,7 +42,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = "classpath:config/namespace-customfactory-context.xml") -public class CustomRepositoryFactoryConfigTests { +class CustomRepositoryFactoryConfigTests { @Autowired(required = false) UserCustomExtendedRepository userRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java index 52512d743c..8a8e739e1f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java @@ -53,7 +53,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class JpaRepositoriesRegistrarIntegrationTests { +class JpaRepositoriesRegistrarIntegrationTests { @Autowired UserRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java index 23ebca9de6..55dd23500c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/NestedRepositoriesJavaConfigTests.java @@ -38,7 +38,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class NestedRepositoriesJavaConfigTests { +class NestedRepositoriesJavaConfigTests { @Autowired NestedUserRepository nestedUserRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java index 288119bd4f..8ce3e4a606 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/QueryLookupStrategyTests.java @@ -38,7 +38,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = "classpath:config/lookup-strategies-context.xml") -public class QueryLookupStrategyTests { +class QueryLookupStrategyTests { @Autowired ApplicationContext context; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java index a9a33c549b..7805669698 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/RepositoriesJavaConfigTests.java @@ -39,7 +39,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class RepositoriesJavaConfigTests { +class RepositoriesJavaConfigTests { @Autowired Repositories repositories; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java index eec0cdaa2b..cf4ce08c03 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -68,7 +68,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MySqlStoredProcedureIntegrationTests.Config.class) -public class MySqlStoredProcedureIntegrationTests { +class MySqlStoredProcedureIntegrationTests { @Autowired EmployeeRepositoryWithNoCursor repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index c812307f53..48907cbccb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -70,7 +70,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = PostgresStoredProcedureIntegrationTests.Config.class) -public class PostgresStoredProcedureIntegrationTests { +class PostgresStoredProcedureIntegrationTests { @Autowired EmployeeRepositoryWithRefCursor repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java index 6f494ef696..2d508020d3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java @@ -65,7 +65,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = PostgresStoredProcedureNullHandlingIntegrationTests.Config.class) -public class PostgresStoredProcedureNullHandlingIntegrationTests { +class PostgresStoredProcedureNullHandlingIntegrationTests { @Autowired TestModelRepository repository; @@ -83,7 +83,7 @@ void invokingNullOnTemporalStoredProcedureParameterShouldWork() { @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity - public class TestModel { + class TestModel { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java index c3465f431c..d100cb3f0f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java @@ -44,7 +44,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = ProjectionsIntegrationTests.Config.class) -public class ProjectionJoinIntegrationTests { +class ProjectionJoinIntegrationTests { @Autowired private UserRepository userRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java index e638fc2ad6..0572cef1cf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java @@ -62,7 +62,7 @@ @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = Config.class) -public class ProjectionsIntegrationTests { +class ProjectionsIntegrationTests { @Autowired DummyEntityWithCollectionRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index b64e34bb9f..f2ee0c720b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -59,7 +59,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") -public class AbstractJpaQueryTests { +class AbstractJpaQueryTests { @PersistenceContext EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java index a36e9821cc..637cd34f51 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQueryIntegrationTests.java @@ -52,7 +52,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") -public class AbstractStringBasedJpaQueryIntegrationTests { +class AbstractStringBasedJpaQueryIntegrationTests { @PersistenceContext EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java index 7e9333b85c..0ddbbdf14b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java @@ -48,7 +48,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class CustomNonBindableJpaParametersIntegrationTests { +class CustomNonBindableJpaParametersIntegrationTests { @Autowired ProductRepository products; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java index f878687c5d..bf7a79f1aa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java @@ -54,7 +54,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application-context.xml") @Transactional -public class Jpa21UtilsTests { +class Jpa21UtilsTests { @Autowired EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java index 49fca35ec6..d1f241e36d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreatorIntegrationTests.java @@ -49,7 +49,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") -public class JpaCountQueryCreatorIntegrationTests { +class JpaCountQueryCreatorIntegrationTests { @PersistenceContext EntityManager entityManager; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index 8eb9037fc0..a425dbce4f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -61,7 +61,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class JpaQueryLookupStrategyUnitTests { +class JpaQueryLookupStrategyUnitTests { private static final QueryMethodEvaluationContextProvider EVALUATION_CONTEXT_PROVIDER = QueryMethodEvaluationContextProvider.DEFAULT; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 564493d7ae..62c2cc8f9f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -69,7 +69,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class JpaQueryMethodUnitTests { +class JpaQueryMethodUnitTests { private static final String METHOD_NAME = "findByFirstname"; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java index 90c8701283..1cfa1d32ec 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryRewriteIntegrationTests.java @@ -51,7 +51,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class JpaQueryRewriteIntegrationTests { +class JpaQueryRewriteIntegrationTests { @Autowired private UserRepositoryWithRewriter repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java index b14b5530dc..21673b6f97 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java @@ -69,7 +69,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration @Transactional -public class MetaAnnotatedQueryMethodEclipseLinkIntegrationTests { +class MetaAnnotatedQueryMethodEclipseLinkIntegrationTests { @Autowired RoleRepositoryWithMeta repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java index 3c1d96152e..ae69439836 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java @@ -67,7 +67,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration @Transactional -public class MetaAnnotatedQueryMethodHibernateIntegrationTests { +class MetaAnnotatedQueryMethodHibernateIntegrationTests { @Autowired RoleRepositoryWithMeta repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java index f4dd7f81eb..edca8ebe4e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodUnitTests.java @@ -41,7 +41,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class MetaAnnotatedQueryMethodUnitTests { +class MetaAnnotatedQueryMethodUnitTests { @Mock QueryExtractor extractor; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java index 0ad1439f67..bc22024d07 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java @@ -56,7 +56,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class ParameterBinderUnitTests { +class ParameterBinderUnitTests { private static final int MAX_PARAMETERS = 1; private Method valid; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 3be03263a0..a124e60e5f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -61,7 +61,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") -public class PartTreeJpaQueryIntegrationTests { +class PartTreeJpaQueryIntegrationTests { private static String PROPERTY = "h.target." + getQueryProperty(); private static Class HIBERNATE_NATIVE_QUERY = org.hibernate.query.Query.class; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index 7fd84f3b74..123806b39f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -72,7 +72,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") -public class QueryUtilsIntegrationTests { +class QueryUtilsIntegrationTests { @PersistenceContext EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java index 710b4f927a..37dbb792a9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java @@ -57,7 +57,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = QueryWithNullLikeHibernateIntegrationTests.Config.class) @Transactional -public class QueryWithNullLikeHibernateIntegrationTests { +class QueryWithNullLikeHibernateIntegrationTests { @Autowired EmployeeWithNullLikeRepository repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java index 7df85fefc7..d898db9334 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/TupleConverterUnitTests.java @@ -52,7 +52,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class TupleConverterUnitTests { +class TupleConverterUnitTests { @Mock Tuple tuple; @Mock TupleElement element; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java index 92013297d6..9a7a4d53ea 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaContextIntegrationTests.java @@ -50,7 +50,7 @@ * @author Jens Schauder * @soundtrack Marcus Miller - Papa Was A Rolling Stone (Afrodeezia) */ -public class DefaultJpaContextIntegrationTests { +class DefaultJpaContextIntegrationTests { private static EntityManagerFactory firstEmf; private static EntityManagerFactory secondEmf; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java index 091ea0cd34..e78dfaaf5c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/DefaultJpaEntityMetadataUnitTest.java @@ -33,7 +33,7 @@ * @author Christoph Strobl * @author Jens Schauder */ -public class DefaultJpaEntityMetadataUnitTest { +class DefaultJpaEntityMetadataUnitTest { @Test @SuppressWarnings({ "rawtypes", "unchecked" }) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java index e9dc44d405..decbc2f8a6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests.java @@ -41,7 +41,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration -public class EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests { +class EntityManagerBeanDefinitionRegistrarPostProcessorIntegrationTests { @Autowired EntityManagerInjectionTarget target; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java index 15eebed827..d47abf82ec 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests.java @@ -35,7 +35,7 @@ * @author Jens Schauder * @author Mark Paluch */ -public class EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests { +class EntityManagerBeanDefinitionRegistrarPostProcessorUnitTests { @Test // DATAJPA-453 void findsBeanDefinitionInParentBeanFactory() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java index 41f9b4d22f..d082af99f2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefTests.java @@ -36,7 +36,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration(locations = "classpath:multiple-entity-manager-integration-context.xml") -public class EntityManagerFactoryRefTests { +class EntityManagerFactoryRefTests { @Autowired UserRepository userRepository; @Autowired AuditableUserRepository auditableUserRepository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java index 433497fdd7..73a98c0d30 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/EntityManagerFactoryRefUnitTests.java @@ -35,7 +35,7 @@ * @author Jens Schauder * @author Krzysztof Krason */ -public class EntityManagerFactoryRefUnitTests { +class EntityManagerFactoryRefUnitTests { @Test @Disabled diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java index f11a30b9a7..2628d87bf3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java @@ -26,7 +26,7 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") -public class HibernateJpaMetamodelEntityInformationIntegrationTests +class HibernateJpaMetamodelEntityInformationIntegrationTests extends JpaMetamodelEntityInformationIntegrationTests { @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java index 099c770ffd..78268f7b12 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java @@ -43,7 +43,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class JpaEntityInformationSupportUnitTests { +class JpaEntityInformationSupportUnitTests { @Mock EntityManager em; @Mock Metamodel metaModel; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index cecd3f0c08..9e8ad5994e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -57,7 +57,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class JpaRepositoryFactoryUnitTests { +class JpaRepositoryFactoryUnitTests { private JpaRepositoryFactory factory; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java index 2306611c4c..9427a33762 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java @@ -55,7 +55,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SampleConfig.class) @Transactional -public class MailMessageRepositoryIntegrationTests { +class MailMessageRepositoryIntegrationTests { private static final QMailMessage message = QMailMessage.mailMessage; static final QMailSender sender = QMailSender.mailSender; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java index 8a14108d93..5704fa2235 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportIntegrationTests.java @@ -45,7 +45,7 @@ @Transactional @ContextConfiguration @ExtendWith(SpringExtension.class) -public class QuerydslRepositorySupportIntegrationTests { +class QuerydslRepositorySupportIntegrationTests { @Configuration @EnableTransactionManagement diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java index 9757948c56..3decd7d034 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupportTests.java @@ -43,7 +43,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:infrastructure.xml" }) @Transactional -public class QuerydslRepositorySupportTests { +class QuerydslRepositorySupportTests { @PersistenceContext EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java index 9c95e51e50..f1f0f8f33f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessorUnitTests.java @@ -50,7 +50,7 @@ */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class ClasspathScanningPersistenceUnitPostProcessorUnitTests { +class ClasspathScanningPersistenceUnitPostProcessorUnitTests { @Mock MutablePersistenceUnitInfo pui; private String basePackage = getClass().getPackage().getName(); From b06e5ead7d05b5f6478b199f859178a5484841c6 Mon Sep 17 00:00:00 2001 From: TheWing Date: Tue, 24 Jan 2023 18:55:04 +0900 Subject: [PATCH 299/821] Fix typos in code. Resolves #2775. --- .../data/jpa/mapping/JpaMetamodelMappingContext.java | 2 +- .../data/jpa/support/MergingPersistenceUnitManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index 8138692776..9cc715cbd3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -180,7 +180,7 @@ private Metamodel getMetamodelFor(Class type) { } catch (IllegalArgumentException o_O) { // Fall back to inspect *all* managed types manually as Metamodel.managedType(…) only - // returns for entities, embeddables and managed supperclasses. + // returns for entities, embeddables and managed superclasses. for (ManagedType managedType : model.getManagedTypes()) { if (type.equals(managedType.getJavaType())) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index fb60cdfac5..ce572064e8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -27,7 +27,7 @@ /** * Extends {@link DefaultPersistenceUnitManager} to merge configurations of one persistence unit residing in multiple - * {@code persistence.xml} files into one. This is necessary to allow the declaration of entities in seperate modules. + * {@code persistence.xml} files into one. This is necessary to allow the declaration of entities in separate modules. * * @author Oliver Gierke * @link https://github.com/spring-projects/spring-framework/issues/7287 From be84272b7b834be3f5708c1961d4fc1a2ff8bf53 Mon Sep 17 00:00:00 2001 From: ErikPelli Date: Thu, 22 Dec 2022 17:16:57 +0100 Subject: [PATCH 300/821] Change calls to deprecated methods in tests to their replacements. * ClassTypeInformation is deprecated and will be removed. Use TypeInformation instead. * StringUtils.trimWhitespace can be replaced with the strip method called on the String object. * StandardAnnotationMetadata is deprecated in favor of the factory method AnnotationMetadata.introspect(Class). Resolves #2739. --- .../data/jpa/mapping/JpaPersistentPropertyImpl.java | 4 ++-- .../data/jpa/infrastructure/HibernateTestUtils.java | 3 ++- .../data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java | 4 ++-- .../config/JpaRepositoriesRegistrarIntegrationTests.java | 5 +++-- .../repository/config/JpaRepositoriesRegistrarUnitTests.java | 4 ++-- .../data/jpa/repository/query/JpaQueryMethodUnitTests.java | 5 +++-- .../data/jpa/repository/query/NamedQueryUnitTests.java | 5 +++-- .../data/jpa/repository/query/QueryUtilsUnitTests.java | 4 ++-- .../data/jpa/repository/query/SimpleJpaQueryUnitTests.java | 5 +++-- 9 files changed, 22 insertions(+), 17 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index a3991e9fa6..1e3f604a83 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -32,7 +32,6 @@ import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; -import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.Lazy; import org.springframework.data.util.TypeInformation; import org.springframework.lang.Nullable; @@ -46,6 +45,7 @@ * @author Greg Turnquist * @author Christoph Strobl * @author Mark Paluch + * @author Erik Pellizzon * @since 1.3 */ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty @@ -254,7 +254,7 @@ private TypeInformation detectAssociationTargetType() { continue; } - return ClassTypeInformation.from((Class) entityValue); + return TypeInformation.of((Class) entityValue); } return null; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java index 289d38d833..0507c7ba6c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java @@ -26,6 +26,7 @@ * Testing utilities for Hibernate. * * @author Oliver Gierke + * @author Erik Pellizzon * @soundtrack Ron Spielman - Africa's Napoleon (Swimming In The Dark) * @since 1.10.2 */ @@ -48,7 +49,7 @@ public static PersistenceProvider getPersistenceProvider() { if (ClassUtils.isPresent(provider, classLoader)) { try { - return (PersistenceProvider) ClassUtils.forName(provider, classLoader).newInstance(); + return (PersistenceProvider) ClassUtils.forName(provider, classLoader).getDeclaredConstructor().newInstance(); } catch (Exception o_O) { throw new RuntimeException(o_O); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index d72c2abf41..f8b23a493d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -43,7 +43,6 @@ import org.mockito.quality.Strictness; import org.springframework.data.annotation.AccessType.Type; import org.springframework.data.annotation.Version; -import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; /** @@ -52,6 +51,7 @@ * @author Oliver Gierke * @author Greg Turnquist * @author Jens Schauder + * @author Erik Pellizzon */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -147,7 +147,7 @@ void considersTargetEntityTypeForPropertyType() { Iterable> entityType = property.getPersistentEntityTypeInformation(); assertThat(entityType.iterator().hasNext()).isTrue(); assertThat(entityType.iterator().next()) - .isEqualTo(ClassTypeInformation.from(Implementation.class)); + .isEqualTo(TypeInformation.of(Implementation.class)); } @Test // DATAJPA-716 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java index 8a8e739e1f..8ae122e050 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java @@ -28,6 +28,7 @@ import org.springframework.aop.Advisor; import org.springframework.aop.framework.Advised; +import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -43,13 +44,13 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.util.ClassUtils; /** * Integration test for {@link JpaRepositoriesRegistrar}. * * @author Oliver Gierke * @author Jens Schauder + * @author Erik Pellizzon */ @ExtendWith(SpringExtension.class) @ContextConfiguration @@ -103,7 +104,7 @@ void foo() { void doesNotProxyPlainAtRepositoryBeans() { assertThat(sampleRepository).isNotNull(); - assertThat(ClassUtils.isCglibProxy(sampleRepository)).isFalse(); + assertThat(AopUtils.isCglibProxy(sampleRepository)).isFalse(); assertExceptionTranslationActive(repository); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java index e1d8718fbd..9ce887397f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java @@ -26,7 +26,6 @@ import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.data.jpa.repository.sample.UserRepository; /** @@ -34,6 +33,7 @@ * * @author Oliver Gierke * @author Jens Schauder + * @author Erik Pellizzon */ class JpaRepositoriesRegistrarUnitTests { @@ -43,7 +43,7 @@ class JpaRepositoriesRegistrarUnitTests { @BeforeEach void setUp() { - metadata = new StandardAnnotationMetadata(Config.class, true); + metadata = AnnotationMetadata.introspect(Config.class); registry = new DefaultListableBeanFactory(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 62c2cc8f9f..cc5cf56964 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -56,7 +56,7 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; /** * Unit test for {@link QueryMethod}. @@ -66,6 +66,7 @@ * @author Christoph Strobl * @author Jens Schauder * @author Mark Paluch + * @author Erik Pellizzon */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -107,7 +108,7 @@ void setUp() throws Exception { Integer.class); when(metadata.getReturnType(any(Method.class))) - .thenAnswer(invocation -> ClassTypeInformation.fromReturnTypeOf(invocation.getArgument(0))); + .thenAnswer(invocation -> TypeInformation.fromReturnTypeOf(invocation.getArgument(0))); } @Test diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java index 8e70800e97..9bc82fe381 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java @@ -41,7 +41,7 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryCreationException; -import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; /** * Unit tests for {@link NamedQuery}. @@ -49,6 +49,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Mark Paluch + * @author Erik Pellizzon */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -72,7 +73,7 @@ void setUp() throws SecurityException, NoSuchMethodException { when(metadata.getDomainType()).thenReturn((Class) String.class); when(metadata.getReturnedDomainClass(method)).thenReturn((Class) String.class); when(metadata.getReturnType(any(Method.class))) - .thenAnswer(invocation -> ClassTypeInformation.fromReturnTypeOf(invocation.getArgument(0))); + .thenAnswer(invocation -> TypeInformation.fromReturnTypeOf(invocation.getArgument(0))); when(em.getMetamodel()).thenReturn(metamodel); when(em.getEntityManagerFactory()).thenReturn(emf); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 45ce3b75e8..62e0b070f7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -30,7 +30,6 @@ import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.JpaSort; -import org.springframework.util.StringUtils; /** * Unit test for {@link QueryUtils}. @@ -48,6 +47,7 @@ * @author Darin Manica * @author Chris Fraser * @author Michał Pachucki + * @author Erik Pellizzon */ class QueryUtilsUnitTests { @@ -264,7 +264,7 @@ private String normalizeWhitespace(String s) { return matcher.replaceAll(" ").trim(); } - return StringUtils.trimWhitespace(s); + return s.strip(); } @Test diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 2c5653e161..4c7b5d1af7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -57,7 +57,7 @@ import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; -import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; import org.springframework.expression.spel.standard.SpelExpressionParser; /** @@ -70,6 +70,7 @@ * @author Mark Paluch * @author Greg Turnquist * @author Krzysztof Krason + * @author Erik Pellizzon */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -105,7 +106,7 @@ void setUp() throws SecurityException, NoSuchMethodException { when(metadata.getDomainType()).thenReturn((Class) User.class); when(metadata.getReturnedDomainClass(Mockito.any(Method.class))).thenReturn((Class) User.class); when(metadata.getReturnType(Mockito.any(Method.class))) - .thenAnswer(invocation -> ClassTypeInformation.fromReturnTypeOf(invocation.getArgument(0))); + .thenAnswer(invocation -> TypeInformation.fromReturnTypeOf(invocation.getArgument(0))); Method setUp = UserRepository.class.getMethod("findByLastname", String.class); method = new JpaQueryMethod(setUp, metadata, factory, extractor); From 8d74a43cd5d8560d2a9e8c53e4fca11802cde0ab Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 24 Jan 2023 15:06:28 -0600 Subject: [PATCH 301/821] Polishing. Related: #2739. --- .../jpa/mapping/JpaPersistentPropertyImpl.java | 6 +++--- .../jpa/infrastructure/HibernateTestUtils.java | 4 ++-- .../JpaPersistentPropertyImplUnitTests.java | 12 ++++++------ ...JpaRepositoriesRegistrarIntegrationTests.java | 6 +++--- .../JpaRepositoriesRegistrarUnitTests.java | 5 ++--- .../query/JpaQueryMethodUnitTests.java | 10 +++++++--- .../repository/query/NamedQueryUnitTests.java | 16 ++++++++++------ .../repository/query/QueryUtilsUnitTests.java | 7 ++++++- 8 files changed, 39 insertions(+), 27 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index 1e3f604a83..6f0773974f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -15,15 +15,15 @@ */ package org.springframework.data.jpa.mapping; +import jakarta.persistence.*; +import jakarta.persistence.metamodel.Metamodel; + import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; -import jakarta.persistence.*; -import jakarta.persistence.metamodel.Metamodel; - import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.annotation.AccessType.Type; import org.springframework.data.jpa.util.JpaMetamodel; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java index 0507c7ba6c..e217a0763f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/infrastructure/HibernateTestUtils.java @@ -15,11 +15,11 @@ */ package org.springframework.data.jpa.infrastructure; +import jakarta.persistence.spi.PersistenceProvider; + import java.util.Arrays; import java.util.List; -import jakarta.persistence.spi.PersistenceProvider; - import org.springframework.util.ClassUtils; /** diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index f8b23a493d..227b24c707 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -15,10 +15,9 @@ */ package org.springframework.data.jpa.mapping; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Collections; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import jakarta.persistence.Access; import jakarta.persistence.AccessType; @@ -31,6 +30,8 @@ import jakarta.persistence.metamodel.ManagedType; import jakarta.persistence.metamodel.Metamodel; +import java.util.Collections; + import org.jmolecules.ddd.types.AggregateRoot; import org.jmolecules.ddd.types.Association; import org.jmolecules.ddd.types.Identifier; @@ -146,8 +147,7 @@ void considersTargetEntityTypeForPropertyType() { Iterable> entityType = property.getPersistentEntityTypeInformation(); assertThat(entityType.iterator().hasNext()).isTrue(); - assertThat(entityType.iterator().next()) - .isEqualTo(TypeInformation.of(Implementation.class)); + assertThat(entityType.iterator().next()).isEqualTo(TypeInformation.of(Implementation.class)); } @Test // DATAJPA-716 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java index 8ae122e050..7a7555be73 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarIntegrationTests.java @@ -15,17 +15,17 @@ */ package org.springframework.data.jpa.repository.config; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.persistence.EntityManagerFactory; import java.util.Arrays; import java.util.List; -import jakarta.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.aop.Advisor; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java index 9ce887397f..f6556ba03f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoriesRegistrarUnitTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.config; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; @@ -60,8 +60,7 @@ void configuresRepositoriesCorrectly() { } @EnableJpaRepositories(basePackageClasses = UserRepository.class) - private - class Config { + private class Config { } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index cc5cf56964..3854b4998b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -15,9 +15,13 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; import jakarta.persistence.LockModeType; import jakarta.persistence.QueryHint; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java index 9bc82fe381..4d6f4ac4b9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java @@ -15,17 +15,22 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.lang.reflect.Method; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.TypedQuery; import jakarta.persistence.metamodel.Metamodel; +import java.lang.reflect.Method; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,7 +38,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; - import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.provider.QueryExtractor; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 62e0b070f7..2ee00f42a2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -17,7 +17,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.data.jpa.repository.query.QueryUtils.*; +import static org.springframework.data.jpa.repository.query.QueryUtils.applySorting; +import static org.springframework.data.jpa.repository.query.QueryUtils.createCountQueryFor; +import static org.springframework.data.jpa.repository.query.QueryUtils.detectAlias; +import static org.springframework.data.jpa.repository.query.QueryUtils.getOuterJoinAliases; +import static org.springframework.data.jpa.repository.query.QueryUtils.hasConstructorExpression; +import static org.springframework.data.jpa.repository.query.QueryUtils.removeSubqueries; import java.util.Collections; import java.util.Set; From 3930581cf43c9aeeeb8c86350c20bbc8f51ab842 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 30 Jan 2023 10:48:06 +0100 Subject: [PATCH 302/821] Upgrade to Maven Wrapper 3.8.7. See #2782 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 75534d80c8..9eb9e9f8c7 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Fri Jun 03 09:32:46 CEST 2022 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip +#Mon Jan 30 10:48:06 CET 2023 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip From 6dc008fb050811164969d3f1354de14da27d5890 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 30 Jan 2023 10:49:48 +0100 Subject: [PATCH 303/821] Update CI properties. See #2707 --- ci/pipeline.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index cf37b39c99..5b54445231 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,16 +1,16 @@ # Java versions -java.main.tag=17.0.5_8-jdk-focal +java.main.tag=17.0.6_10-jdk-focal # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} # Supported versions of MongoDB -docker.mongodb.4.4.version=4.4.17 -docker.mongodb.5.0.version=5.0.13 -docker.mongodb.6.0.version=6.0.2 +docker.mongodb.4.4.version=4.4.18 +docker.mongodb.5.0.version=5.0.14 +docker.mongodb.6.0.version=6.0.4 # Supported versions of Redis -docker.redis.6.version=6.2.6 +docker.redis.6.version=6.2.10 # Supported versions of Cassandra docker.cassandra.3.version=3.11.14 From 64b5a22a24b42e8660f441897b001e4b594fff1c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 25 Jan 2023 12:08:19 +0100 Subject: [PATCH 304/821] Differentiate between JPQL and native queries in count query derivation. We now consider whether a query is a native one when deriving a count query for pagination. Previously, the generated queries used JPQL syntax that doesn't comply with native SQL syntax rules. Closes #2773 Original pull request #2777 --- .../query/DefaultQueryEnhancer.java | 2 +- .../query/JSqlParserQueryEnhancer.java | 23 +++-- .../data/jpa/repository/query/QueryUtils.java | 43 +++++++-- .../query/DefaultQueryEnhancerUnitTests.java | 30 +++++++ .../JSqlParserQueryEnhancerUnitTests.java | 30 +++++++ .../query/QueryEnhancerTckTests.java | 87 +++++++++++++++++++ .../query/QueryEnhancerUnitTests.java | 26 +++--- 7 files changed, 217 insertions(+), 24 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancerUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java index 53a07bf6f9..92387c973e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancer.java @@ -46,7 +46,7 @@ public String detectAlias() { @Override public String createCountQueryFor(@Nullable String countProjection) { - return QueryUtils.createCountQueryFor(this.query.getQueryString(), countProjection); + return QueryUtils.createCountQueryFor(this.query.getQueryString(), countProjection, this.query.isNativeQuery()); } @Override 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 7a44873ac4..cd76876ac7 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,9 +15,8 @@ */ package org.springframework.data.jpa.repository.query; -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 static org.springframework.data.jpa.repository.query.JSqlParserUtils.*; +import static org.springframework.data.jpa.repository.query.QueryUtils.*; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Alias; @@ -29,11 +28,23 @@ 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.*; +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.update.Update; import net.sf.jsqlparser.statement.values.ValuesStatement; -import java.util.*; +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; @@ -400,7 +411,7 @@ public String createCountQueryFor(@Nullable String countProjection) { return selectBody.toString(); } - String countProp = tableAlias == null ? "*" : tableAlias; + String countProp = query.isNativeQuery() ? (distinct ? "*" : "1") : tableAlias == null ? "*" : tableAlias; Function jSqlCount = getJSqlCount(Collections.singletonList(countProp), distinct); selectBody.setSelectItems(Collections.singletonList(new SelectExpressionItem(jSqlCount))); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 5707caec26..785dde580d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -18,10 +18,23 @@ import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.*; import static java.util.regex.Pattern.*; -import jakarta.persistence.*; -import jakarta.persistence.criteria.*; -import jakarta.persistence.metamodel.*; +import jakarta.persistence.EntityManager; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Parameter; +import jakarta.persistence.Query; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Fetch; +import jakarta.persistence.criteria.From; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.metamodel.Attribute; import jakarta.persistence.metamodel.Attribute.PersistentAttributeType; +import jakarta.persistence.metamodel.Bindable; +import jakarta.persistence.metamodel.ManagedType; +import jakarta.persistence.metamodel.PluralAttribute; +import jakarta.persistence.metamodel.SingularAttribute; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; @@ -570,6 +583,19 @@ public static String createCountQueryFor(String originalQuery) { */ @Deprecated public static String createCountQueryFor(String originalQuery, @Nullable String countProjection) { + return createCountQueryFor(originalQuery, countProjection, false); + } + + /** + * Creates a count projected query from the given original query. + * + * @param originalQuery must not be {@literal null}. + * @param countProjection may be {@literal null}. + * @param nativeQuery whether the underlying query is a native query. + * @return a query String to be used a count query for pagination. Guaranteed to be not {@literal null}. + * @since 2.7.8 + */ + static String createCountQueryFor(String originalQuery, @Nullable String countProjection, boolean nativeQuery) { Assert.hasText(originalQuery, "OriginalQuery must not be null or empty"); @@ -591,9 +617,14 @@ public static String createCountQueryFor(String originalQuery, @Nullable String String replacement = useVariable ? SIMPLE_COUNT_VALUE : complexCountValue; - String alias = QueryUtils.detectAlias(originalQuery); - if ("*".equals(variable) && alias != null) { - replacement = alias; + if (nativeQuery && (variable.contains(",") || "*".equals(variable))) { + replacement = "1"; + } else { + + String alias = QueryUtils.detectAlias(originalQuery); + if (("*".equals(variable) && alias != null)) { + replacement = alias; + } } countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, replacement)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancerUnitTests.java new file mode 100644 index 0000000000..f113586fc9 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancerUnitTests.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +/** + * TCK Tests for {@link DefaultQueryEnhancer}. + * + * @author Mark Paluch + */ +public class DefaultQueryEnhancerUnitTests extends QueryEnhancerTckTests { + + @Override + QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { + return new DefaultQueryEnhancer(declaredQuery); + } + +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java new file mode 100644 index 0000000000..940af91477 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +/** + * TCK Tests for {@link JSqlParserQueryEnhancer}. + * + * @author Mark Paluch + */ +public class JSqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { + + @Override + QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { + return new JSqlParserQueryEnhancer(declaredQuery); + } + +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java new file mode 100644 index 0000000000..cc8bb6155b --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * TCK Tests for {@link QueryEnhancer}. + * + * @author Mark Paluch + */ +abstract class QueryEnhancerTckTests { + + @ParameterizedTest + @MethodSource("nativeCountQueries") // GH-2773 + void shouldDeriveNativeCountQuery(String query, String expected) { + + DeclaredQuery declaredQuery = DeclaredQuery.of(query, true); + QueryEnhancer enhancer = createQueryEnhancer(declaredQuery); + String countQueryFor = enhancer.createCountQueryFor(null); + + assertThat(countQueryFor).isEqualToIgnoringCase(expected); + } + + static Stream nativeCountQueries() { + + return Stream.of(Arguments.of( // + "SELECT * FROM table_name some_alias", // + "select count(1) FROM table_name some_alias"), // + + Arguments.of( // + "SELECT name FROM table_name some_alias", // + "select count(name) FROM table_name some_alias"), // + + Arguments.of( // + "SELECT DISTINCT name FROM table_name some_alias", // + "select count(DISTINCT name) FROM table_name some_alias")); + } + + @ParameterizedTest // GH-2773 + @MethodSource("jpqlCountQueries") + void shouldDeriveJpqlCountQuery(String query, String expected) { + + DeclaredQuery declaredQuery = DeclaredQuery.of(query, false); + QueryEnhancer enhancer = createQueryEnhancer(declaredQuery); + String countQueryFor = enhancer.createCountQueryFor(null); + + assertThat(countQueryFor).isEqualToIgnoringCase(expected); + } + + static Stream jpqlCountQueries() { + + return Stream.of(Arguments.of( // + "SELECT some_alias FROM table_name some_alias", // + "select count(some_alias) FROM table_name some_alias"), // + + Arguments.of( // + "SELECT name FROM table_name some_alias", // + "select count(name) FROM table_name some_alias"), // + + Arguments.of( // + "SELECT DISTINCT name FROM table_name some_alias", // + "select count(DISTINCT name) FROM table_name some_alias")); + } + + abstract QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery); + +} 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 5c1137aea2..8cfe0e0a3f 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 @@ -15,9 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import java.util.Collections; @@ -229,7 +227,12 @@ void createsCountQueryForNestedReferenceCorrectly() { @Test // DATAJPA-420 void createsCountQueryForScalarSelects() { - assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p", true); + assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p", false); + } + + @Test // DATAJPA-420 + void createsCountQueryForNativeScalarSelects() { + assertCountQuery("select p.lastname,p.firstname from Person p", "select count(1) from Person p", true); } @Test // DATAJPA-456 @@ -490,7 +493,7 @@ void createCountQuerySupportsWhitespaceCharacters() { " order by user.name\n ", true); assertThat(getEnhancer(query).createCountQueryFor()) - .isEqualToIgnoringCase("select count(user) from User user where user.age = 18"); + .isEqualToIgnoringCase("select count(1) from User user where user.age = 18"); } @Test @@ -503,7 +506,7 @@ void createCountQuerySupportsLineBreaksInSelectClause() { " order\nby\nuser.name\n ", true); assertThat(getEnhancer(query).createCountQueryFor()) - .isEqualToIgnoringCase("select count(user) from User user where user.age = 18"); + .isEqualToIgnoringCase("select count(1) from User user where user.age = 18"); } @Test // DATAJPA-1061 @@ -724,17 +727,17 @@ void countQueryUsesCorrectVariable() { QueryEnhancer queryEnhancer = getEnhancer(nativeQuery); String countQueryFor = queryEnhancer.createCountQueryFor(); - assertThat(countQueryFor).isEqualTo("SELECT count(*) FROM User WHERE created_at > $1"); + assertThat(countQueryFor).isEqualTo("SELECT count(1) FROM User WHERE created_at > $1"); nativeQuery = new StringQuery("SELECT * FROM (select * from test) ", true); queryEnhancer = getEnhancer(nativeQuery); countQueryFor = queryEnhancer.createCountQueryFor(); - assertThat(countQueryFor).isEqualTo("SELECT count(*) FROM (SELECT * FROM test)"); + assertThat(countQueryFor).isEqualTo("SELECT count(1) FROM (SELECT * FROM test)"); nativeQuery = new StringQuery("SELECT * FROM (select * from test) as test", true); queryEnhancer = getEnhancer(nativeQuery); countQueryFor = queryEnhancer.createCountQueryFor(); - assertThat(countQueryFor).isEqualTo("SELECT count(test) FROM (SELECT * FROM test) AS test"); + assertThat(countQueryFor).isEqualTo("SELECT count(1) FROM (SELECT * FROM test) AS test"); } @Test // GH-2555 @@ -864,7 +867,7 @@ void withStatementsWorksWithJSQLParser() { assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase( "with sample_data (day, value) AS (VALUES ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16)))\n" - + "SELECT count(a) FROM sample_data AS a"); + + "SELECT count(1) FROM sample_data AS a"); assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).endsWith("ORDER BY a.day DESC"); assertThat(queryEnhancer.getJoinAliases()).isEmpty(); assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase("a"); @@ -887,7 +890,7 @@ void multipleWithStatementsWorksWithJSQLParser() { assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase( "with sample_data (day, value) AS (VALUES ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))),test2 AS (VALUES (1, 2, 3))\n" - + "SELECT count(a) FROM sample_data AS a"); + + "SELECT count(1) FROM sample_data AS a"); assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).endsWith("ORDER BY a.day DESC"); assertThat(queryEnhancer.getJoinAliases()).isEmpty(); assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase("a"); @@ -985,4 +988,5 @@ private static void assertCountQuery(StringQuery originalQuery, String countQuer private static QueryEnhancer getEnhancer(DeclaredQuery query) { return QueryEnhancerFactory.forQuery(query); } + } From 25ed44c0046f70df302efc8af3e58377ad634735 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 25 Jan 2023 15:41:40 +0100 Subject: [PATCH 305/821] Refactor QueryEnhancer Tests into TCK-style. De-duplicate code, use parametrized tests instead of test methods to verify individual fixtures. Ensure all variants are tested with JSQLparser and the default enhancer. See #2773 Original pull request #2777 --- .../query/DefaultQueryEnhancerUnitTests.java | 7 + .../JSqlParserQueryEnhancerUnitTests.java | 190 +++++++++++ .../query/QueryEnhancerTckTests.java | 127 ++++++- .../query/QueryEnhancerUnitTests.java | 316 ------------------ 4 files changed, 320 insertions(+), 320 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancerUnitTests.java index f113586fc9..8b38061bf1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryEnhancerUnitTests.java @@ -15,6 +15,9 @@ */ package org.springframework.data.jpa.repository.query; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + /** * TCK Tests for {@link DefaultQueryEnhancer}. * @@ -27,4 +30,8 @@ QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { return new DefaultQueryEnhancer(declaredQuery); } + @Override + @Test // GH-2511, GH-2773 + @Disabled("Not properly supported by QueryUtils") + void shouldDeriveNativeCountQueryWithVariable(String query, String expected) {} } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java index 940af91477..5d97802439 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java @@ -15,10 +15,23 @@ */ package org.springframework.data.jpa.repository.query; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.data.domain.Sort; + /** * TCK Tests for {@link JSqlParserQueryEnhancer}. * * @author Mark Paluch + * @author Diego Krupitza + * @author Geoffrey Deremetz */ public class JSqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { @@ -27,4 +40,181 @@ QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { return new JSqlParserQueryEnhancer(declaredQuery); } + @Override + @ParameterizedTest // GH-2773 + @MethodSource("jpqlCountQueries") + void shouldDeriveJpqlCountQuery(String query, String expected) { + + assumeThat(query).as("JSQLParser does not support simple JPQL syntax").doesNotStartWithIgnoringCase("FROM"); + + assumeThat(query).as("JSQLParser does not support constructor JPQL syntax").doesNotContain(" new "); + + super.shouldDeriveJpqlCountQuery(query, expected); + } + + @Test + // GH-2578 + void setOperationListWorks() { + + String setQuery = "select SOME_COLUMN from SOME_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // + + "except \n" // + + "select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNullOrEmpty(); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); + assertThat(queryEnhancer.applySorting(Sort.by("SOME_COLUMN"))).endsWith("ORDER BY SOME_COLUMN ASC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void complexSetOperationListWorks() { + + String setQuery = "select SOME_COLUMN from SOME_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // + + "except \n" // + + "select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // + + "union select SOME_COLUMN from SOME_OTHER_OTHER_TABLE"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNullOrEmpty(); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); + assertThat(queryEnhancer.applySorting(Sort.by("SOME_COLUMN").ascending())).endsWith("ORDER BY SOME_COLUMN ASC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void deeplyNestedcomplexSetOperationListWorks() { + + String setQuery = "SELECT CustomerID FROM (\n" // + + "\t\t\tselect * from Customers\n" // + + "\t\t\texcept\n"// + + "\t\t\tselect * from Customers where country = 'Austria'\n"// + + "\t)\n" // + + "\texcept\n"// + + "\tselect CustomerID from customers where country = 'Germany'\n"// + + "\t;"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNullOrEmpty(); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("CustomerID"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); + assertThat(queryEnhancer.applySorting(Sort.by("CustomerID").descending())).endsWith("ORDER BY CustomerID DESC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("CustomerID"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void valuesStatementsWorks() { + + String setQuery = "VALUES (1, 2, 'test')"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNullOrEmpty(); + assertThat(stringQuery.getProjection()).isNullOrEmpty(); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); + assertThat(queryEnhancer.applySorting(Sort.by("CustomerID").descending())).isEqualTo(setQuery); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); + assertThat(queryEnhancer.getProjection()).isNullOrEmpty(); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void withStatementsWorks() { + + String setQuery = "with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))) \n" + + "select day, value from sample_data as a"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isEqualToIgnoringCase("a"); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("day, value"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase( + "with sample_data (day, value) AS (VALUES ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16)))\n" + + "SELECT count(1) FROM sample_data AS a"); + assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).endsWith("ORDER BY a.day DESC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase("a"); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("day, value"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @Test // GH-2578 + void multipleWithStatementsWorks() { + + String setQuery = "with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))), test2 as (values (1,2,3)) \n" + + "select day, value from sample_data as a"; + + StringQuery stringQuery = new StringQuery(setQuery, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isEqualToIgnoringCase("a"); + assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("day, value"); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase( + "with sample_data (day, value) AS (VALUES ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))),test2 AS (VALUES (1, 2, 3))\n" + + "SELECT count(1) FROM sample_data AS a"); + assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).endsWith("ORDER BY a.day DESC"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase("a"); + assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("day, value"); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + @ParameterizedTest // GH-2641 + @MethodSource("mergeStatementWorksSource") + void mergeStatementWorksWithJSqlParser(String query, String alias) { + + StringQuery stringQuery = new StringQuery(query, true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(queryEnhancer.detectAlias()).isEqualTo(alias); + assertThat(QueryUtils.detectAlias(query)).isNull(); + + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isEqualTo(alias); + assertThat(queryEnhancer.getProjection()).isEmpty(); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + + static Stream mergeStatementWorksSource() { + + 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/query/QueryEnhancerTckTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java index cc8bb6155b..4bbeb008fb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java @@ -19,6 +19,7 @@ import java.util.stream.Stream; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -36,9 +37,13 @@ void shouldDeriveNativeCountQuery(String query, String expected) { DeclaredQuery declaredQuery = DeclaredQuery.of(query, true); QueryEnhancer enhancer = createQueryEnhancer(declaredQuery); - String countQueryFor = enhancer.createCountQueryFor(null); + String countQueryFor = enhancer.createCountQueryFor(); - assertThat(countQueryFor).isEqualToIgnoringCase(expected); + // lenient cleanup to allow for rendering variance + String sanitized = countQueryFor.replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" {2}", " ") + .replaceAll(" {2}", " ").trim(); + + assertThat(sanitized).isEqualToIgnoringCase(expected); } static Stream nativeCountQueries() { @@ -53,7 +58,58 @@ static Stream nativeCountQueries() { Arguments.of( // "SELECT DISTINCT name FROM table_name some_alias", // - "select count(DISTINCT name) FROM table_name some_alias")); + "select count(DISTINCT name) FROM table_name some_alias"), // + + Arguments.of( // + "select distinct u from User u where u.foo = ?", // + "select count(distinct u) from User u where u.foo = ?"), + + Arguments.of( // + "select u from User as u", // + "select count(u) from User as u"), + + Arguments.of( // + "SELECT u FROM User u where u.foo.bar = ?", // + "select count(u) FROM User u where u.foo.bar = ?"), + + Arguments.of( // + "select p.lastname,p.firstname from Person p", // + "select count(1) from Person p"), + + // whitespace quirks + Arguments.of( // + """ + select user.age, + user.name + from User user + where user.age = 18 + order + by + user.name + \s""", // + "select count(1) from User user where user.age = 18"), + + Arguments.of( // + "select * from User user\n" + // + " where user.age = 18\n" + // + " order by user.name\n ", // + "select count(1) from User user where user.age = 18"), + + Arguments.of( // + "SELECT DISTINCT entity1\nFROM Entity1 entity1\nLEFT JOIN Entity2 entity2 ON entity1.key = entity2.key", // + "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"), + + Arguments.of( // + "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key", // + "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"), + + Arguments.of( // + "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799", // + "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key where entity1.id = 1799"), + + Arguments.of( // + "select distinct m.genre from Media m where m.user = ?1 OrDer By m.genre ASC", // + "select count(distinct m.genre) from Media m where m.user = ?1")); } @ParameterizedTest // GH-2773 @@ -79,7 +135,70 @@ static Stream jpqlCountQueries() { Arguments.of( // "SELECT DISTINCT name FROM table_name some_alias", // - "select count(DISTINCT name) FROM table_name some_alias")); + "select count(DISTINCT name) FROM table_name some_alias"), + + Arguments.of( // + "select distinct new User(u.name) from User u where u.foo = ?", // + "select count(distinct u) from User u where u.foo = ?"), + + Arguments.of( // + "FROM User u WHERE u.foo.bar = ?", // + "select count(u) FROM User u WHERE u.foo.bar = ?"), + + Arguments.of( // + "from User u", // + "select count(u) FROM User u"), + + Arguments.of( // + "select u from User as u", // + "select count(u) from User as u"), + + Arguments.of( // + "select p.lastname,p.firstname from Person p", // + "select count(p) from Person p"), + + Arguments.of( // + "select a.b from A a", // + "select count(a.b) from A a"), + + Arguments.of( // + "select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", // + "select count(distinct m.genre) from Media m where m.user = ?1")); + } + + @ParameterizedTest // GH-2511, GH-2773 + @MethodSource("nativeQueriesWithVariables") + void shouldDeriveNativeCountQueryWithVariable(String query, String expected) { + + DeclaredQuery declaredQuery = DeclaredQuery.of(query, true); + QueryEnhancer enhancer = createQueryEnhancer(declaredQuery); + String countQueryFor = enhancer.createCountQueryFor(); + + assertThat(countQueryFor).isEqualToIgnoringCase(expected); + } + + static Stream nativeQueriesWithVariables() { + + return Stream.of(Arguments.of( // + "SELECT * FROM User WHERE created_at > $1", // + "SELECT count(1) FROM User WHERE created_at > $1"), // + + Arguments.of( // + "SELECT * FROM (select * from test) ", // + "SELECT count(1) FROM (SELECT * FROM test)"), // + + Arguments.of( // + "SELECT * FROM (select * from test) as test", // + "SELECT count(1) FROM (SELECT * FROM test) AS test")); + } + + @Test + // DATAJPA-1696 + void findProjectionClauseWithIncludedFrom() { + + StringQuery query = new StringQuery("select x, frommage, y from t", true); + + assertThat(createQueryEnhancer(query).getProjection()).isEqualTo("x, frommage, y"); } abstract QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery); 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 8cfe0e0a3f..549528160f 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 @@ -45,37 +45,8 @@ class QueryEnhancerUnitTests { private static final String FQ_QUERY = "select u from org.acme.domain.User$Foo_Bar u"; private static final String SIMPLE_QUERY = "from User u"; private static final String COUNT_QUERY = "select count(u) from User u"; - private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?"; - @Test - void createsCountQueryCorrectly() { - assertCountQuery(QUERY, COUNT_QUERY, true); - } - - @Test - void createsCountQueriesCorrectlyForCapitalLetterJPQL() { - - assertCountQuery("FROM User u WHERE u.foo.bar = ?", "select count(u) FROM User u WHERE u.foo.bar = ?", false); - - assertCountQuery("SELECT u FROM User u where u.foo.bar = ?", "select count(u) FROM User u where u.foo.bar = ?", - true); - } - - @Test - void createsCountQueryForDistinctQueries() { - - assertCountQuery("select distinct u from User u where u.foo = ?", - "select count(distinct u) from User u where u.foo = ?", true); - } - - @Test - void createsCountQueryForConstructorQueries() { - - assertCountQuery("select distinct new User(u.name) from User u where u.foo = ?", - "select count(distinct u) from User u where u.foo = ?", false); - } - @Test void createsCountQueryForJoinsNoneNative() { @@ -97,11 +68,6 @@ void createsCountQueryForQueriesWithSubSelects() { "select count(u) from User u left outer join u.roles r where r in (select r from Role)", true); } - @Test - void createsCountQueryForAliasesCorrectly() { - assertCountQuery("select u from User as u", "select count(u) from User as u", true); - } - @Test void allowsShortJpaSyntax() { assertCountQuery(SIMPLE_QUERY, COUNT_QUERY, false); @@ -177,13 +143,6 @@ void appendsIgnoreCaseOrderingCorrectly() { .endsWithIgnoringCase("order by p.lastname asc, lower(p.firstname) asc"); } - @Test // DATAJPA-342 - void usesReturnedVariableInCountProjectionIfSet() { - - assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", - "select count(distinct m.genre) from Media m where m.user = ?1", true); - } - @Test // DATAJPA-343 void projectsCountQueriesForQueriesWithSubSelects() { @@ -203,13 +162,6 @@ void doesNotPrefixSortsIfFunction() { .isInstanceOf(InvalidDataAccessApiUsageException.class); } - @Test // DATAJPA-377 - void removesOrderByInGeneratedCountQueryFromOriginalQueryIfPresent() { - - assertCountQuery("select distinct m.genre from Media m where m.user = ?1 OrDer By m.genre ASC", - "select count(distinct m.genre) from Media m where m.user = ?1", true); - } - @Test // DATAJPA-375 void findsExistingOrderByIndependentOfCase() { @@ -220,21 +172,6 @@ void findsExistingOrderByIndependentOfCase() { assertThat(query).endsWithIgnoringCase("ORDER BY p.firstname, p.lastname asc"); } - @Test // DATAJPA-409 - void createsCountQueryForNestedReferenceCorrectly() { - assertCountQuery("select a.b from A a", "select count(a.b) from A a", true); - } - - @Test // DATAJPA-420 - void createsCountQueryForScalarSelects() { - assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p", false); - } - - @Test // DATAJPA-420 - void createsCountQueryForNativeScalarSelects() { - assertCountQuery("select p.lastname,p.firstname from Person p", "select count(1) from Person p", true); - } - @Test // DATAJPA-456 void createCountQueryFromTheGivenCountProjection() { @@ -485,30 +422,6 @@ void detectsAliasWithGroupAndOrderBy() { assertThat(getEnhancer(queryWithOrderAlias).detectAlias()).isEqualTo("u"); } - @Test // DATAJPA-1500 - void createCountQuerySupportsWhitespaceCharacters() { - - StringQuery query = new StringQuery("select * from User user\n" + // - " where user.age = 18\n" + // - " order by user.name\n ", true); - - assertThat(getEnhancer(query).createCountQueryFor()) - .isEqualToIgnoringCase("select count(1) from User user where user.age = 18"); - } - - @Test - void createCountQuerySupportsLineBreaksInSelectClause() { - - StringQuery query = new StringQuery("select user.age,\n" + // - " user.name\n" + // - " from User user\n" + // - " where user.age = 18\n" + // - " order\nby\nuser.name\n ", true); - - assertThat(getEnhancer(query).createCountQueryFor()) - .isEqualToIgnoringCase("select count(1) from User user where user.age = 18"); - } - @Test // DATAJPA-1061 void appliesSortCorrectlyForFieldAliases() { @@ -630,52 +543,6 @@ void findProjectionClauseWithSubselectNative() { assertThat(getEnhancer(query).getProjection()).isEqualTo("*"); } - @Test // DATAJPA-1696 - void findProjectionClauseWithIncludedFrom() { - - StringQuery query = new StringQuery("select x, frommage, y from t", true); - - assertThat(getEnhancer(query).getProjection()).isEqualTo("x, frommage, y"); - } - - @Test - void countProjectionDistinctQueryIncludesNewLineAfterFromAndBeforeJoin() { - - StringQuery originalQuery = new StringQuery( - "SELECT DISTINCT entity1\nFROM Entity1 entity1\nLEFT JOIN Entity2 entity2 ON entity1.key = entity2.key", true); - - assertCountQuery(originalQuery, - "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"); - } - - @Test - void countProjectionDistinctQueryIncludesNewLineAfterEntity() { - - StringQuery originalQuery = new StringQuery( - "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key", true); - - assertCountQuery(originalQuery, - "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key"); - } - - @Test - void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { - - StringQuery originalQuery = new StringQuery( - "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799", - true); - - assertCountQuery(originalQuery, - "select count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN Entity2 entity2 ON entity1.key = entity2.key where entity1.id = 1799"); - } - - @Test - void createsCountQueriesCorrectlyForCapitalLetter() { - - assertCountQuery("SELECT u FROM User u where u.foo.bar = ?", "select count(u) FROM User u where u.foo.bar = ?", - true); - } - @ParameterizedTest // DATAJPA-252 @MethodSource("detectsJoinAliasesCorrectlySource") void detectsJoinAliasesCorrectly(String queryString, List aliases) { @@ -689,7 +556,6 @@ void detectsJoinAliasesCorrectly(String queryString, List aliases) { assertThat(nonNativeJoinAliases).containsAll(nativeJoinAliases); assertThat(nativeJoinAliases).hasSameSizeAs(aliases) // .containsAll(aliases); - } @Test // GH-2441 @@ -720,26 +586,6 @@ void correctApplySortOnComplexNestedFunctionQuery() { assertThat(result).containsIgnoringCase("order by dd.institutesIds"); } - @Test // GH-2511 - void countQueryUsesCorrectVariable() { - - StringQuery nativeQuery = new StringQuery("SELECT * FROM User WHERE created_at > $1", true); - - QueryEnhancer queryEnhancer = getEnhancer(nativeQuery); - String countQueryFor = queryEnhancer.createCountQueryFor(); - assertThat(countQueryFor).isEqualTo("SELECT count(1) FROM User WHERE created_at > $1"); - - nativeQuery = new StringQuery("SELECT * FROM (select * from test) ", true); - queryEnhancer = getEnhancer(nativeQuery); - countQueryFor = queryEnhancer.createCountQueryFor(); - assertThat(countQueryFor).isEqualTo("SELECT count(1) FROM (SELECT * FROM test)"); - - nativeQuery = new StringQuery("SELECT * FROM (select * from test) as test", true); - queryEnhancer = getEnhancer(nativeQuery); - countQueryFor = queryEnhancer.createCountQueryFor(); - assertThat(countQueryFor).isEqualTo("SELECT count(1) FROM (SELECT * FROM test) AS test"); - } - @Test // GH-2555 void modifyingQueriesAreDetectedCorrectly() { @@ -760,143 +606,6 @@ void modifyingQueriesAreDetectedCorrectly() { assertThat(QueryEnhancerFactory.forQuery(modiQuery).createCountQueryFor()).isEqualToIgnoringCase(modifyingQuery); } - @Test // GH-2578 - void setOperationListWorksWithJSQLParser() { - - String setQuery = "select SOME_COLUMN from SOME_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // - + "except \n" // - + "select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE"; - - StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); - - assertThat(stringQuery.getAlias()).isNullOrEmpty(); - assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); - assertThat(stringQuery.hasConstructorExpression()).isFalse(); - - assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); - assertThat(queryEnhancer.applySorting(Sort.by("SOME_COLUMN"))).endsWith("ORDER BY SOME_COLUMN ASC"); - assertThat(queryEnhancer.getJoinAliases()).isEmpty(); - assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); - assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); - assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); - } - - @Test // GH-2578 - void complexSetOperationListWorksWithJSQLParser() { - - String setQuery = "select SOME_COLUMN from SOME_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // - + "except \n" // - + "select SOME_COLUMN from SOME_OTHER_TABLE where REPORTING_DATE = :REPORTING_DATE \n" // - + "union select SOME_COLUMN from SOME_OTHER_OTHER_TABLE"; - - StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); - - assertThat(stringQuery.getAlias()).isNullOrEmpty(); - assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); - assertThat(stringQuery.hasConstructorExpression()).isFalse(); - - assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); - assertThat(queryEnhancer.applySorting(Sort.by("SOME_COLUMN").ascending())).endsWith("ORDER BY SOME_COLUMN ASC"); - assertThat(queryEnhancer.getJoinAliases()).isEmpty(); - assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); - assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("SOME_COLUMN"); - assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); - } - - @Test // GH-2578 - void deeplyNestedcomplexSetOperationListWorksWithJSQLParser() { - - String setQuery = "SELECT CustomerID FROM (\n" // - + "\t\t\tselect * from Customers\n" // - + "\t\t\texcept\n"// - + "\t\t\tselect * from Customers where country = 'Austria'\n"// - + "\t)\n" // - + "\texcept\n"// - + "\tselect CustomerID from customers where country = 'Germany'\n"// - + "\t;"; - - StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); - - assertThat(stringQuery.getAlias()).isNullOrEmpty(); - assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("CustomerID"); - assertThat(stringQuery.hasConstructorExpression()).isFalse(); - - assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); - assertThat(queryEnhancer.applySorting(Sort.by("CustomerID").descending())).endsWith("ORDER BY CustomerID DESC"); - assertThat(queryEnhancer.getJoinAliases()).isEmpty(); - assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); - assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("CustomerID"); - assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); - } - - @Test // GH-2578 - void valuesStatementsWorksWithJSQLParser() { - - String setQuery = "VALUES (1, 2, 'test')"; - - StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); - - assertThat(stringQuery.getAlias()).isNullOrEmpty(); - assertThat(stringQuery.getProjection()).isNullOrEmpty(); - assertThat(stringQuery.hasConstructorExpression()).isFalse(); - - assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(setQuery); - assertThat(queryEnhancer.applySorting(Sort.by("CustomerID").descending())).isEqualTo(setQuery); - assertThat(queryEnhancer.getJoinAliases()).isEmpty(); - assertThat(queryEnhancer.detectAlias()).isNullOrEmpty(); - assertThat(queryEnhancer.getProjection()).isNullOrEmpty(); - assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); - } - - @Test // GH-2578 - void withStatementsWorksWithJSQLParser() { - - String setQuery = "with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))) \n" - + "select day, value from sample_data as a"; - - StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); - - assertThat(stringQuery.getAlias()).isEqualToIgnoringCase("a"); - assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("day, value"); - assertThat(stringQuery.hasConstructorExpression()).isFalse(); - - assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase( - "with sample_data (day, value) AS (VALUES ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16)))\n" - + "SELECT count(1) FROM sample_data AS a"); - assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).endsWith("ORDER BY a.day DESC"); - assertThat(queryEnhancer.getJoinAliases()).isEmpty(); - assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase("a"); - assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("day, value"); - assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); - } - - @Test // GH-2578 - void multipleWithStatementsWorksWithJSQLParser() { - - String setQuery = "with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))), test2 as (values (1,2,3)) \n" - + "select day, value from sample_data as a"; - - StringQuery stringQuery = new StringQuery(setQuery, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); - - assertThat(stringQuery.getAlias()).isEqualToIgnoringCase("a"); - assertThat(stringQuery.getProjection()).isEqualToIgnoringCase("day, value"); - assertThat(stringQuery.hasConstructorExpression()).isFalse(); - - assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase( - "with sample_data (day, value) AS (VALUES ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))),test2 AS (VALUES (1, 2, 3))\n" - + "SELECT count(1) FROM sample_data AS a"); - assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).endsWith("ORDER BY a.day DESC"); - assertThat(queryEnhancer.getJoinAliases()).isEmpty(); - assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase("a"); - assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase("day, value"); - assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); - } @ParameterizedTest // GH-2593 @MethodSource("insertStatementIsProcessedSameAsDefaultSource") @@ -929,21 +638,7 @@ void insertStatementIsProcessedSameAsDefault(String insertQuery) { assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); } - @ParameterizedTest // GH-2641 - @MethodSource("mergeStatementWorksWithJSqlParserSource") - void mergeStatementWorksWithJSqlParser(String query, String alias) { - StringQuery stringQuery = new StringQuery(query, true); - QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); - - assertThat(queryEnhancer.detectAlias()).isEqualTo(alias); - assertThat(QueryUtils.detectAlias(query)).isNull(); - - assertThat(queryEnhancer.getJoinAliases()).isEmpty(); - assertThat(queryEnhancer.detectAlias()).isEqualTo(alias); - assertThat(queryEnhancer.getProjection()).isEmpty(); - assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); - } public static Stream insertStatementIsProcessedSameAsDefaultSource() { @@ -953,17 +648,6 @@ public static Stream insertStatementIsProcessedSameAsDefaultSource() ); } - 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"), - Arguments.of( - "merge into a using (select id2, value from b) on (id = id2) when matched then update set a.value = value", - null)); - } - public static Stream detectsJoinAliasesCorrectlySource() { return Stream.of( // From 7d632ef06a8b7c91cb46673f7d879adfdd7617a1 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 14 Feb 2023 10:28:35 +0100 Subject: [PATCH 306/821] Adopt to upgraded dependencies. Closes #2798 --- .../convert/QueryByExamplePredicateBuilderUnitTests.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java index 51b3563190..61c7d22549 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilderUnitTests.java @@ -172,6 +172,8 @@ void multiPredicateCriteriaShouldReturnCombinedOnes() { p.firstname = "foo"; p.age = 2L; + when(cb.and(any(Predicate[].class))).thenReturn(andPredicate); + assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(p), EscapeCharacter.DEFAULT)) .isEqualTo(andPredicate); @@ -188,10 +190,12 @@ void orConcatenatesPredicatesIfMatcherSpecifies() { Example example = of(person, ExampleMatcher.matchingAny()); + when(cb.or(any(Predicate[].class))).thenReturn(orPredicate); + assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, example, EscapeCharacter.DEFAULT)) .isEqualTo(orPredicate); - verify(cb, times(1)).or(ArgumentMatchers.any()); + verify(cb).or(ArgumentMatchers.any(Predicate[].class)); } @Test // DATAJPA-1372 From 945d39a4e085d4db577055e6319f5ec16e06ca8b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 15 Feb 2023 10:53:41 +0100 Subject: [PATCH 307/821] Fix broken links in reference docs. Closes #2801 --- src/main/asciidoc/glossary.adoc | 2 +- src/main/asciidoc/jpa.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/asciidoc/glossary.adoc b/src/main/asciidoc/glossary.adoc index 7f7c354b2a..b0810fe1cf 100644 --- a/src/main/asciidoc/glossary.adoc +++ b/src/main/asciidoc/glossary.adoc @@ -18,4 +18,4 @@ Hibernate :: Object relational mapper implementing JPA - link:$$https://hibernat JPA :: Jakarta Persistence API -Spring :: Java application framework - link:$$https://spring.io/projects/spring-framework$$[https://spring.io/projects/spring-framework] +Spring :: Java application framework - link:$$https://spring.io/projects/spring-framework/$$[https://spring.io/projects/spring-framework] diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index e7a79601cd..07565ea689 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -1383,7 +1383,7 @@ A plain JPA setup requires all annotation-mapped entity classes to be listed in ---- ==== -NOTE: As of Spring 3.1, a package to scan can be configured on the `LocalContainerEntityManagerFactoryBean` directly to enable classpath scanning for entity classes. See the link:$$https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setPackagesToScan(java.lang.String...)$$[JavaDoc] for details. +NOTE: As of Spring 3.1, a package to scan can be configured on the `LocalContainerEntityManagerFactoryBean` directly to enable classpath scanning for entity classes. See the link:{springJavadocUrl}org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setPackagesToScan(java.lang.String...)$$[JavaDoc] for details. [[jpd.misc.cdi-integration]] == CDI Integration From fdbfa243a1e403e4ed1b787945fcb67b0603666e Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Feb 2023 13:27:08 +0100 Subject: [PATCH 308/821] Prepare 3.1 M1 (2023.0.0). See #2707 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c94ae35cd5..1b88cc8c6d 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-SNAPSHOT + 3.1.0-M1 @@ -36,7 +36,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-SNAPSHOT + 3.1.0-M1 0.10.3 org.hibernate @@ -225,8 +225,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From 2cab87e75e9a84cb4b19493489830b29576d71c3 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Feb 2023 13:27:49 +0100 Subject: [PATCH 309/821] Release version 3.1 M1 (2023.0.0). See #2707 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 1b88cc8c6d..5d88c0dfc2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M1 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index db915d7c3b..7595ae88d7 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-SNAPSHOT + 3.1.0-M1 org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M1 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a5cb2f09b5..1ba491cf44 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M1 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 95a83aac44..fdf2fa8ab1 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-SNAPSHOT + 3.1.0-M1 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M1 ../pom.xml From a3022c56cbb1b1abd44053f94f247dd149ed5147 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Feb 2023 13:31:52 +0100 Subject: [PATCH 310/821] Prepare next development iteration. See #2707 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 5d88c0dfc2..1b88cc8c6d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M1 + 3.1.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 7595ae88d7..db915d7c3b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-M1 + 3.1.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.1.0-M1 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 1ba491cf44..a5cb2f09b5 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M1 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index fdf2fa8ab1..95a83aac44 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-M1 + 3.1.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M1 + 3.1.0-SNAPSHOT ../pom.xml From 0091d97e269fa78a67e31f37c124b8914211e4d0 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Feb 2023 13:31:54 +0100 Subject: [PATCH 311/821] After release cleanups. See #2707 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 1b88cc8c6d..c94ae35cd5 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-M1 + 3.1.0-SNAPSHOT @@ -36,7 +36,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-M1 + 3.1.0-SNAPSHOT 0.10.3 org.hibernate @@ -225,8 +225,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 5462802ba6497eef03fa0f3bb8c878a93a4c2be9 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Feb 2023 14:22:16 +0100 Subject: [PATCH 312/821] Prepare 3.1 M2 (2023.0.0). See #2806 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c94ae35cd5..226ec77500 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-SNAPSHOT + 3.1.0-M2 @@ -36,7 +36,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-SNAPSHOT + 3.1.0-M2 0.10.3 org.hibernate @@ -225,8 +225,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From 0b51bdd1cdc6968eb45c8fd88978cbbac46e23d5 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Feb 2023 14:22:30 +0100 Subject: [PATCH 313/821] Release version 3.1 M2 (2023.0.0). See #2806 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 226ec77500..93c1dae5b3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M2 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index db915d7c3b..ccca8d204f 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-SNAPSHOT + 3.1.0-M2 org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M2 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a5cb2f09b5..2aafd71e4e 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M2 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 95a83aac44..e82cdcebe9 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-SNAPSHOT + 3.1.0-M2 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M2 ../pom.xml From af16b08bfdbf381f242821ef5103529c07eb5463 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Feb 2023 14:25:46 +0100 Subject: [PATCH 314/821] Prepare next development iteration. See #2806 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 93c1dae5b3..226ec77500 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M2 + 3.1.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index ccca8d204f..db915d7c3b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-M2 + 3.1.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.1.0-M2 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 2aafd71e4e..a5cb2f09b5 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M2 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index e82cdcebe9..95a83aac44 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-M2 + 3.1.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M2 + 3.1.0-SNAPSHOT ../pom.xml From cd1ab79ecf22b96497e175cee02bb54849bac283 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 17 Feb 2023 14:25:48 +0100 Subject: [PATCH 315/821] After release cleanups. See #2806 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 226ec77500..c94ae35cd5 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-M2 + 3.1.0-SNAPSHOT @@ -36,7 +36,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-M2 + 3.1.0-SNAPSHOT 0.10.3 org.hibernate @@ -225,8 +225,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 45b24ad9163982fa4a164225e5e6346d904f9a57 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 20 Feb 2023 11:58:03 +0100 Subject: [PATCH 316/821] Upgrade to Maven Wrapper 3.9.0. See #2809 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 9eb9e9f8c7..bae44aada3 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Mon Jan 30 10:48:06 CET 2023 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip +#Mon Feb 20 11:58:03 CET 2023 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.0/apache-maven-3.9.0-bin.zip From 5f244c4289e29923c6c312469f0c6e538670b624 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 23 Feb 2023 15:55:31 +0100 Subject: [PATCH 317/821] Delay count query derivation. We now delay the count query creation to the actual time when we need the count query to avoid query creation of invalid queries (e.g. count queries for DELETE or UPDATE statements). See #2812 --- .../query/AbstractStringBasedJpaQuery.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java index e3a2ff1ce8..cf22cc9f40 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java @@ -18,15 +18,13 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.QueryRewriter; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; +import org.springframework.data.util.Lazy; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -46,7 +44,7 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { private final DeclaredQuery query; - private final DeclaredQuery countQuery; + private final Lazy countQuery; private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final SpelExpressionParser parser; private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache(); @@ -65,8 +63,8 @@ abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery { * @param queryRewriter must not be {@literal null}. */ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, String queryString, - @Nullable String countQueryString, QueryRewriter queryRewriter, QueryMethodEvaluationContextProvider evaluationContextProvider, - SpelExpressionParser parser) { + @Nullable String countQueryString, QueryRewriter queryRewriter, + QueryMethodEvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { super(method, em); @@ -79,9 +77,10 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Stri this.query = new ExpressionBasedStringQuery(queryString, method.getEntityInformation(), parser, method.isNativeQuery()); - DeclaredQuery countQuery = query.deriveCountQuery(countQueryString, method.getCountQueryProjection()); - this.countQuery = ExpressionBasedStringQuery.from(countQuery, method.getEntityInformation(), parser, - method.isNativeQuery()); + this.countQuery = Lazy.of(() -> { + DeclaredQuery countQuery = query.deriveCountQuery(countQueryString, method.getCountQueryProjection()); + return ExpressionBasedStringQuery.from(countQuery, method.getEntityInformation(), parser, method.isNativeQuery()); + }); this.parser = parser; this.queryRewriter = queryRewriter; @@ -117,7 +116,7 @@ protected ParameterBinder createBinder() { @Override protected Query doCreateCountQuery(JpaParametersParameterAccessor accessor) { - String queryString = countQuery.getQueryString(); + String queryString = countQuery.get().getQueryString(); EntityManager em = getEntityManager(); Query query = getQueryMethod().isNativeQuery() // @@ -142,7 +141,7 @@ public DeclaredQuery getQuery() { * @return the countQuery */ public DeclaredQuery getCountQuery() { - return countQuery; + return countQuery.get(); } /** From c63d0bfd8210051aeffe97222ed159b312e93e74 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 23 Feb 2023 15:56:02 +0100 Subject: [PATCH 318/821] Fix NullPointer when deriving a count query for non-SELECT statements. Closes #2812 --- .../data/jpa/repository/query/DeclaredQuery.java | 2 +- .../data/jpa/repository/query/QueryUtils.java | 2 +- .../jpa/repository/query/DefaultQueryUtilsUnitTests.java | 9 +++++++++ .../jpa/repository/query/QueryEnhancerUnitTests.java | 9 +++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java index 500e64d1ac..4821865def 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java @@ -105,7 +105,7 @@ default boolean usesPaging() { /** * Return whether the query is a native query of not. - * + * * @return true if native query otherwise false */ default boolean isNativeQuery() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 785dde580d..f581f191ff 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -617,7 +617,7 @@ static String createCountQueryFor(String originalQuery, @Nullable String countPr String replacement = useVariable ? SIMPLE_COUNT_VALUE : complexCountValue; - if (nativeQuery && (variable.contains(",") || "*".equals(variable))) { + if (variable != null && (nativeQuery && (variable.contains(",") || "*".equals(variable)))) { replacement = "1"; } else { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java index 89dba5b884..2409a9b2d2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultQueryUtilsUnitTests.java @@ -70,6 +70,15 @@ void createsCountQueryForDistinctQueries() { "select count(distinct u) from User u where u.foo = ?"); } + @Test // GH-2812 + void createsCountQueryForDeleteQuery() { + + String result = createCountQueryFor("delete from some_table where id in :ids", null, true); + + // ح(•̀ж•́)ง † + assertThat(result).isEqualTo("deleteselect count(where) from some_table where id in :ids"); + } + @Test void createsCountQueryForConstructorQueries() { 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 549528160f..cdc9919fab 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 @@ -172,6 +172,15 @@ void findsExistingOrderByIndependentOfCase() { assertThat(query).endsWithIgnoringCase("ORDER BY p.firstname, p.lastname asc"); } + @Test // GH-2812 + void createCountQueryFromDeleteQuery() { + + StringQuery query = new StringQuery("delete from some_table where id in :ids", true); + + assertThat(getEnhancer(query).createCountQueryFor("p.lastname")) + .isEqualToIgnoringCase("delete from some_table where id in :ids"); + } + @Test // DATAJPA-456 void createCountQueryFromTheGivenCountProjection() { From aab0efa5df549a392da30a0bf5f290987ca6fa77 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 27 Feb 2023 12:33:39 -0600 Subject: [PATCH 319/821] Use Jakarta EE 9 fetchgraph hint. Migrate from the old Java EE fetchgraph hint (javax.persistence.fetchgraph) to the new Jakarta EE fetchgraph hint (jakarta.persistence.fetchgraph) hint. Resolves #2825 Original pull request #2828 --- .../data/jpa/repository/support/EntityGraphFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java index 626e42444d..92fcf0c5cd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityGraphFactory.java @@ -30,11 +30,12 @@ * * @author Jens Schauder * @author Petr Strnad + * @author Greg Turnquist * @since 2.6 */ abstract class EntityGraphFactory { - public static final String HINT = "javax.persistence.fetchgraph"; + public static final String HINT = "jakarta.persistence.fetchgraph"; /** * Create an {@link EntityGraph} from a collection of properties. From 752fe463ed0cd209e8792a3124aa5d3c1bb2877a Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 27 Feb 2023 11:33:52 -0600 Subject: [PATCH 320/821] Apply hints for pagined projections on fetchable Querydsl queries. When using Querydsl predicates to build a fluent query, be sure to apply the projection hint that generates a fetch graph. Resolves #2820. Original pull request: #2827. --- .../FetchableFluentQueryByPredicate.java | 16 +++--- .../jpa/repository/UserRepositoryTests.java | 51 +++++++++++++++++-- .../jpa/repository/sample/UserRepository.java | 24 +++++++-- ...eanEntityPathResolverIntegrationTests.java | 11 +--- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index 61268e6087..b70b3b4d42 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository.support; +import jakarta.persistence.EntityManager; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -23,8 +25,6 @@ import java.util.function.Function; import java.util.stream.Stream; -import jakarta.persistence.EntityManager; - import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -69,8 +69,7 @@ public FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, private FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, Class resultType, Sort sort, Collection properties, Function> finder, BiFunction> pagedFinder, Function countOperation, - Function existsOperation, - EntityManager entityManager) { + Function existsOperation, EntityManager entityManager) { super(resultType, sort, properties, entityType); this.predicate = predicate; @@ -175,8 +174,13 @@ public boolean exists() { private Page readPage(Pageable pageable) { - AbstractJPAQuery pagedQuery = pagedFinder.apply(sort, pageable); - List paginatedResults = convert(pagedQuery.fetch()); + AbstractJPAQuery query = pagedFinder.apply(sort, pageable); + + if (!properties.isEmpty()) { + query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties)); + } + + List paginatedResults = convert(query.fetch()); return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(predicate)); } 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 f555d1baa6..b450692a7d 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 @@ -15,18 +15,17 @@ */ package org.springframework.data.jpa.repository; -import static java.util.Arrays.asList; +import static java.util.Arrays.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.springframework.data.domain.Example.of; +import static org.springframework.data.domain.Example.*; import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; import static org.springframework.data.domain.ExampleMatcher.StringMatcher; import static org.springframework.data.domain.ExampleMatcher.matching; -import static org.springframework.data.domain.Sort.Direction.ASC; -import static org.springframework.data.domain.Sort.Direction.DESC; +import static org.springframework.data.domain.Sort.Direction.*; +import static org.springframework.data.jpa.domain.Specification.*; import static org.springframework.data.jpa.domain.Specification.not; -import static org.springframework.data.jpa.domain.Specification.where; import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasAgeLess; import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstname; import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstnameLike; @@ -75,6 +74,7 @@ import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.sample.Address; +import org.springframework.data.jpa.domain.sample.QUser; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; @@ -2444,6 +2444,47 @@ void findByFluentSpecificationWithSimplePropertyPathsDoesntLoadUnrequestedPaths( ); } + @Test // GH-2820 + void findByFluentPredicateWithProjectionAndPageRequest() { + + flushTestUsers(); + em.clear(); + + Page users = repository.findBy(QUser.user.firstname.contains("v"), q -> q // + .project("firstname") // + .page(PageRequest.of(0, 10))); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + } + + @Test // GH-2820 + void findByFluentPredicateWithProjectionAndAll() { + + flushTestUsers(); + em.clear(); + + List users = repository.findBy(QUser.user.firstname.contains("v"), q -> q // + .project("firstname") // + .all()); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + } + + @Test // GH-2820 + void findByFluentPredicateWithPageRequest() { + + flushTestUsers(); + em.clear(); + + Page users = repository.findBy(QUser.user.firstname.contains("v"), q -> q // + .page(PageRequest.of(0, 10))); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + } + @Test // GH-2274 void findByFluentSpecificationWithCollectionPropertyPathsDoesntLoadUnrequestedPaths() { 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 6ff229d87c..ed5fcdbac5 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 @@ -18,15 +18,29 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.QueryHint; -import java.util.*; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Stream; -import org.springframework.data.domain.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; -import org.springframework.data.jpa.repository.*; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryHints; import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.querydsl.ListQuerydslPredicateExecutor; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; @@ -45,8 +59,8 @@ * @author Diego Krupitza * @author Geoffrey Deremetz */ -public interface UserRepository - extends JpaRepository, JpaSpecificationExecutor, UserRepositoryCustom { +public interface UserRepository extends JpaRepository, JpaSpecificationExecutor, + UserRepositoryCustom, ListQuerydslPredicateExecutor { /** * Retrieve users by their lastname. The finder {@literal User.findByLastname} is declared in diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java index 345a71e15d..76e83da05a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java @@ -32,8 +32,6 @@ import org.springframework.data.querydsl.SimpleEntityPathResolver; import org.springframework.test.util.ReflectionTestUtils; -import com.querydsl.core.types.EntityPath; - /** * Unit tests for {@link EntityPathResolver} related tests on {@link JpaRepositoryFactoryBean}. * @@ -47,14 +45,7 @@ class JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests { @EnableJpaRepositories(basePackageClasses = UserRepository.class, // includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserRepository.class)) static class BaseConfig { - - static final EntityPathResolver RESOLVER = new EntityPathResolver() { - - @Override - public EntityPath createPath(Class domainClass) { - return null; - } - }; + static final EntityPathResolver RESOLVER = SimpleEntityPathResolver.INSTANCE; } @Configuration From 0d8c06d661cd65d1e418fa7d22d34a0973e44243 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 16 Feb 2023 10:40:37 -0600 Subject: [PATCH 321/821] Create JPQL and HQL parsers. Introduce grammars that support both JPQL (JPA 3.1) as well as HQL (Hibernate 6.1) and allow us to leverage it for query handling. Related: #2814. --- pom.xml | 1 + spring-data-jpa/pom.xml | 49 +- .../data/jpa/repository/query/Hql.g4 | 1045 ++++++++ .../data/jpa/repository/query/Jpql.g4 | 850 ++++++ .../jpa/repository/query/HqlQueryParser.java | 136 + .../repository/query/HqlQueryRenderer.java | 2324 ++++++++++++++++ .../repository/query/HqlQueryTransformer.java | 338 +++ .../jpa/repository/query/JpaQueryParser.java | 213 ++ .../query/JpaQueryParsingEnhancer.java | 143 + .../query/JpaQueryParsingSyntaxError.java | 32 + .../JpaQueryParsingSyntaxErrorListener.java | 35 + .../query/JpaQueryParsingToken.java | 182 ++ .../jpa/repository/query/JpqlQueryParser.java | 136 + .../repository/query/JpqlQueryRenderer.java | 2379 +++++++++++++++++ .../query/JpqlQueryTransformer.java | 229 ++ .../query/QueryEnhancerFactory.java | 58 +- .../jpa/repository/query/StringQuery.java | 5 +- .../repository/UserRepositoryFinderTests.java | 10 +- .../jpa/repository/UserRepositoryTests.java | 27 +- .../ExpressionBasedStringQueryUnitTests.java | 38 +- .../HqlParserQueryEnhancerUnitTests.java | 65 + .../query/HqlQueryRendererTests.java | 1433 ++++++++++ .../query/HqlQueryTransformerTests.java | 815 ++++++ .../query/HqlSpecificationTests.java | 1401 ++++++++++ .../JpaQueryLookupStrategyUnitTests.java | 18 +- .../JpqlParserQueryEnhancerUnitTests.java | 67 + .../query/JpqlQueryRendererTests.java | 917 +++++++ .../query/JpqlQueryTransformerTests.java | 704 +++++ .../query/JpqlSpecificationTests.java | 888 ++++++ .../ParameterBindingParserUnitTests.java | 2 + .../query/QueryEnhancerFactoryUnitTests.java | 11 +- .../query/QueryEnhancerTckTests.java | 13 +- .../query/QueryEnhancerUnitTests.java | 25 +- .../QueryParameterSetterFactoryUnitTests.java | 8 +- .../query/SimpleJpaQueryUnitTests.java | 15 +- .../query/StringQueryUnitTests.java | 51 +- .../sample/MappedTypeRepository.java | 2 +- .../jpa/repository/sample/UserRepository.java | 40 +- 38 files changed, 14576 insertions(+), 129 deletions(-) create mode 100644 spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 create mode 100644 spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryParser.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParser.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingEnhancer.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxError.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxErrorListener.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryParser.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java diff --git a/pom.xml b/pom.xml index c94ae35cd5..c24ec2964c 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,7 @@ 16 + 4.11.1 3.0.3 6.1.4.Final 2.7.1 diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 95a83aac44..27313e9e3c 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -73,6 +73,12 @@ + + org.antlr + antlr4-runtime + ${antlr} + + org.aspectj aspectjweaver @@ -247,8 +253,8 @@ org.jacoco @@ -344,6 +350,45 @@ + + org.antlr + antlr4-maven-plugin + ${antlr} + + + + antlr4 + + generate-sources + + true + + + + + + + com.google.code.maven-replacer-plugin + maven-replacer-plugin + 1.4.1 + + + process-sources + + replace + + + + + + target/generated-sources/antlr4/**/*.java + + + public class=class,public interface=interface + + + + maven-compiler-plugin diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 new file mode 100644 index 0000000000..253caafc11 --- /dev/null +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -0,0 +1,1045 @@ +/* + * Copyright 2011-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +grammar Hql; + +@header { +/** + * HQL per https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#query-language + * + * This is a mixture of Hibernate's BNF and missing bits of grammar. There are gaps and inconsistencies in the + * BNF itself, explained by other fragments of their spec. Additionally, alternate labels are used to provide easier + * management of complex rules in the generated Visitor. Finally, there are labels applied to rule elements (op=('+'|'-') + * to simplify the processing. + * + * @author Greg Turnquist + * @since 3.1 + */ +} + +/* + Parser rules + */ + +start + : ql_statement EOF + ; + +ql_statement + : selectStatement + | updateStatement + | deleteStatement + | insertStatement + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-select +selectStatement + : queryExpression + ; + +queryExpression + : orderedQuery (setOperator orderedQuery)* + ; + +orderedQuery + : (query | '(' queryExpression ')') queryOrder? + ; + +query + : selectClause fromClause? whereClause? (groupByClause havingClause?)? # SelectQuery + | fromClause whereClause? (groupByClause havingClause?)? selectClause? # FromQuery + ; + +queryOrder + : orderByClause limitClause? offsetClause? fetchClause? + ; + +fromClause + : FROM entityWithJoins (',' entityWithJoins)* + ; + +entityWithJoins + : fromRoot (joinSpecifier)* + ; + +joinSpecifier + : join + | crossJoin + | jpaCollectionJoin + ; + +fromRoot + : entityName variable? + | LATERAL? '(' subquery ')' variable? + ; + +join + : joinType JOIN FETCH? joinTarget joinRestriction? // Spec BNF says joinType isn't optional, but text says that it is. + ; + +joinTarget + : path variable? # JoinPath + | LATERAL? '(' subquery ')' variable? # JoinSubquery + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-update +updateStatement + : UPDATE VERSIONED? targetEntity setClause whereClause? + ; + +targetEntity + : entityName variable? + ; + +setClause + : SET assignment (',' assignment)* + ; + +assignment + : simplePath '=' expressionOrPredicate + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-delete +deleteStatement + : DELETE FROM? targetEntity whereClause? + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-insert +insertStatement + : INSERT INTO? targetEntity targetFields (queryExpression | valuesList) + ; + +// Already defined underneath updateStatement +//targetEntity +// : entityName variable? +// ; + +targetFields + : '(' simplePath (',' simplePath)* ')' + ; + +valuesList + : VALUES values (',' values)* + ; + +values + : '(' expression (',' expression)* ')' + ; + +projectedItem + : (expression | instantiation) alias? + ; + +instantiation + : NEW instantiationTarget '(' instantiationArguments ')' + ; + +alias + : AS? identifier // spec says IDENTIFIER but clearly does NOT mean a reserved word + ; + +groupedItem + : identifier + | INTEGER_LITERAL + | expression + ; + +sortedItem + : sortExpression sortDirection? nullsPrecedence? + ; + +sortExpression + : identifier + | INTEGER_LITERAL + | expression + ; + +sortDirection + : ASC + | DESC + ; + +nullsPrecedence + : NULLS (FIRST | LAST) + ; + +limitClause + : LIMIT parameterOrIntegerLiteral + ; + +offsetClause + : OFFSET parameterOrIntegerLiteral (ROW | ROWS)? + ; + +fetchClause + : FETCH (FIRST | NEXT) (parameterOrIntegerLiteral | parameterOrNumberLiteral '%') (ROW | ROWS) (ONLY | WITH TIES) + ; + +/******************* + Gaps in the spec. + *******************/ + +subquery + : queryExpression + ; + +selectClause + : SELECT DISTINCT? selectionList + ; + +selectionList + : selection (',' selection)* + ; + +selection + : selectExpression variable? + ; + +selectExpression + : instantiation + | mapEntrySelection + | jpaSelectObjectSyntax + | expressionOrPredicate + ; + +mapEntrySelection + : ENTRY '(' path ')' + ; + +/** + * Deprecated syntax dating back to EJB-QL prior to EJB 3, required by JPA, never documented in Hibernate + */ +jpaSelectObjectSyntax + : OBJECT '(' identifier ')' + ; + +whereClause + : WHERE predicate (',' predicate)* + ; + +joinType + : INNER? + | (LEFT | RIGHT | FULL)? OUTER? + | CROSS + ; + +crossJoin + : CROSS JOIN entityName variable? + ; + +joinRestriction + : (ON | WITH) predicate + ; + +// Deprecated syntax dating back to EJB-QL prior to EJB 3, required by JPA, never documented in Hibernate +jpaCollectionJoin + : ',' IN '(' path ')' variable? + ; + +groupByClause + : GROUP BY groupedItem (',' groupedItem)* + ; + +orderByClause + : ORDER BY projectedItem (',' projectedItem)* + ; + +havingClause + : HAVING predicate (',' predicate)* + ; + +setOperator + : UNION ALL? + | INTERSECT ALL? + | EXCEPT ALL? + ; + +// Literals +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-literals +literal + : NULL + | booleanLiteral + | stringLiteral + | numericLiteral + | dateTimeLiteral + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-boolean-literals +booleanLiteral + : TRUE + | FALSE + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-string-literals +stringLiteral + : STRINGLITERAL + | JAVASTRINGLITERAL + | CHARACTER + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-numeric-literals +numericLiteral + : INTEGER_LITERAL + | FLOAT_LITERAL + | HEXLITERAL + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-datetime-literals +dateTimeLiteral + : LOCAL_DATE + | LOCAL_TIME + | LOCAL_DATETIME + | CURRENT_DATE + | CURRENT_TIME + | CURRENT_TIMESTAMP + | OFFSET_DATETIME + | (LOCAL | CURRENT) DATE + | (LOCAL | CURRENT) TIME + | (LOCAL | CURRENT | OFFSET) DATETIME + | INSTANT + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-duration-literals +// TBD + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-binary-literals +// TBD + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-enum-literals +// TBD + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-java-constants +// TBD + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-entity-name-literals +// TBD + +// Expressions +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-expressions +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-concatenation +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-numeric-arithmetic +expression + : '(' expression ')' # GroupedExpression + | '(' expressionOrPredicate (',' expressionOrPredicate)+ ')' # TupleExpression + | '(' subquery ')' # SubqueryExpression + | primaryExpression # PlainPrimaryExpression + | op=('+' | '-') numericLiteral # SignedNumericLiteral + | op=('+' | '-') expression # SignedExpression + | expression op=('*' | '/') expression # MultiplicationExpression + | expression op=('+' | '-') expression # AdditionExpression + | expression '||' expression # HqlConcatenationExpression + ; + +primaryExpression + : caseList # CaseExpression + | literal # LiteralExpression + | parameter # ParameterExpression + | function # FunctionExpression + | generalPathFragment # GeneralPathExpression + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-Datetime-arithmetic +// TBD + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-path-expressions +identificationVariable + : identifier + | simplePath + ; + +path + : treatedPath pathContinutation? + | generalPathFragment + ; + +generalPathFragment + : simplePath indexedPathAccessFragment? + ; + +indexedPathAccessFragment + : '[' expression ']' ('.' generalPathFragment)? + ; + +simplePath + : identifier simplePathElement* + ; + +simplePathElement + : '.' identifier + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-case-expressions +caseList + : simpleCaseExpression + | searchedCaseExpression + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-simple-case-expressions +simpleCaseExpression + : CASE expressionOrPredicate caseWhenExpressionClause+ (ELSE expressionOrPredicate)? END + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-searched-case-expressions +searchedCaseExpression + : CASE caseWhenPredicateClause+ (ELSE expressionOrPredicate)? END + ; + +caseWhenExpressionClause + : WHEN expression THEN expressionOrPredicate + ; + +caseWhenPredicateClause + : WHEN predicate THEN expressionOrPredicate + ; + +// Functions +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-exp-functions +function + : functionName '(' (functionArguments | ASTERISK)? ')' pathContinutation? filterClause? withinGroup? overClause? # GenericFunction + | functionName '(' subquery ')' # FunctionWithSubquery + | castFunction # CastFunctionInvocation + | extractFunction # ExtractFunctionInvocation + | trimFunction # TrimFunctionInvocation + | everyFunction # EveryFunctionInvocation + | anyFunction # AnyFunctionInvocation + | treatedPath # TreatedPathInvocation + ; + +functionArguments + : DISTINCT? expressionOrPredicate (',' expressionOrPredicate)* + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-aggregate-functions-filter +filterClause + : FILTER '(' whereClause ')' + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-aggregate-functions-orderedset +withinGroup + : WITHIN GROUP '(' orderByClause ')' + ; + +overClause + : OVER '(' partitionClause? orderByClause? frameClause? ')' + ; + +partitionClause + : PARTITION BY expression (',' expression)* + ; + +frameClause + : (RANGE|ROWS|GROUPS) frameStart frameExclusion? + | (RANGE|ROWS|GROUPS) BETWEEN frameStart AND frameEnd frameExclusion? + ; + +frameStart + : UNBOUNDED PRECEDING # UnboundedPrecedingFrameStart + | expression PRECEDING # ExpressionPrecedingFrameStart + | CURRENT ROW # CurrentRowFrameStart + | expression FOLLOWING # ExpressionFollowingFrameStart + ; + +frameExclusion + : EXCLUDE CURRENT ROW # CurrentRowFrameExclusion + | EXCLUDE GROUP # GroupFrameExclusion + | EXCLUDE TIES # TiesFrameExclusion + | EXCLUDE NO OTHERS # NoOthersFrameExclusion + ; + +frameEnd + : expression PRECEDING # ExpressionPrecedingFrameEnd + | CURRENT ROW # CurrentRowFrameEnd + | expression FOLLOWING # ExpressionFollowingFrameEnd + | UNBOUNDED FOLLOWING # UnboundedFollowingFrameEnd + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-functions +castFunction + : CAST '(' expression AS identifier ')' + ; + +extractFunction + : EXTRACT '(' expression FROM expression ')' + | dateTimeFunction '(' expression ')' + ; + +trimFunction + : TRIM '(' (LEADING | TRAILING | BOTH)? stringLiteral? FROM? expression ')' + ; + +dateTimeFunction + : d=(YEAR + | MONTH + | DAY + | WEEK + | QUARTER + | HOUR + | MINUTE + | SECOND + | NANOSECOND + | EPOCH) + ; + +everyFunction + : every=(EVERY | ALL) '(' predicate ')' + | every=(EVERY | ALL) '(' subquery ')' + | every=(EVERY | ALL) (ELEMENTS | INDICES) '(' simplePath ')' + ; + +anyFunction + : any=(ANY | SOME) '(' predicate ')' + | any=(ANY | SOME) '(' subquery ')' + | any=(ANY | SOME) (ELEMENTS | INDICES) '(' simplePath ')' + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-treat-type +treatedPath + : TREAT '(' path AS simplePath')' pathContinutation? + ; + +pathContinutation + : '.' simplePath + ; + +// Predicates +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-conditional-expressions +predicate + : '(' predicate ')' # GroupedPredicate + | dealingWithNullExpression # NullExpressionPredicate + | inExpression # InPredicate + | betweenExpression # BetweenPredicate + | relationalExpression # RelationalPredicate + | stringPatternMatching # LikePredicate + | existsExpression # ExistsPredicate + | collectionExpression # CollectionPredicate + | NOT predicate # NotPredicate + | predicate AND predicate # AndPredicate + | predicate OR predicate # OrPredicate + | expression # ExpressionPredicate + ; + +expressionOrPredicate + : expression + | predicate + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-relational-comparisons +relationalExpression + : expression op=('=' | '>' | '>=' | '<' | '<=' | '<>' ) expression + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-between-predicate +betweenExpression + : expression NOT? BETWEEN expression AND expression + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-null-predicate +dealingWithNullExpression + : expression IS NOT? NULL + | expression IS NOT? DISTINCT FROM expression + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-like-predicate +stringPatternMatching + : expression NOT? (LIKE | ILIKE) expression (ESCAPE character)? + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-elements-indices +// TBD + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-in-predicate +inExpression + : expression NOT? IN inList + ; + +inList + : (ELEMENTS | INDICES) '(' simplePath ')' + | '(' subquery ')' + | parameter + | '(' (expressionOrPredicate (',' expressionOrPredicate)*)? ')' + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-exists-predicate +// TBD +existsExpression + : EXISTS (ELEMENTS | INDICES) '(' simplePath ')' + | EXISTS expression + ; + +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-collection-operators +collectionExpression + : expression IS NOT? EMPTY + | expression NOT? MEMBER OF path + ; + +// Projection +// https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-select-new +instantiationTarget + : LIST + | MAP + | simplePath + ; + +instantiationArguments + : instantiationArgument (',' instantiationArgument)* + ; + +instantiationArgument + : (expressionOrPredicate | instantiation) variable? + ; + +// Low level parsing rules + +parameterOrIntegerLiteral + : parameter + | INTEGER_LITERAL + ; + +parameterOrNumberLiteral + : parameter + | numericLiteral + ; + +variable + : AS identifier + | reservedWord + ; + +parameter + : prefix=':' identifier + | prefix='?' (INTEGER_LITERAL | spelExpression)? + ; + +entityName + : identifier ('.' identifier)* + ; + +identifier + : reservedWord + | spelExpression + ; + +spelExpression + : prefix='#{#' identificationVariable ('.' identificationVariable)* '}' // #{#entityName} + | prefix='#{#[' INTEGER_LITERAL ']}' // #{[0]} + | prefix='#{' identificationVariable '(' ( stringLiteral | '[' INTEGER_LITERAL ']' )? ')}' // #{escape([0])} | #{escapeCharacter()} + ; + + +character + : CHARACTER + ; + +functionName + : reservedWord + ; + +reservedWord + : IDENTIFICATION_VARIABLE + | f=(ALL + | AND + | ANY + | AS + | ASC + | AVG + | BETWEEN + | BOTH + | BREADTH + | BY + | CASE + | CAST + | COLLATE + | COUNT + | CROSS + | CUBE + | CURRENT + | CURRENT_DATE + | CURRENT_INSTANT + | CURRENT_TIME + | CURRENT_TIMESTAMP + | CYCLE + | DATE + | DATETIME + | DAY + | DEFAULT + | DELETE + | DEPTH + | DESC + | DISTINCT + | ELEMENT + | ELEMENTS + | ELSE + | EMPTY + | END + | ENTRY + | EPOCH + | ERROR + | ESCAPE + | EVERY + | EXCEPT + | EXCLUDE + | EXISTS + | EXTRACT + | FETCH + | FILTER + | FIRST + | FOLLOWING + | FOR + | FORMAT + | FROM +// | FULL + | FUNCTION + | GROUP + | GROUPS + | HAVING + | HOUR + | ID + | IGNORE + | ILIKE + | IN + | INDEX + | INDICES +// | INNER + | INSERT + | INSTANT + | INTERSECT + | INTO + | IS + | JOIN + | KEY + | LAST + | LEADING +// | LEFT + | LIKE + | LIMIT + | LIST + | LISTAGG + | LOCAL + | LOCAL_DATE + | LOCAL_DATETIME + | LOCAL_TIME + | MAP + | MATERIALIZED + | MAX + | MAXELEMENT + | MAXINDEX + | MEMBER + | MICROSECOND + | MILLISECOND + | MIN + | MINELEMENT + | MININDEX + | MINUTE + | MONTH + | NANOSECOND + | NATURALID + | NEW + | NEXT + | NO + | NOT + | NULLS + | OBJECT + | OF + | OFFSET + | OFFSET_DATETIME + | ON + | ONLY + | OR + | ORDER + | OTHERS +// | OUTER + | OVER + | OVERFLOW + | OVERLAY + | PAD + | PARTITION + | PERCENT + | PLACING + | POSITION + | PRECEDING + | QUARTER + | RANGE + | RESPECT +// | RIGHT + | ROLLUP + | ROW + | ROWS + | SEARCH + | SECOND + | SELECT + | SET + | SIZE + | SOME + | SUBSTRING + | SUM + | THEN + | TIES + | TIME + | TIMESTAMP + | TIMEZONE_HOUR + | TIMEZONE_MINUTE + | TO + | TRAILING + | TREAT + | TRIM + | TRUNC + | TRUNCATE + | TYPE + | UNBOUNDED + | UNION + | UPDATE + | USING + | VALUE + | VALUES + | VERSION + | VERSIONED + | WEEK + | WHEN + | WHERE + | WITH + | WITHIN + | WITHOUT + | YEAR) + ; + +/* + Lexer rules + */ + + +WS : [ \t\r\n] -> skip ; + +// Build up case-insentive tokens + +fragment A: 'a' | 'A'; +fragment B: 'b' | 'B'; +fragment C: 'c' | 'C'; +fragment D: 'd' | 'D'; +fragment E: 'e' | 'E'; +fragment F: 'f' | 'F'; +fragment G: 'g' | 'G'; +fragment H: 'h' | 'H'; +fragment I: 'i' | 'I'; +fragment J: 'j' | 'J'; +fragment K: 'k' | 'K'; +fragment L: 'l' | 'L'; +fragment M: 'm' | 'M'; +fragment N: 'n' | 'N'; +fragment O: 'o' | 'O'; +fragment P: 'p' | 'P'; +fragment Q: 'q' | 'Q'; +fragment R: 'r' | 'R'; +fragment S: 's' | 'S'; +fragment T: 't' | 'T'; +fragment U: 'u' | 'U'; +fragment V: 'v' | 'V'; +fragment W: 'w' | 'W'; +fragment X: 'x' | 'X'; +fragment Y: 'y' | 'Y'; +fragment Z: 'z' | 'Z'; + +// The following are reserved identifiers: + +ALL : A L L; +AND : A N D; +ANY : A N Y; +AS : A S; +ASC : A S C; +ASTERISK : '*'; +AVG : A V G; +BETWEEN : B E T W E E N; +BOTH : B O T H; +BREADTH : B R E A D T H; +BY : B Y; +CASE : C A S E; +CAST : C A S T; +CEILING : C E I L I N G; +COLLATE : C O L L A T E; +COUNT : C O U N T; +CROSS : C R O S S; +CUBE : C U B E; +CURRENT : C U R R E N T; +CURRENT_DATE : C U R R E N T '_' D A T E; +CURRENT_INSTANT : C U R R E N T '_' I N S T A N T; +CURRENT_TIME : C U R R E N T '_' T I M E; +CURRENT_TIMESTAMP : C U R R E N T '_' T I M E S T A M P; +CYCLE : C Y C L E; +DATE : D A T E; +DATETIME : D A T E T I M E ; +DAY : D A Y; +DEFAULT : D E F A U L T; +DELETE : D E L E T E; +DEPTH : D E P T H; +DESC : D E S C; +DISTINCT : D I S T I N C T; +ELEMENT : E L E M E N T; +ELEMENTS : E L E M E N T S; +ELSE : E L S E; +EMPTY : E M P T Y; +END : E N D; +ENTRY : E N T R Y; +EPOCH : E P O C H; +ERROR : E R R O R; +ESCAPE : E S C A P E; +EVERY : E V E R Y; +EXCEPT : E X C E P T; +EXCLUDE : E X C L U D E; +EXISTS : E X I S T S; +EXP : E X P; +EXTRACT : E X T R A C T; +FALSE : F A L S E; +FETCH : F E T C H; +FILTER : F I L T E R; +FIRST : F I R S T; +FK : F K; +FLOOR : F L O O R; +FOLLOWING : F O L L O W I N G; +FOR : F O R; +FORMAT : F O R M A T; +FROM : F R O M; +FULL : F U L L; +FUNCTION : F U N C T I O N; +GROUP : G R O U P; +GROUPS : G R O U P S; +HAVING : H A V I N G; +HOUR : H O U R; +ID : I D; +IGNORE : I G N O R E; +ILIKE : I L I K E; +IN : I N; +INDEX : I N D E X; +INDICES : I N D I C E S; +INNER : I N N E R; +INSERT : I N S E R T; +INSTANT : I N S T A N T; +INTERSECT : I N T E R S E C T; +INTO : I N T O; +IS : I S; +JOIN : J O I N; +KEY : K E Y; +LAST : L A S T; +LATERAL : L A T E R A L; +LEADING : L E A D I N G; +LEFT : L E F T; +LIKE : L I K E; +LIMIT : L I M I T; +LIST : L I S T; +LISTAGG : L I S T A G G; +LN : L N; +LOCAL : L O C A L; +LOCAL_DATE : L O C A L '_' D A T E ; +LOCAL_DATETIME : L O C A L '_' D A T E T I M E; +LOCAL_TIME : L O C A L '_' T I M E; +MAP : M A P; +MATERIALIZED : M A T E R I A L I Z E D; +MAX : M A X; +MAXELEMENT : M A X E L E M E N T; +MAXINDEX : M A X I N D E X; +MEMBER : M E M B E R; +MICROSECOND : M I C R O S E C O N D; +MILLISECOND : M I L L I S E C O N D; +MIN : M I N; +MINELEMENT : M I N E L E M E N T; +MININDEX : M I N I N D E X; +MINUTE : M I N U T E; +MONTH : M O N T H; +NANOSECOND : N A N O S E C O N D; +NATURALID : N A T U R A L I D; +NEW : N E W; +NEXT : N E X T; +NO : N O; +NOT : N O T; +NULL : N U L L; +NULLS : N U L L S; +OBJECT : O B J E C T; +OF : O F; +OFFSET : O F F S E T; +OFFSET_DATETIME : O F F S E T '_' D A T E T I M E; +ON : O N; +ONLY : O N L Y; +OR : O R; +ORDER : O R D E R; +OTHERS : O T H E R S; +OUTER : O U T E R; +OVER : O V E R; +OVERFLOW : O V E R F L O W; +OVERLAY : O V E R L A Y; +PAD : P A D; +PARTITION : P A R T I T I O N; +PERCENT : P E R C E N T; +PLACING : P L A C I N G; +POSITION : P O S I T I O N; +POWER : P O W E R; +PRECEDING : P R E C E D I N G; +QUARTER : Q U A R T E R; +RANGE : R A N G E; +RESPECT : R E S P E C T; +RIGHT : R I G H T; +ROLLUP : R O L L U P; +ROUND : R O U N D; +ROW : R O W; +ROWS : R O W S; +SEARCH : S E A R C H; +SECOND : S E C O N D; +SELECT : S E L E C T; +SET : S E T; +SIGN : S I G N; +SIZE : S I Z E; +SOME : S O M E; +SUBSTRING : S U B S T R I N G; +SUM : S U M; +THEN : T H E N; +TIES : T I E S; +TIME : T I M E; +TIMESTAMP : T I M E S T A M P; +TIMEZONE_HOUR : T I M E Z O N E '_' H O U R; +TIMEZONE_MINUTE : T I M E Z O N E '_' M I N U T E; +TO : T O; +TRAILING : T R A I L I N G; +TREAT : T R E A T; +TRIM : T R I M; +TRUE : T R U E; +TRUNC : T R U N C; +TRUNCATE : T R U N C A T E; +TYPE : T Y P E; +UNBOUNDED : U N B O U N D E D; +UNION : U N I O N; +UPDATE : U P D A T E; +USING : U S I N G; +VALUE : V A L U E; +VALUES : V A L U E S; +VERSION : V E R S I O N; +VERSIONED : V E R S I O N E D; +WEEK : W E E K; +WHEN : W H E N; +WHERE : W H E R E; +WITH : W I T H; +WITHIN : W I T H I N; +WITHOUT : W I T H O U T; +YEAR : Y E A R; + +fragment INTEGER_NUMBER : ('0' .. '9')+ ; +fragment FLOAT_NUMBER : INTEGER_NUMBER+ '.'? INTEGER_NUMBER* (E [+-]? INTEGER_NUMBER)? ; + +CHARACTER : '\'' (~ ('\'' | '\\' )) '\'' ; +STRINGLITERAL : '\'' ('\'' '\'' | ~('\'' | '\\'))* '\'' ; +JAVASTRINGLITERAL : '"' ( ('\\' [btnfr"']) | ~('"'))* '"'; +INTEGER_LITERAL : INTEGER_NUMBER (L | B I)? ; +FLOAT_LITERAL : FLOAT_NUMBER (D | F | B D)?; +HEXLITERAL : '0' X ('0' .. '9' | A | B | C | D | E)+ ; + +IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ; + diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 new file mode 100644 index 0000000000..dc96a6ea46 --- /dev/null +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -0,0 +1,850 @@ +/* + * Copyright 2011-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +grammar Jpql; + +@header { +/** + * JPQL per https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1.html#bnf + * + * This is JPA BNF for JPQL. There are gaps and inconsistencies in the BNF itself, explained by other fragments of the spec. + * + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#bnf + * @author Greg Turnquist + * @since 3.1 + */ +} + +/* + Parser rules + */ + +start + : ql_statement EOF + ; + +ql_statement + : select_statement + | update_statement + | delete_statement + ; + +select_statement + : select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? + ; + +update_statement + : update_clause (where_clause)? + ; + +delete_statement + : delete_clause (where_clause)? + ; + +from_clause + : FROM identification_variable_declaration (',' identificationVariableDeclarationOrCollectionMemberDeclaration )* + ; + +// This parser rule is needed to iterate over these two types from #from_clause +identificationVariableDeclarationOrCollectionMemberDeclaration + : identification_variable_declaration + | collection_member_declaration + ; + +identification_variable_declaration + : range_variable_declaration (join | fetch_join)* + ; + +range_variable_declaration + : entity_name (AS)? identification_variable + ; + +join + : join_spec join_association_path_expression (AS)? identification_variable (join_condition)? + ; + +fetch_join + : join_spec FETCH join_association_path_expression + ; + +join_spec + : ((LEFT (OUTER)?) | INNER)? JOIN + ; + +join_condition + : ON conditional_expression + ; + +join_association_path_expression + : join_collection_valued_path_expression + | join_single_valued_path_expression + | TREAT '(' join_collection_valued_path_expression AS subtype ')' + | TREAT '(' join_single_valued_path_expression AS subtype ')' + ; + +join_collection_valued_path_expression + : identification_variable '.' (single_valued_embeddable_object_field '.')* collection_valued_field + ; + +join_single_valued_path_expression + : identification_variable '.' (single_valued_embeddable_object_field '.')* single_valued_object_field + ; + +collection_member_declaration + : IN '(' collection_valued_path_expression ')' (AS)? identification_variable + ; + +qualified_identification_variable + : map_field_identification_variable + | ENTRY '(' identification_variable ')' + ; + +map_field_identification_variable + : KEY '(' identification_variable ')' + | VALUE '(' identification_variable ')' + ; + +single_valued_path_expression + : qualified_identification_variable + | TREAT '(' qualified_identification_variable AS subtype ')' + | state_field_path_expression + | single_valued_object_path_expression + ; + +general_identification_variable + : identification_variable + | map_field_identification_variable + ; + +general_subpath + : simple_subpath + | treated_subpath ('.' single_valued_object_field)* + ; + +simple_subpath + : general_identification_variable + | general_identification_variable ('.' single_valued_object_field)* + ; + +treated_subpath + : TREAT '(' general_subpath AS subtype ')' + ; + +state_field_path_expression + : general_subpath '.' state_field + ; + +state_valued_path_expression + : state_field_path_expression + | general_identification_variable + ; + +single_valued_object_path_expression + : general_subpath '.' single_valued_object_field + ; + +collection_valued_path_expression + : general_subpath '.' collection_value_field // BNF at end of spec has a typo + ; + +update_clause + : UPDATE entity_name ((AS)? identification_variable)? SET update_item (',' update_item)* + ; + +update_item + : (identification_variable '.')? (single_valued_embeddable_object_field '.')* (state_field | single_valued_object_field) '=' new_value + ; + +new_value + : scalar_expression + | simple_entity_expression + | NULL + ; + +delete_clause + : DELETE FROM entity_name ((AS)? identification_variable)? + ; + +select_clause + : SELECT (DISTINCT)? select_item (',' select_item)* + ; + +select_item + : select_expression ((AS)? result_variable)? + ; + +select_expression + : single_valued_path_expression + | scalar_expression + | aggregate_expression + | identification_variable + | OBJECT '(' identification_variable ')' + | constructor_expression + ; + +constructor_expression + : NEW constructor_name '(' constructor_item (',' constructor_item)* ')' + ; + +constructor_item + : single_valued_path_expression + | scalar_expression + | aggregate_expression + | identification_variable + ; + +aggregate_expression + : (AVG | MAX | MIN | SUM) '(' (DISTINCT)? state_valued_path_expression ')' + | COUNT '(' (DISTINCT)? (identification_variable | state_valued_path_expression | single_valued_object_path_expression) ')' + | function_invocation + ; + +where_clause + : WHERE conditional_expression + ; + +groupby_clause + : GROUP BY groupby_item (',' groupby_item)* + ; + +groupby_item + : single_valued_path_expression + | identification_variable + ; + +having_clause + : HAVING conditional_expression + ; + +orderby_clause + : ORDER BY orderby_item (',' orderby_item)* + ; + +// TODO Error in spec BNF, correctly shown elsewhere in spec. +orderby_item + : (state_field_path_expression | general_identification_variable | result_variable ) (ASC | DESC)? + ; + +subquery + : simple_select_clause subquery_from_clause (where_clause)? (groupby_clause)? (having_clause)? + ; + +subquery_from_clause + : FROM subselect_identification_variable_declaration (',' (subselect_identification_variable_declaration | collection_member_declaration))* + ; + +subselect_identification_variable_declaration + : identification_variable_declaration + | derived_path_expression (AS)? identification_variable (join)* + | derived_collection_member_declaration + ; + +derived_path_expression + : general_derived_path '.' single_valued_object_field + | general_derived_path '.' collection_valued_field + ; + +general_derived_path + : simple_derived_path + | treated_derived_path ('.' single_valued_object_field)* + ; + +simple_derived_path + : superquery_identification_variable ('.' single_valued_object_field)* + ; + +treated_derived_path + : TREAT '(' general_derived_path AS subtype ')' + ; + +derived_collection_member_declaration + : IN superquery_identification_variable '.' (single_valued_object_field '.')* collection_valued_field + ; + +simple_select_clause + : SELECT (DISTINCT)? simple_select_expression + ; + +simple_select_expression + : single_valued_path_expression + | scalar_expression + | aggregate_expression + | identification_variable + ; + +scalar_expression + : arithmetic_expression + | string_expression + | enum_expression + | datetime_expression + | boolean_expression + | case_expression + | entity_type_expression + ; + +conditional_expression + : conditional_term + | conditional_expression OR conditional_term + ; + +conditional_term + : conditional_factor + | conditional_term AND conditional_factor + ; + +conditional_factor + : (NOT)? conditional_primary + ; + +conditional_primary + : simple_cond_expression + | '(' conditional_expression ')' + ; + +simple_cond_expression + : comparison_expression + | between_expression + | in_expression + | like_expression + | null_comparison_expression + | empty_collection_comparison_expression + | collection_member_expression + | exists_expression + ; + +between_expression + : arithmetic_expression (NOT)? BETWEEN arithmetic_expression AND arithmetic_expression + | string_expression (NOT)? BETWEEN string_expression AND string_expression + | datetime_expression (NOT)? BETWEEN datetime_expression AND datetime_expression + ; + +in_expression + : (state_valued_path_expression | type_discriminator) (NOT)? IN (('(' in_item (',' in_item)* ')') | ( '(' subquery ')') | collection_valued_input_parameter) + ; + +in_item + : literal + | single_valued_input_parameter + ; + +like_expression + : string_expression (NOT)? LIKE pattern_value (ESCAPE escape_character)? + ; + +null_comparison_expression + : (single_valued_path_expression | input_parameter) IS (NOT)? NULL + ; + +empty_collection_comparison_expression + : collection_valued_path_expression IS (NOT)? EMPTY + ; + +collection_member_expression + : entity_or_value_expression (NOT)? MEMBER (OF)? collection_valued_path_expression + ; + +entity_or_value_expression + : single_valued_object_path_expression + | state_field_path_expression + | simple_entity_or_value_expression + ; + +simple_entity_or_value_expression + : identification_variable + | input_parameter + | literal + ; + +exists_expression + : (NOT)? EXISTS '(' subquery ')' + ; + +all_or_any_expression + : (ALL | ANY | SOME) '(' subquery ')' + ; + +comparison_expression + : string_expression comparison_operator (string_expression | all_or_any_expression) + | boolean_expression op=('=' | '<>') (boolean_expression | all_or_any_expression) + | enum_expression op=('=' | '<>') (enum_expression | all_or_any_expression) + | datetime_expression comparison_operator (datetime_expression | all_or_any_expression) + | entity_expression op=('=' | '<>') (entity_expression | all_or_any_expression) + | arithmetic_expression comparison_operator (arithmetic_expression | all_or_any_expression) + | entity_type_expression op=('=' | '<>') entity_type_expression + ; + +comparison_operator + : op='=' + | op='>' + | op='>=' + | op='<' + | op='<=' + | op='<>' + ; + +arithmetic_expression + : arithmetic_term + | arithmetic_expression op=('+' | '-') arithmetic_term + ; + +arithmetic_term + : arithmetic_factor + | arithmetic_term op=('*' | '/') arithmetic_factor + ; + +arithmetic_factor + : op=('+' | '-')? arithmetic_primary + ; + +arithmetic_primary + : state_valued_path_expression + | numeric_literal + | '(' arithmetic_expression ')' + | input_parameter + | functions_returning_numerics + | aggregate_expression + | case_expression + | function_invocation + | '(' subquery ')' + ; + +string_expression + : state_valued_path_expression + | string_literal + | input_parameter + | functions_returning_strings + | aggregate_expression + | case_expression + | function_invocation + | '(' subquery ')' + ; + +datetime_expression + : state_valued_path_expression + | input_parameter + | functions_returning_datetime + | aggregate_expression + | case_expression + | function_invocation + | date_time_timestamp_literal + | '(' subquery ')' + ; + +boolean_expression + : state_valued_path_expression + | boolean_literal + | input_parameter + | case_expression + | function_invocation + | '(' subquery ')' + ; + +enum_expression + : state_valued_path_expression + | enum_literal + | input_parameter + | case_expression + | '(' subquery ')' + ; + +entity_expression + : single_valued_object_path_expression + | simple_entity_expression + ; + +simple_entity_expression + : identification_variable + | input_parameter + ; + +entity_type_expression + : type_discriminator + | entity_type_literal + | input_parameter + ; + +type_discriminator + : TYPE '(' (general_identification_variable | single_valued_object_path_expression | input_parameter) ')' + ; + +functions_returning_numerics + : LENGTH '(' string_expression ')' + | LOCATE '(' string_expression ',' string_expression (',' arithmetic_expression)? ')' + | ABS '(' arithmetic_expression ')' + | CEILING '(' arithmetic_expression ')' + | EXP '(' arithmetic_expression ')' + | FLOOR '(' arithmetic_expression ')' + | LN '(' arithmetic_expression ')' + | SIGN '(' arithmetic_expression ')' + | SQRT '(' arithmetic_expression ')' + | MOD '(' arithmetic_expression ',' arithmetic_expression ')' + | POWER '(' arithmetic_expression ',' arithmetic_expression ')' + | ROUND '(' arithmetic_expression ',' arithmetic_expression ')' + | SIZE '(' collection_valued_path_expression ')' + | INDEX '(' identification_variable ')' + | extract_datetime_field + ; + +functions_returning_datetime + : CURRENT_DATE + | CURRENT_TIME + | CURRENT_TIMESTAMP + | LOCAL DATE + | LOCAL TIME + | LOCAL DATETIME + | extract_datetime_part + ; + +functions_returning_strings + : CONCAT '(' string_expression ',' string_expression (',' string_expression)* ')' + | SUBSTRING '(' string_expression ',' arithmetic_expression (',' arithmetic_expression)? ')' + | TRIM '(' ((trim_specification)? (trim_character)? FROM)? string_expression ')' + | LOWER '(' string_expression ')' + | UPPER '(' string_expression ')' + ; + +trim_specification + : LEADING + | TRAILING + | BOTH + ; + + +function_invocation + : FUNCTION '(' function_name (',' function_arg)* ')' + ; + +extract_datetime_field + : EXTRACT '(' datetime_field FROM datetime_expression ')' + ; + +datetime_field + : identification_variable + ; + +extract_datetime_part + : EXTRACT '(' datetime_part FROM datetime_expression ')' + ; + +datetime_part + : identification_variable + ; + +function_arg + : literal + | state_valued_path_expression + | input_parameter + | scalar_expression + ; + +case_expression + : general_case_expression + | simple_case_expression + | coalesce_expression + | nullif_expression + ; + +general_case_expression + : CASE when_clause (when_clause)* ELSE scalar_expression END + ; + +when_clause + : WHEN conditional_expression THEN scalar_expression + ; + +simple_case_expression + : CASE case_operand simple_when_clause (simple_when_clause)* ELSE scalar_expression END + ; + +case_operand + : state_valued_path_expression + | type_discriminator + ; + +simple_when_clause + : WHEN scalar_expression THEN scalar_expression + ; + +coalesce_expression + : COALESCE '(' scalar_expression (',' scalar_expression)+ ')' + ; + +nullif_expression + : NULLIF '(' scalar_expression ',' scalar_expression ')' + ; + +/******************* + Gaps in the spec. + *******************/ + +trim_character + : CHARACTER + | character_valued_input_parameter + ; + +identification_variable + : IDENTIFICATION_VARIABLE + | ORDER // Gap in the spec requires supporting 'Order' as an entity name + | COUNT // Gap in the spec requires supporting 'count' as a possible name + | KEY // Gap in the sepc requires supported 'key' as a possible name + | spel_expression // we use various SpEL expressions in our queries + ; + +constructor_name + : state_field_path_expression + ; + +literal + : STRINGLITERAL + | INTLITERAL + | FLOATLITERAL + | boolean_literal + | entity_type_literal + ; + +input_parameter + : '?' INTLITERAL + | ':' identification_variable + ; + +pattern_value + : string_expression + ; + +date_time_timestamp_literal + : STRINGLITERAL + ; + +entity_type_literal + : identification_variable + ; + +escape_character + : CHARACTER + | character_valued_input_parameter // + ; + +numeric_literal + : INTLITERAL + | FLOATLITERAL + ; + +boolean_literal + : TRUE + | FALSE + ; + +enum_literal + : state_field_path_expression + ; + +string_literal + : CHARACTER + | STRINGLITERAL + ; + +single_valued_embeddable_object_field + : identification_variable + ; + +subtype + : identification_variable + ; + +collection_valued_field + : identification_variable + ; + +single_valued_object_field + : identification_variable + ; + +state_field + : identification_variable + ; + +collection_value_field + : identification_variable + ; + +entity_name + : identification_variable + | identification_variable ('.' identification_variable)* // Hibernate sometimes expands the entity name to FQDN when using named queries + ; + +result_variable + : identification_variable + ; + +superquery_identification_variable + : identification_variable + ; + +collection_valued_input_parameter + : input_parameter + ; + +single_valued_input_parameter + : input_parameter + ; + +function_name + : string_literal + ; + +spel_expression + : prefix='#{#' identification_variable ('.' identification_variable)* '}' // #{#entityName} + | prefix='#{#[' INTLITERAL ']}' // #{[0]} + | prefix='#{' identification_variable '(' ( string_literal | '[' INTLITERAL ']' )? ')}' // #{escape([0])} | #{escapeCharacter()} + ; + +character_valued_input_parameter + : CHARACTER + | input_parameter + ; + +/* + Lexer rules + */ + + +WS : [ \t\r\n] -> skip ; + +// Build up case-insentive tokens + +fragment A: 'a' | 'A'; +fragment B: 'b' | 'B'; +fragment C: 'c' | 'C'; +fragment D: 'd' | 'D'; +fragment E: 'e' | 'E'; +fragment F: 'f' | 'F'; +fragment G: 'g' | 'G'; +fragment H: 'h' | 'H'; +fragment I: 'i' | 'I'; +fragment J: 'j' | 'J'; +fragment K: 'k' | 'K'; +fragment L: 'l' | 'L'; +fragment M: 'm' | 'M'; +fragment N: 'n' | 'N'; +fragment O: 'o' | 'O'; +fragment P: 'p' | 'P'; +fragment Q: 'q' | 'Q'; +fragment R: 'r' | 'R'; +fragment S: 's' | 'S'; +fragment T: 't' | 'T'; +fragment U: 'u' | 'U'; +fragment V: 'v' | 'V'; +fragment W: 'w' | 'W'; +fragment X: 'x' | 'X'; +fragment Y: 'y' | 'Y'; +fragment Z: 'z' | 'Z'; + +// The following are reserved identifiers: + +ABS : A B S; +ALL : A L L; +AND : A N D; +ANY : A N Y; +AS : A S; +ASC : A S C; +AVG : A V G; +BETWEEN : B E T W E E N; +BOTH : B O T H; +BY : B Y; +CASE : C A S E; +CEILING : C E I L I N G; +COALESCE : C O A L E S C E; +CONCAT : C O N C A T; +COUNT : C O U N T; +CURRENT_DATE : C U R R E N T '_' D A T E; +CURRENT_TIME : C U R R E N T '_' T I M E; +CURRENT_TIMESTAMP : C U R R E N T '_' T I M E S T A M P; +DATE : D A T E; +DATETIME : D A T E T I M E ; +DELETE : D E L E T E; +DESC : D E S C; +DISTINCT : D I S T I N C T; +END : E N D; +ELSE : E L S E; +EMPTY : E M P T Y; +ENTRY : E N T R Y; +ESCAPE : E S C A P E; +EXISTS : E X I S T S; +EXP : E X P; +EXTRACT : E X T R A C T; +FALSE : F A L S E; +FETCH : F E T C H; +FLOOR : F L O O R; +FROM : F R O M; +FUNCTION : F U N C T I O N; +GROUP : G R O U P; +HAVING : H A V I N G; +IN : I N; +INDEX : I N D E X; +INNER : I N N E R; +IS : I S; +JOIN : J O I N; +KEY : K E Y; +LEADING : L E A D I N G; +LEFT : L E F T; +LENGTH : L E N G T H; +LIKE : L I K E; +LN : L N; +LOCAL : L O C A L; +LOCATE : L O C A T E; +LOWER : L O W E R; +MAX : M A X; +MEMBER : M E M B E R; +MIN : M I N; +MOD : M O D; +NEW : N E W; +NOT : N O T; +NULL : N U L L; +NULLIF : N U L L I F; +OBJECT : O B J E C T; +OF : O F; +ON : O N; +OR : O R; +ORDER : O R D E R; +OUTER : O U T E R; +POWER : P O W E R; +ROUND : R O U N D; +SELECT : S E L E C T; +SET : S E T; +SIGN : S I G N; +SIZE : S I Z E; +SOME : S O M E; +SQRT : S Q R T; +SUBSTRING : S U B S T R I N G; +SUM : S U M; +THEN : T H E N; +TIME : T I M E; +TRAILING : T R A I L I N G; +TREAT : T R E A T; +TRIM : T R I M; +TRUE : T R U E; +TYPE : T Y P E; +UPDATE : U P D A T E; +UPPER : U P P E R; +VALUE : V A L U E; +WHEN : W H E N; +WHERE : W H E R E; + + +CHARACTER : '\'' (~ ('\'' | '\\')) '\'' ; +IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ; +STRINGLITERAL : '\'' (~ ('\'' | '\\'))* '\'' ; +FLOATLITERAL : ('0' .. '9')* '.' ('0' .. '9')+ (E '0' .. '9')* ; +INTLITERAL : ('0' .. '9')+ ; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryParser.java new file mode 100644 index 0000000000..1f65272d15 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryParser.java @@ -0,0 +1,136 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.List; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; + +/** + * Implements the parsing operations of a {@link JpaQueryParser} using the ANTLR-generated {@link HqlParser} and + * {@link HqlQueryTransformer}. + * + * @author Greg Turnquist + * @since 3.1 + */ +class HqlQueryParser extends JpaQueryParser { + + HqlQueryParser(DeclaredQuery declaredQuery) { + super(declaredQuery); + } + + HqlQueryParser(String query) { + super(query); + } + + /** + * Convenience method to parse an HQL query. Will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid. + * + * @param query + * @return a parsed query, ready for postprocessing + */ + static ParserRuleContext parse(String query) { + + HqlLexer lexer = new HqlLexer(CharStreams.fromString(query)); + HqlParser parser = new HqlParser(new CommonTokenStream(lexer)); + + parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener()); + + return parser.start(); + } + + /** + * Parse the query using {@link #parse(String)}. + * + * @return a parsed query + */ + @Override + protected ParserRuleContext parse() { + return parse(getQuery()); + } + + /** + * Use the {@link HqlQueryTransformer} to transform the original query into a query with the {@link Sort} applied. + * + * @param parsedQuery + * @param sort can be {@literal null} + * @return list of {@link JpaQueryParsingToken}s + */ + @Override + protected List doCreateQuery(ParserRuleContext parsedQuery, Sort sort) { + return new HqlQueryTransformer(sort).visit(parsedQuery); + } + + /** + * Use the {@link HqlQueryTransformer} to transform the original query into a count query. + * + * @param parsedQuery + * @param countProjection + * @return list of {@link JpaQueryParsingToken}s + */ + @Override + protected List doCreateCountQuery(ParserRuleContext parsedQuery, + @Nullable String countProjection) { + return new HqlQueryTransformer(true, countProjection).visit(parsedQuery); + } + + /** + * Run the parsed query through {@link HqlQueryTransformer} to find the primary FROM clause's alias. + * + * @param parsedQuery + * @return can be {@literal null} + */ + @Override + protected String doFindAlias(ParserRuleContext parsedQuery) { + + HqlQueryTransformer transformVisitor = new HqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.getAlias(); + } + + /** + * Use {@link HqlQueryTransformer} to find the projection of the query. + * + * @param parsedQuery + * @return + */ + @Override + protected List doFindProjection(ParserRuleContext parsedQuery) { + + HqlQueryTransformer transformVisitor = new HqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.getProjection(); + } + + /** + * Use {@link HqlQueryTransformer} to detect if the query uses a {@code new com.example.Dto()} DTO constructor in the + * primary select clause. + * + * @param parsedQuery + * @return Guaranteed to be {@literal true} or {@literal false}. + */ + @Override + protected boolean doCheckForConstructor(ParserRuleContext parsedQuery) { + + HqlQueryTransformer transformVisitor = new HqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.hasConstructorExpression(); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java new file mode 100644 index 0000000000..c7930c3dcd --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -0,0 +1,2324 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders an HQL query without making any changes. + * + * @author Greg Turnquist + * @since 3.1 + */ +class HqlQueryRenderer extends HqlBaseVisitor> { + + @Override + public List visitStart(HqlParser.StartContext ctx) { + return visit(ctx.ql_statement()); + } + + @Override + public List visitQl_statement(HqlParser.Ql_statementContext ctx) { + + if (ctx.selectStatement() != null) { + return visit(ctx.selectStatement()); + } else if (ctx.updateStatement() != null) { + return visit(ctx.updateStatement()); + } else if (ctx.deleteStatement() != null) { + return visit(ctx.deleteStatement()); + } else if (ctx.insertStatement() != null) { + return visit(ctx.insertStatement()); + } else { + return List.of(); + } + } + + @Override + public List visitSelectStatement(HqlParser.SelectStatementContext ctx) { + return visit(ctx.queryExpression()); + } + + @Override + public List visitQueryExpression(HqlParser.QueryExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.orderedQuery(0))); + + for (int i = 1; i < ctx.orderedQuery().size(); i++) { + + tokens.addAll(visit(ctx.setOperator(i - 1))); + tokens.addAll(visit(ctx.orderedQuery(i))); + } + + return tokens; + } + + @Override + public List visitOrderedQuery(HqlParser.OrderedQueryContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.query() != null) { + tokens.addAll(visit(ctx.query())); + } else if (ctx.queryExpression() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.queryExpression())); + tokens.add(TOKEN_CLOSE_PAREN); + } + + if (ctx.queryOrder() != null) { + tokens.addAll(visit(ctx.queryOrder())); + } + + return tokens; + } + + @Override + public List visitSelectQuery(HqlParser.SelectQueryContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.selectClause() != null) { + tokens.addAll(visit(ctx.selectClause())); + } + + if (ctx.fromClause() != null) { + tokens.addAll(visit(ctx.fromClause())); + } + + if (ctx.whereClause() != null) { + tokens.addAll(visit(ctx.whereClause())); + } + + if (ctx.groupByClause() != null) { + tokens.addAll(visit(ctx.groupByClause())); + } + + if (ctx.havingClause() != null) { + tokens.addAll(visit(ctx.havingClause())); + } + + return tokens; + } + + @Override + public List visitFromQuery(HqlParser.FromQueryContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.fromClause())); + + if (ctx.whereClause() != null) { + tokens.addAll(visit(ctx.whereClause())); + } + + if (ctx.groupByClause() != null) { + tokens.addAll(visit(ctx.groupByClause())); + } + + if (ctx.havingClause() != null) { + tokens.addAll(visit(ctx.havingClause())); + } + + if (ctx.selectClause() != null) { + tokens.addAll(visit(ctx.selectClause())); + } + + return tokens; + } + + @Override + public List visitQueryOrder(HqlParser.QueryOrderContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.orderByClause())); + + if (ctx.limitClause() != null) { + SPACE(tokens); + tokens.addAll(visit(ctx.limitClause())); + } + if (ctx.offsetClause() != null) { + tokens.addAll(visit(ctx.offsetClause())); + } + if (ctx.fetchClause() != null) { + tokens.addAll(visit(ctx.fetchClause())); + } + + return tokens; + } + + @Override + public List visitFromClause(HqlParser.FromClauseContext ctx) { + + List tokens = new ArrayList<>(); + + // TODO: Read up on Framework's LeastRecentlyUsedCache + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + + ctx.entityWithJoins().forEach(entityWithJoinsContext -> { + tokens.addAll(visit(entityWithJoinsContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitEntityWithJoins(HqlParser.EntityWithJoinsContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.fromRoot())); + SPACE(tokens); + + ctx.joinSpecifier().forEach(joinSpecifierContext -> { + tokens.addAll(visit(joinSpecifierContext)); + }); + + return tokens; + } + + @Override + public List visitJoinSpecifier(HqlParser.JoinSpecifierContext ctx) { + + if (ctx.join() != null) { + return visit(ctx.join()); + } else if (ctx.crossJoin() != null) { + return visit(ctx.crossJoin()); + } else if (ctx.jpaCollectionJoin() != null) { + return visit(ctx.jpaCollectionJoin()); + } else { + return List.of(); + } + } + + @Override + public List visitFromRoot(HqlParser.FromRootContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.entityName() != null) { + + tokens.addAll(visit(ctx.entityName())); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + NOSPACE(tokens); + } else if (ctx.subquery() != null) { + + if (ctx.LATERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LATERAL())); + } + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + } + + return tokens; + } + + @Override + public List visitJoin(HqlParser.JoinContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.joinType())); + tokens.add(new JpaQueryParsingToken(ctx.JOIN())); + + if (ctx.FETCH() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FETCH())); + } + + tokens.addAll(visit(ctx.joinTarget())); + + if (ctx.joinRestriction() != null) { + tokens.addAll(visit(ctx.joinRestriction())); + } + + return tokens; + } + + @Override + public List visitJoinPath(HqlParser.JoinPathContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.path())); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + + return tokens; + } + + @Override + public List visitJoinSubquery(HqlParser.JoinSubqueryContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.LATERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LATERAL())); + } + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + + return tokens; + } + + @Override + public List visitUpdateStatement(HqlParser.UpdateStatementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.UPDATE())); + + if (ctx.VERSIONED() != null) { + tokens.add(new JpaQueryParsingToken(ctx.VERSIONED())); + } + + tokens.addAll(visit(ctx.targetEntity())); + tokens.addAll(visit(ctx.setClause())); + + if (ctx.whereClause() != null) { + tokens.addAll(visit(ctx.whereClause())); + } + + return tokens; + } + + @Override + public List visitTargetEntity(HqlParser.TargetEntityContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.entityName())); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + + return tokens; + } + + @Override + public List visitSetClause(HqlParser.SetClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SET())); + + ctx.assignment().forEach(assignmentContext -> { + tokens.addAll(visit(assignmentContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitAssignment(HqlParser.AssignmentContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.simplePath())); + tokens.add(TOKEN_EQUALS); + tokens.addAll(visit(ctx.expressionOrPredicate())); + + return tokens; + } + + @Override + public List visitDeleteStatement(HqlParser.DeleteStatementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.DELETE())); + + if (ctx.FROM() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + } + + tokens.addAll(visit(ctx.targetEntity())); + + if (ctx.whereClause() != null) { + tokens.addAll(visit(ctx.whereClause())); + } + + return tokens; + } + + @Override + public List visitInsertStatement(HqlParser.InsertStatementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.INSERT())); + + if (ctx.INTO() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INTO())); + } + + tokens.addAll(visit(ctx.targetEntity())); + tokens.addAll(visit(ctx.targetFields())); + + if (ctx.queryExpression() != null) { + tokens.addAll(visit(ctx.queryExpression())); + } else if (ctx.valuesList() != null) { + tokens.addAll(visit(ctx.valuesList())); + } + + return tokens; + } + + @Override + public List visitTargetFields(HqlParser.TargetFieldsContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_OPEN_PAREN); + + ctx.simplePath().forEach(simplePathContext -> { + tokens.addAll(visit(simplePathContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitValuesList(HqlParser.ValuesListContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.VALUES())); + + ctx.values().forEach(valuesContext -> { + tokens.addAll(visit(valuesContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitValues(HqlParser.ValuesContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_OPEN_PAREN); + + ctx.expression().forEach(expressionContext -> { + tokens.addAll(visit(expressionContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitProjectedItem(HqlParser.ProjectedItemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.expression() != null) { + tokens.addAll(visit(ctx.expression())); + } else if (ctx.instantiation() != null) { + tokens.addAll(visit(ctx.instantiation())); + } + + if (ctx.alias() != null) { + tokens.addAll(visit(ctx.alias())); + } + + return tokens; + } + + @Override + public List visitInstantiation(HqlParser.InstantiationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.NEW())); + tokens.addAll(visit(ctx.instantiationTarget())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.instantiationArguments())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitAlias(HqlParser.AliasContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + tokens.addAll(visit(ctx.identifier())); + + return tokens; + } + + @Override + public List visitGroupedItem(HqlParser.GroupedItemContext ctx) { + + if (ctx.identifier() != null) { + return visit(ctx.identifier()); + } else if (ctx.INTEGER_LITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); + } else if (ctx.expression() != null) { + return visit(ctx.expression()); + } else { + return List.of(); + } + } + + @Override + public List visitSortedItem(HqlParser.SortedItemContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.sortExpression())); + + if (ctx.sortDirection() != null) { + tokens.addAll(visit(ctx.sortDirection())); + } + + if (ctx.nullsPrecedence() != null) { + tokens.addAll(visit(ctx.nullsPrecedence())); + } + + return tokens; + } + + @Override + public List visitSortExpression(HqlParser.SortExpressionContext ctx) { + + if (ctx.identifier() != null) { + return visit(ctx.identifier()); + } else if (ctx.INTEGER_LITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); + } else if (ctx.expression() != null) { + return visit(ctx.expression()); + } else { + return List.of(); + } + } + + @Override + public List visitSortDirection(HqlParser.SortDirectionContext ctx) { + + if (ctx.ASC() != null) { + return List.of(new JpaQueryParsingToken(ctx.ASC())); + } else if (ctx.DESC() != null) { + return List.of(new JpaQueryParsingToken(ctx.DESC())); + } else { + return List.of(); + } + } + + @Override + public List visitNullsPrecedence(HqlParser.NullsPrecedenceContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.NULLS())); + + if (ctx.FIRST() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FIRST())); + } else if (ctx.LAST() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LAST())); + } + + return tokens; + } + + @Override + public List visitLimitClause(HqlParser.LimitClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.LIMIT())); + tokens.addAll(visit(ctx.parameterOrIntegerLiteral())); + + return tokens; + } + + @Override + public List visitOffsetClause(HqlParser.OffsetClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.OFFSET())); + tokens.addAll(visit(ctx.parameterOrIntegerLiteral())); + + if (ctx.ROW() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ROW())); + } else if (ctx.ROWS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ROWS())); + } + + return tokens; + } + + @Override + public List visitFetchClause(HqlParser.FetchClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.FETCH())); + + if (ctx.FIRST() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FIRST())); + } else if (ctx.NEXT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NEXT())); + } + + if (ctx.parameterOrIntegerLiteral() != null) { + tokens.addAll(visit(ctx.parameterOrIntegerLiteral())); + } else if (ctx.parameterOrNumberLiteral() != null) { + + tokens.addAll(visit(ctx.parameterOrNumberLiteral())); + tokens.add(TOKEN_PERCENT); + } + + if (ctx.ROW() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ROW())); + } else if (ctx.ROWS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ROWS())); + } + + if (ctx.ONLY() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ONLY())); + } else if (ctx.WITH() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.WITH())); + tokens.add(new JpaQueryParsingToken(ctx.TIES())); + } + + return tokens; + } + + @Override + public List visitSubquery(HqlParser.SubqueryContext ctx) { + return visit(ctx.queryExpression()); + } + + @Override + public List visitSelectClause(HqlParser.SelectClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SELECT())); + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + tokens.addAll(visit(ctx.selectionList())); + + return tokens; + } + + @Override + public List visitSelectionList(HqlParser.SelectionListContext ctx) { + + List tokens = new ArrayList<>(); + + ctx.selection().forEach(selectionContext -> { + tokens.addAll(visit(selectionContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitSelection(HqlParser.SelectionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.selectExpression())); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + + return tokens; + } + + @Override + public List visitSelectExpression(HqlParser.SelectExpressionContext ctx) { + + if (ctx.instantiation() != null) { + return visit(ctx.instantiation()); + } else if (ctx.mapEntrySelection() != null) { + return visit(ctx.mapEntrySelection()); + } else if (ctx.jpaSelectObjectSyntax() != null) { + return visit(ctx.jpaSelectObjectSyntax()); + } else if (ctx.expressionOrPredicate() != null) { + return visit(ctx.expressionOrPredicate()); + } else { + return List.of(); + } + } + + @Override + public List visitMapEntrySelection(HqlParser.MapEntrySelectionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.ENTRY())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.path())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitJpaSelectObjectSyntax(HqlParser.JpaSelectObjectSyntaxContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.OBJECT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identifier())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitWhereClause(HqlParser.WhereClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHERE())); + + ctx.predicate().forEach(predicateContext -> { + tokens.addAll(visit(predicateContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitJoinType(HqlParser.JoinTypeContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.INNER() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INNER())); + } + if (ctx.LEFT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LEFT())); + } + if (ctx.RIGHT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.RIGHT())); + } + if (ctx.FULL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FULL())); + } + if (ctx.OUTER() != null) { + tokens.add(new JpaQueryParsingToken(ctx.OUTER())); + } + if (ctx.CROSS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CROSS())); + } + + return tokens; + } + + @Override + public List visitCrossJoin(HqlParser.CrossJoinContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CROSS())); + tokens.add(new JpaQueryParsingToken(ctx.JOIN())); + tokens.addAll(visit(ctx.entityName())); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + + return tokens; + } + + @Override + public List visitJoinRestriction(HqlParser.JoinRestrictionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.ON() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ON())); + } else if (ctx.WITH() != null) { + tokens.add(new JpaQueryParsingToken(ctx.WITH())); + } + + tokens.addAll(visit(ctx.predicate())); + + return tokens; + } + + @Override + public List visitJpaCollectionJoin(HqlParser.JpaCollectionJoinContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_COMMA); + tokens.add(new JpaQueryParsingToken(ctx.IN(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.path())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + + return tokens; + } + + @Override + public List visitGroupByClause(HqlParser.GroupByClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.GROUP())); + tokens.add(new JpaQueryParsingToken(ctx.BY())); + + ctx.groupedItem().forEach(groupedItemContext -> { + tokens.addAll(visit(groupedItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitOrderByClause(HqlParser.OrderByClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.ORDER())); + tokens.add(new JpaQueryParsingToken(ctx.BY())); + + ctx.projectedItem().forEach(projectedItemContext -> { + tokens.addAll(visit(projectedItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitHavingClause(HqlParser.HavingClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.HAVING())); + + ctx.predicate().forEach(predicateContext -> { + tokens.addAll(visit(predicateContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitSetOperator(HqlParser.SetOperatorContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.UNION() != null) { + tokens.add(new JpaQueryParsingToken(ctx.UNION())); + } else if (ctx.INTERSECT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INTERSECT())); + } else if (ctx.EXCEPT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.EXCEPT())); + } + + if (ctx.ALL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ALL())); + } + + return tokens; + } + + @Override + public List visitLiteral(HqlParser.LiteralContext ctx) { + + if (ctx.NULL() != null) { + return List.of(new JpaQueryParsingToken(ctx.NULL())); + } else if (ctx.booleanLiteral() != null) { + return visit(ctx.booleanLiteral()); + } else if (ctx.stringLiteral() != null) { + return visit(ctx.stringLiteral()); + } else if (ctx.numericLiteral() != null) { + return visit(ctx.numericLiteral()); + } else if (ctx.dateTimeLiteral() != null) { + return visit(ctx.dateTimeLiteral()); + } else { + return List.of(); + } + } + + @Override + public List visitBooleanLiteral(HqlParser.BooleanLiteralContext ctx) { + + if (ctx.TRUE() != null) { + return List.of(new JpaQueryParsingToken(ctx.TRUE())); + } else if (ctx.FALSE() != null) { + return List.of(new JpaQueryParsingToken(ctx.FALSE())); + } else { + return List.of(); + } + } + + @Override + public List visitStringLiteral(HqlParser.StringLiteralContext ctx) { + + if (ctx.STRINGLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.STRINGLITERAL())); + } else if (ctx.CHARACTER() != null) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } else { + return List.of(); + } + } + + @Override + public List visitNumericLiteral(HqlParser.NumericLiteralContext ctx) { + + if (ctx.INTEGER_LITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); + } else if (ctx.FLOAT_LITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.FLOAT_LITERAL())); + } else if (ctx.HEXLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.HEXLITERAL())); + } else { + return List.of(); + } + } + + @Override + public List visitDateTimeLiteral(HqlParser.DateTimeLiteralContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.LOCAL_DATE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LOCAL_DATE())); + } else if (ctx.LOCAL_TIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LOCAL_TIME())); + } else if (ctx.LOCAL_DATETIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LOCAL_DATETIME())); + } else if (ctx.CURRENT_DATE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_DATE())); + } else if (ctx.CURRENT_TIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_TIME())); + } else if (ctx.CURRENT_TIMESTAMP() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_TIMESTAMP())); + } else if (ctx.OFFSET_DATETIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.OFFSET_DATETIME())); + } else { + + if (ctx.LOCAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LOCAL())); + } else if (ctx.CURRENT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT())); + } else if (ctx.OFFSET() != null) { + tokens.add(new JpaQueryParsingToken(ctx.OFFSET())); + } + + if (ctx.DATE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DATE())); + } else if (ctx.TIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.TIME())); + } else if (ctx.DATETIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DATETIME())); + } + + if (ctx.INSTANT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INSTANT())); + } + } + + return tokens; + } + + @Override + public List visitPlainPrimaryExpression(HqlParser.PlainPrimaryExpressionContext ctx) { + return visit(ctx.primaryExpression()); + } + + @Override + public List visitTupleExpression(HqlParser.TupleExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_OPEN_PAREN); + + ctx.expressionOrPredicate().forEach(expressionOrPredicateContext -> { + tokens.addAll(visit(expressionOrPredicateContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitHqlConcatenationExpression(HqlParser.HqlConcatenationExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression(0))); + tokens.add(TOKEN_DOUBLE_PIPE); + tokens.addAll(visit(ctx.expression(1))); + + return tokens; + } + + @Override + public List visitGroupedExpression(HqlParser.GroupedExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.expression())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitAdditionExpression(HqlParser.AdditionExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + tokens.addAll(visit(ctx.expression(1))); + + return tokens; + } + + @Override + public List visitSignedNumericLiteral(HqlParser.SignedNumericLiteralContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.op)); + tokens.addAll(visit(ctx.numericLiteral())); + + return tokens; + } + + @Override + public List visitMultiplicationExpression(HqlParser.MultiplicationExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression(0))); + NOSPACE(tokens); + tokens.add(new JpaQueryParsingToken(ctx.op, false)); + tokens.addAll(visit(ctx.expression(1))); + + return tokens; + } + + @Override + public List visitSubqueryExpression(HqlParser.SubqueryExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitSignedExpression(HqlParser.SignedExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.op)); + tokens.addAll(visit(ctx.expression())); + + return tokens; + } + + @Override + public List visitCaseExpression(HqlParser.CaseExpressionContext ctx) { + return visit(ctx.caseList()); + } + + @Override + public List visitLiteralExpression(HqlParser.LiteralExpressionContext ctx) { + return visit(ctx.literal()); + } + + @Override + public List visitParameterExpression(HqlParser.ParameterExpressionContext ctx) { + return visit(ctx.parameter()); + } + + @Override + public List visitFunctionExpression(HqlParser.FunctionExpressionContext ctx) { + return visit(ctx.function()); + } + + @Override + public List visitGeneralPathExpression(HqlParser.GeneralPathExpressionContext ctx) { + return visit(ctx.generalPathFragment()); + } + + @Override + public List visitIdentificationVariable(HqlParser.IdentificationVariableContext ctx) { + + if (ctx.identifier() != null) { + return visit(ctx.identifier()); + } else if (ctx.simplePath() != null) { + return visit(ctx.simplePath()); + } else { + return List.of(); + } + } + + @Override + public List visitPath(HqlParser.PathContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.treatedPath() != null) { + + tokens.addAll(visit(ctx.treatedPath())); + + if (ctx.pathContinutation() != null) { + NOSPACE(tokens); + tokens.addAll(visit(ctx.pathContinutation())); + } + } else if (ctx.generalPathFragment() != null) { + tokens.addAll(visit(ctx.generalPathFragment())); + } + + return tokens; + } + + @Override + public List visitGeneralPathFragment(HqlParser.GeneralPathFragmentContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.simplePath())); + + if (ctx.indexedPathAccessFragment() != null) { + tokens.addAll(visit(ctx.indexedPathAccessFragment())); + } + + return tokens; + } + + @Override + public List visitIndexedPathAccessFragment(HqlParser.IndexedPathAccessFragmentContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_OPEN_SQUARE_BRACKET); + tokens.addAll(visit(ctx.expression())); + tokens.add(TOKEN_CLOSE_SQUARE_BRACKET); + + if (ctx.generalPathFragment() != null) { + + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.generalPathFragment())); + } + + return tokens; + } + + @Override + public List visitSimplePath(HqlParser.SimplePathContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.identifier())); + NOSPACE(tokens); + + ctx.simplePathElement().forEach(simplePathElementContext -> { + tokens.addAll(visit(simplePathElementContext)); + NOSPACE(tokens); + }); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitSimplePathElement(HqlParser.SimplePathElementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.identifier())); + + return tokens; + } + + @Override + public List visitCaseList(HqlParser.CaseListContext ctx) { + + if (ctx.simpleCaseExpression() != null) { + return visit(ctx.simpleCaseExpression()); + } else if (ctx.searchedCaseExpression() != null) { + return visit(ctx.searchedCaseExpression()); + } else { + return List.of(); + } + } + + @Override + public List visitSimpleCaseExpression(HqlParser.SimpleCaseExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CASE())); + tokens.addAll(visit(ctx.expressionOrPredicate(0))); + + ctx.caseWhenExpressionClause().forEach(caseWhenExpressionClauseContext -> { + tokens.addAll(visit(caseWhenExpressionClauseContext)); + }); + + if (ctx.ELSE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ELSE())); + tokens.addAll(visit(ctx.expressionOrPredicate(1))); + } + + tokens.add(new JpaQueryParsingToken(ctx.END())); + + return tokens; + } + + @Override + public List visitSearchedCaseExpression(HqlParser.SearchedCaseExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CASE())); + + ctx.caseWhenPredicateClause().forEach(caseWhenPredicateClauseContext -> { + tokens.addAll(visit(caseWhenPredicateClauseContext)); + }); + + if (ctx.ELSE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ELSE())); + tokens.addAll(visit(ctx.expressionOrPredicate())); + } + + tokens.add(new JpaQueryParsingToken(ctx.END())); + + return tokens; + } + + @Override + public List visitCaseWhenExpressionClause(HqlParser.CaseWhenExpressionClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHEN())); + tokens.addAll(visit(ctx.expression())); + tokens.add(new JpaQueryParsingToken(ctx.THEN())); + tokens.addAll(visit(ctx.expressionOrPredicate())); + + return tokens; + } + + @Override + public List visitCaseWhenPredicateClause(HqlParser.CaseWhenPredicateClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHEN())); + tokens.addAll(visit(ctx.predicate())); + tokens.add(new JpaQueryParsingToken(ctx.THEN())); + tokens.addAll(visit(ctx.expressionOrPredicate())); + + return tokens; + } + + @Override + public List visitGenericFunction(HqlParser.GenericFunctionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.functionName())); + NOSPACE(tokens); + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.functionArguments() != null) { + tokens.addAll(visit(ctx.functionArguments())); + } else if (ctx.ASTERISK() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ASTERISK())); + } + + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.pathContinutation() != null) { + NOSPACE(tokens); + tokens.addAll(visit(ctx.pathContinutation())); + } + + if (ctx.filterClause() != null) { + tokens.addAll(visit(ctx.filterClause())); + } + + if (ctx.withinGroup() != null) { + tokens.addAll(visit(ctx.withinGroup())); + } + + if (ctx.overClause() != null) { + tokens.addAll(visit(ctx.overClause())); + } + + return tokens; + } + + @Override + public List visitFunctionWithSubquery(HqlParser.FunctionWithSubqueryContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.functionName())); + NOSPACE(tokens); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitCastFunctionInvocation(HqlParser.CastFunctionInvocationContext ctx) { + return visit(ctx.castFunction()); + } + + @Override + public List visitExtractFunctionInvocation(HqlParser.ExtractFunctionInvocationContext ctx) { + return visit(ctx.extractFunction()); + } + + @Override + public List visitTrimFunctionInvocation(HqlParser.TrimFunctionInvocationContext ctx) { + return visit(ctx.trimFunction()); + } + + @Override + public List visitEveryFunctionInvocation(HqlParser.EveryFunctionInvocationContext ctx) { + return visit(ctx.everyFunction()); + } + + @Override + public List visitAnyFunctionInvocation(HqlParser.AnyFunctionInvocationContext ctx) { + return visit(ctx.anyFunction()); + } + + @Override + public List visitTreatedPathInvocation(HqlParser.TreatedPathInvocationContext ctx) { + return visit(ctx.treatedPath()); + } + + @Override + public List visitFunctionArguments(HqlParser.FunctionArgumentsContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + ctx.expressionOrPredicate().forEach(expressionOrPredicateContext -> { + tokens.addAll(visit(expressionOrPredicateContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitFilterClause(HqlParser.FilterClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.FILTER())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.whereClause())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitWithinGroup(HqlParser.WithinGroupContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WITHIN())); + tokens.add(new JpaQueryParsingToken(ctx.GROUP())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.orderByClause())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitOverClause(HqlParser.OverClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.OVER())); + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.partitionClause() != null) { + tokens.addAll(visit(ctx.partitionClause())); + } + + if (ctx.orderByClause() != null) { + tokens.addAll(visit(ctx.orderByClause())); + SPACE(tokens); + } + + if (ctx.frameClause() != null) { + tokens.addAll(visit(ctx.frameClause())); + } + + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitPartitionClause(HqlParser.PartitionClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.PARTITION())); + tokens.add(new JpaQueryParsingToken(ctx.BY())); + + ctx.expression().forEach(expressionContext -> { + tokens.addAll(visit(expressionContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitFrameClause(HqlParser.FrameClauseContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.RANGE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.RANGE())); + } else if (ctx.ROWS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ROWS())); + } else if (ctx.GROUPS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.GROUPS())); + } + + if (ctx.BETWEEN() != null) { + tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); + } + + tokens.addAll(visit(ctx.frameStart())); + + if (ctx.AND() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.frameEnd())); + } + + if (ctx.frameExclusion() != null) { + tokens.addAll(visit(ctx.frameExclusion())); + } + + return tokens; + } + + @Override + public List visitUnboundedPrecedingFrameStart( + HqlParser.UnboundedPrecedingFrameStartContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.UNBOUNDED())); + tokens.add(new JpaQueryParsingToken(ctx.PRECEDING())); + + return tokens; + } + + @Override + public List visitExpressionPrecedingFrameStart( + HqlParser.ExpressionPrecedingFrameStartContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression())); + tokens.add(new JpaQueryParsingToken(ctx.PRECEDING())); + + return tokens; + } + + @Override + public List visitCurrentRowFrameStart(HqlParser.CurrentRowFrameStartContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CURRENT())); + tokens.add(new JpaQueryParsingToken(ctx.ROW())); + + return tokens; + } + + @Override + public List visitExpressionFollowingFrameStart( + HqlParser.ExpressionFollowingFrameStartContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression())); + tokens.add(new JpaQueryParsingToken(ctx.FOLLOWING())); + + return tokens; + } + + @Override + public List visitCurrentRowFrameExclusion(HqlParser.CurrentRowFrameExclusionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.EXCLUDE())); + tokens.add(new JpaQueryParsingToken(ctx.CURRENT())); + tokens.add(new JpaQueryParsingToken(ctx.ROW())); + + return tokens; + } + + @Override + public List visitGroupFrameExclusion(HqlParser.GroupFrameExclusionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.EXCLUDE())); + tokens.add(new JpaQueryParsingToken(ctx.GROUP())); + + return tokens; + } + + @Override + public List visitTiesFrameExclusion(HqlParser.TiesFrameExclusionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.EXCLUDE())); + tokens.add(new JpaQueryParsingToken(ctx.TIES())); + + return tokens; + } + + @Override + public List visitNoOthersFrameExclusion(HqlParser.NoOthersFrameExclusionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.EXCLUDE())); + tokens.add(new JpaQueryParsingToken(ctx.NO())); + tokens.add(new JpaQueryParsingToken(ctx.OTHERS())); + + return tokens; + } + + @Override + public List visitExpressionPrecedingFrameEnd(HqlParser.ExpressionPrecedingFrameEndContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression())); + tokens.add(new JpaQueryParsingToken(ctx.PRECEDING())); + + return tokens; + } + + @Override + public List visitCurrentRowFrameEnd(HqlParser.CurrentRowFrameEndContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CURRENT())); + tokens.add(new JpaQueryParsingToken(ctx.ROW())); + + return tokens; + } + + @Override + public List visitExpressionFollowingFrameEnd(HqlParser.ExpressionFollowingFrameEndContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression())); + tokens.add(new JpaQueryParsingToken(ctx.FOLLOWING())); + + return tokens; + } + + @Override + public List visitUnboundedFollowingFrameEnd(HqlParser.UnboundedFollowingFrameEndContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.UNBOUNDED())); + tokens.add(new JpaQueryParsingToken(ctx.FOLLOWING())); + + return tokens; + } + + @Override + public List visitCastFunction(HqlParser.CastFunctionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CAST())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.expression())); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.identifier())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitExtractFunction(HqlParser.ExtractFunctionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.EXTRACT() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.EXTRACT())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + tokens.addAll(visit(ctx.expression(1))); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.dateTimeFunction() != null) { + + tokens.addAll(visit(ctx.dateTimeFunction())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.expression(0))); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitTrimFunction(HqlParser.TrimFunctionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.TRIM())); + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.LEADING() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LEADING())); + } else if (ctx.TRAILING() != null) { + tokens.add(new JpaQueryParsingToken(ctx.TRAILING())); + } else if (ctx.BOTH() != null) { + tokens.add(new JpaQueryParsingToken(ctx.BOTH())); + } + + if (ctx.stringLiteral() != null) { + tokens.addAll(visit(ctx.stringLiteral())); + } + + if (ctx.FROM() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + } + + tokens.addAll(visit(ctx.expression())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitDateTimeFunction(HqlParser.DateTimeFunctionContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.d)); + } + + @Override + public List visitEveryFunction(HqlParser.EveryFunctionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.every)); + + if (ctx.ELEMENTS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ELEMENTS())); + } else if (ctx.INDICES() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INDICES())); + } + + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.predicate() != null) { + tokens.addAll(visit(ctx.predicate())); + } else if (ctx.subquery() != null) { + tokens.addAll(visit(ctx.subquery())); + } else if (ctx.simplePath() != null) { + tokens.addAll(visit(ctx.simplePath())); + } + + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitAnyFunction(HqlParser.AnyFunctionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.any)); + + if (ctx.ELEMENTS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ELEMENTS())); + } else if (ctx.INDICES() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INDICES())); + } + + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.predicate() != null) { + tokens.addAll(visit(ctx.predicate())); + } else if (ctx.subquery() != null) { + tokens.addAll(visit(ctx.subquery())); + } else if (ctx.simplePath() != null) { + tokens.addAll(visit(ctx.simplePath())); + } + + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitTreatedPath(HqlParser.TreatedPathContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.path())); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.simplePath())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.pathContinutation() != null) { + NOSPACE(tokens); + tokens.addAll(visit(ctx.pathContinutation())); + } + + return tokens; + } + + @Override + public List visitPathContinutation(HqlParser.PathContinutationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.simplePath())); + + return tokens; + } + + @Override + public List visitNullExpressionPredicate(HqlParser.NullExpressionPredicateContext ctx) { + return visit(ctx.dealingWithNullExpression()); + } + + @Override + public List visitBetweenPredicate(HqlParser.BetweenPredicateContext ctx) { + return visit(ctx.betweenExpression()); + } + + @Override + public List visitOrPredicate(HqlParser.OrPredicateContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.predicate(0))); + tokens.add(new JpaQueryParsingToken(ctx.OR())); + tokens.addAll(visit(ctx.predicate(1))); + + return tokens; + } + + @Override + public List visitRelationalPredicate(HqlParser.RelationalPredicateContext ctx) { + return visit(ctx.relationalExpression()); + } + + @Override + public List visitExistsPredicate(HqlParser.ExistsPredicateContext ctx) { + return visit(ctx.existsExpression()); + } + + @Override + public List visitCollectionPredicate(HqlParser.CollectionPredicateContext ctx) { + return visit(ctx.collectionExpression()); + } + + @Override + public List visitAndPredicate(HqlParser.AndPredicateContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.predicate(0))); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.predicate(1))); + + return tokens; + } + + @Override + public List visitGroupedPredicate(HqlParser.GroupedPredicateContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.predicate())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitLikePredicate(HqlParser.LikePredicateContext ctx) { + return visit(ctx.stringPatternMatching()); + } + + @Override + public List visitInPredicate(HqlParser.InPredicateContext ctx) { + return visit(ctx.inExpression()); + } + + @Override + public List visitNotPredicate(HqlParser.NotPredicateContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + tokens.addAll(visit(ctx.predicate())); + + return tokens; + } + + @Override + public List visitExpressionPredicate(HqlParser.ExpressionPredicateContext ctx) { + return visit(ctx.expression()); + } + + @Override + public List visitExpressionOrPredicate(HqlParser.ExpressionOrPredicateContext ctx) { + + if (ctx.expression() != null) { + return visit(ctx.expression()); + } else if (ctx.predicate() != null) { + return visit(ctx.predicate()); + } else { + return List.of(); + } + } + + @Override + public List visitRelationalExpression(HqlParser.RelationalExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + tokens.addAll(visit(ctx.expression(1))); + + return tokens; + } + + @Override + public List visitBetweenExpression(HqlParser.BetweenExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression(0))); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); + tokens.addAll(visit(ctx.expression(1))); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.expression(2))); + + return tokens; + } + + @Override + public List visitDealingWithNullExpression(HqlParser.DealingWithNullExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.IS())); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + if (ctx.NULL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NULL())); + } else if (ctx.DISTINCT() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + tokens.addAll(visit(ctx.expression(1))); + } + + return tokens; + } + + @Override + public List visitStringPatternMatching(HqlParser.StringPatternMatchingContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression(0))); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + if (ctx.LIKE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LIKE())); + } else if (ctx.ILIKE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ILIKE())); + } + + tokens.addAll(visit(ctx.expression(1))); + + if (ctx.ESCAPE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ESCAPE())); + tokens.addAll(visit(ctx.character())); + } + + return tokens; + } + + @Override + public List visitInExpression(HqlParser.InExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression())); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.IN())); + tokens.addAll(visit(ctx.inList())); + + return tokens; + } + + @Override + public List visitInList(HqlParser.InListContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.simplePath() != null) { + + if (ctx.ELEMENTS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ELEMENTS())); + } else if (ctx.INDICES() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INDICES())); + } + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.simplePath())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.parameter() != null) { + tokens.addAll(visit(ctx.parameter())); + } else if (ctx.expressionOrPredicate() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + + ctx.expressionOrPredicate().forEach(expressionOrPredicateContext -> { + tokens.addAll(visit(expressionOrPredicateContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitExistsExpression(HqlParser.ExistsExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.simplePath() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.EXISTS())); + + if (ctx.ELEMENTS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ELEMENTS())); + } else if (ctx.INDICES() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INDICES())); + } + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.simplePath())); + tokens.add(TOKEN_CLOSE_PAREN); + + } else if (ctx.expression() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.EXISTS())); + tokens.addAll(visit(ctx.expression())); + } + + return tokens; + } + + @Override + public List visitCollectionExpression(HqlParser.CollectionExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression())); + + if (ctx.IS() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.IS())); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.EMPTY())); + } else if (ctx.MEMBER() != null) { + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.MEMBER())); + tokens.add(new JpaQueryParsingToken(ctx.OF())); + tokens.addAll(visit(ctx.path())); + } + + return tokens; + } + + @Override + public List visitInstantiationTarget(HqlParser.InstantiationTargetContext ctx) { + + if (ctx.LIST() != null) { + return List.of(new JpaQueryParsingToken(ctx.LIST())); + } else if (ctx.MAP() != null) { + return List.of(new JpaQueryParsingToken(ctx.MAP())); + } else if (ctx.simplePath() != null) { + + List tokens = visit(ctx.simplePath()); + NOSPACE(tokens); + return tokens; + } else { + return List.of(); + } + } + + @Override + public List visitInstantiationArguments(HqlParser.InstantiationArgumentsContext ctx) { + + List tokens = new ArrayList<>(); + + ctx.instantiationArgument().forEach(instantiationArgumentContext -> { + tokens.addAll(visit(instantiationArgumentContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitInstantiationArgument(HqlParser.InstantiationArgumentContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.expressionOrPredicate() != null) { + tokens.addAll(visit(ctx.expressionOrPredicate())); + } else if (ctx.instantiation() != null) { + tokens.addAll(visit(ctx.instantiation())); + } + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + } + + return tokens; + } + + @Override + public List visitParameterOrIntegerLiteral(HqlParser.ParameterOrIntegerLiteralContext ctx) { + + if (ctx.parameter() != null) { + return visit(ctx.parameter()); + } else if (ctx.INTEGER_LITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); + } else { + return List.of(); + } + } + + @Override + public List visitParameterOrNumberLiteral(HqlParser.ParameterOrNumberLiteralContext ctx) { + + if (ctx.parameter() != null) { + return visit(ctx.parameter()); + } else if (ctx.numericLiteral() != null) { + return visit(ctx.numericLiteral()); + } else { + return List.of(); + } + } + + @Override + public List visitVariable(HqlParser.VariableContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identifier() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.identifier())); + } else if (ctx.reservedWord() != null) { + tokens.addAll(visit(ctx.reservedWord())); + } + + return tokens; + } + + @Override + public List visitParameter(HqlParser.ParameterContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.prefix.getText().equals(":")) { + + tokens.add(TOKEN_COLON); + tokens.addAll(visit(ctx.identifier())); + } else if (ctx.prefix.getText().equals("?")) { + + tokens.add(TOKEN_QUESTION_MARK); + + if (ctx.INTEGER_LITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); + } else if (ctx.spelExpression() != null) { + tokens.addAll(visit(ctx.spelExpression())); + } + } + + return tokens; + } + + @Override + public List visitEntityName(HqlParser.EntityNameContext ctx) { + + List tokens = new ArrayList<>(); + + ctx.identifier().forEach(identifierContext -> { + tokens.addAll(visit(identifierContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitIdentifier(HqlParser.IdentifierContext ctx) { + + if (ctx.reservedWord() != null) { + return visit(ctx.reservedWord()); + } else if (ctx.spelExpression() != null) { + return visit(ctx.spelExpression()); + } else { + return List.of(); + } + } + + @Override + public List visitSpelExpression(HqlParser.SpelExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.prefix.equals("#{#")) { // #{#entityName} + + tokens.add(new JpaQueryParsingToken(ctx.prefix)); + + ctx.identificationVariable().forEach(identificationVariableContext -> { + tokens.addAll(visit(identificationVariableContext)); + tokens.add(TOKEN_DOT); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_BRACE); + + } else if (ctx.prefix.equals("#{#[")) { // #{[0]} + + tokens.add(new JpaQueryParsingToken(ctx.prefix)); + tokens.add(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); + tokens.add(TOKEN_CLOSE_SQUARE_BRACKET_BRACE); + + } else if (ctx.prefix.equals("#{")) {// #{escape([0])} or #{escape('foo')} + + tokens.add(new JpaQueryParsingToken(ctx.prefix)); + tokens.addAll(visit(ctx.identificationVariable(0))); + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.stringLiteral() != null) { + tokens.addAll(visit(ctx.stringLiteral())); + } else if (ctx.INTEGER_LITERAL() != null) { + + tokens.add(TOKEN_OPEN_SQUARE_BRACKET); + tokens.add(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); + tokens.add(TOKEN_CLOSE_SQUARE_BRACKET); + } + + tokens.add(TOKEN_CLOSE_PAREN_BRACE); + } + + return tokens; + } + + @Override + public List visitCharacter(HqlParser.CharacterContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } + + @Override + public List visitFunctionName(HqlParser.FunctionNameContext ctx) { + return visit(ctx.reservedWord()); + } + + @Override + public List visitReservedWord(HqlParser.ReservedWordContext ctx) { + + if (ctx.IDENTIFICATION_VARIABLE() != null) { + return List.of(new JpaQueryParsingToken(ctx.IDENTIFICATION_VARIABLE())); + } else { + return List.of(new JpaQueryParsingToken(ctx.f)); + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java new file mode 100644 index 0000000000..27fd78dc85 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java @@ -0,0 +1,338 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import java.util.ArrayList; +import java.util.List; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; + +/** + * An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed HQL query. + * + * @author Greg Turnquist + * @since 3.1 + */ +class HqlQueryTransformer extends HqlQueryRenderer { + + @Nullable private Sort sort; + private boolean countQuery; + + @Nullable private String countProjection; + + @Nullable private String alias = null; + + private List projection = null; + + private boolean hasConstructorExpression = false; + + HqlQueryTransformer() { + this(null, false, null); + } + + HqlQueryTransformer(@Nullable Sort sort) { + this(sort, false, null); + } + + HqlQueryTransformer(boolean countQuery, @Nullable String countProjection) { + this(null, countQuery, countProjection); + } + + private HqlQueryTransformer(@Nullable Sort sort, boolean countQuery, @Nullable String countProjection) { + + this.sort = sort; + this.countQuery = countQuery; + this.countProjection = countProjection; + } + + @Nullable + public String getAlias() { + return this.alias; + } + + public List getProjection() { + return this.projection; + } + + public boolean hasConstructorExpression() { + return this.hasConstructorExpression; + } + + /** + * Is this select clause a {@literal subquery}? + * + * @return boolean + */ + private static boolean isSubquery(ParserRuleContext ctx) { + + if (ctx instanceof HqlParser.SubqueryContext) { + return true; + } else if (ctx instanceof HqlParser.SelectStatementContext) { + return false; + } else { + return isSubquery(ctx.getParent()); + } + } + + @Override + public List visitOrderedQuery(HqlParser.OrderedQueryContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.query() != null) { + tokens.addAll(visit(ctx.query())); + } else if (ctx.queryExpression() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.queryExpression())); + tokens.add(TOKEN_CLOSE_PAREN); + } + + if (!countQuery && !isSubquery(ctx)) { + + if (ctx.queryOrder() != null) { + tokens.addAll(visit(ctx.queryOrder())); + } + + if (this.sort != null && this.sort.isSorted()) { + + if (ctx.queryOrder() != null) { + + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + } else { + + SPACE(tokens); + tokens.add(TOKEN_ORDER_BY); + } + + this.sort.forEach(order -> { + + JpaQueryParser.checkSortExpression(order); + + if (order.isIgnoreCase()) { + tokens.add(TOKEN_LOWER_FUNC); + } + tokens.add(new JpaQueryParsingToken(() -> { + + if (order.getProperty().contains("(")) { + return order.getProperty(); + } + + return this.alias + "." + order.getProperty(); + }, true)); + if (order.isIgnoreCase()) { + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + tokens.add(order.isDescending() ? TOKEN_DESC : TOKEN_ASC); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + } + } else { + + if (ctx.queryOrder() != null) { + tokens.addAll(visit(ctx.queryOrder())); + } + } + + return tokens; + } + + @Override + public List visitFromQuery(HqlParser.FromQueryContext ctx) { + + List tokens = new ArrayList<>(); + + if (countQuery && !isSubquery(ctx) && ctx.selectClause() == null) { + + tokens.add(TOKEN_SELECT_COUNT); + + if (countProjection != null) { + tokens.add(new JpaQueryParsingToken(countProjection)); + } else { + tokens.add(new JpaQueryParsingToken(() -> this.alias, false)); + } + + tokens.add(TOKEN_CLOSE_PAREN); + } + + if (ctx.fromClause() != null) { + tokens.addAll(visit(ctx.fromClause())); + } + + if (ctx.whereClause() != null) { + tokens.addAll(visit(ctx.whereClause())); + } + + if (ctx.groupByClause() != null) { + tokens.addAll(visit(ctx.groupByClause())); + } + + if (ctx.havingClause() != null) { + tokens.addAll(visit(ctx.havingClause())); + } + + if (ctx.selectClause() != null) { + tokens.addAll(visit(ctx.selectClause())); + } + + return tokens; + } + + @Override + public List visitQueryOrder(HqlParser.QueryOrderContext ctx) { + + List tokens = new ArrayList<>(); + + if (!countQuery) { + tokens.addAll(visit(ctx.orderByClause())); + } + + if (ctx.limitClause() != null) { + SPACE(tokens); + tokens.addAll(visit(ctx.limitClause())); + } + if (ctx.offsetClause() != null) { + tokens.addAll(visit(ctx.offsetClause())); + } + if (ctx.fetchClause() != null) { + tokens.addAll(visit(ctx.fetchClause())); + } + + return tokens; + } + + @Override + public List visitFromRoot(HqlParser.FromRootContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.entityName() != null) { + + tokens.addAll(visit(ctx.entityName())); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + + if (this.alias == null && !isSubquery(ctx)) { + this.alias = tokens.get(tokens.size() - 1).getToken(); + } + } + } else if (ctx.subquery() != null) { + + if (ctx.LATERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LATERAL())); + } + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.variable() != null) { + tokens.addAll(visit(ctx.variable())); + + if (this.alias == null && !isSubquery(ctx)) { + this.alias = tokens.get(tokens.size() - 1).getToken(); + } + } + } + + return tokens; + } + + @Override + public List visitAlias(HqlParser.AliasContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + tokens.addAll(visit(ctx.identifier())); + + if (this.alias == null && !isSubquery(ctx)) { + this.alias = tokens.get(tokens.size() - 1).getToken(); + } + + return tokens; + } + + @Override + public List visitSelectClause(HqlParser.SelectClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SELECT())); + + if (countQuery && !isSubquery(ctx)) { + tokens.add(TOKEN_COUNT_FUNC); + + if (countProjection != null) { + tokens.add(new JpaQueryParsingToken(countProjection)); + } + } + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + List selectionListTokens = visit(ctx.selectionList()); + + if (countQuery && !isSubquery(ctx)) { + + if (countProjection == null) { + + if (ctx.DISTINCT() != null) { + + if (selectionListTokens.stream().anyMatch(hqlToken -> hqlToken.getToken().contains("new"))) { + // constructor + tokens.add(new JpaQueryParsingToken(() -> this.alias)); + } else { + // keep all the select items to distinct against + tokens.addAll(selectionListTokens); + } + } else { + tokens.add(new JpaQueryParsingToken(() -> this.alias)); + } + } + + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else { + tokens.addAll(selectionListTokens); + } + + if (projection == null && !isSubquery(ctx)) { + this.projection = selectionListTokens; + } + + return tokens; + } + + @Override + public List visitInstantiation(HqlParser.InstantiationContext ctx) { + + this.hasConstructorExpression = true; + + return super.visitInstantiation(ctx); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParser.java new file mode 100644 index 0000000000..c41a4e56cb --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParser.java @@ -0,0 +1,213 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import java.util.List; +import java.util.regex.Pattern; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.JpaSort; +import org.springframework.lang.Nullable; + +/** + * Operations needed to parse a JPA query. + * + * @author Greg Turnquist + * @since 3.1 + */ +abstract class JpaQueryParser { + + private static final Pattern PUNCTUATION_PATTERN = Pattern.compile(".*((?![._])[\\p{Punct}|\\s])"); + + private static final String UNSAFE_PROPERTY_REFERENCE = "Sort expression '%s' must only contain property references or " + + "aliases used in the select clause; If you really want to use something other than that for sorting, please use " + + "JpaSort.unsafe(…)"; + + private final DeclaredQuery declaredQuery; + + JpaQueryParser(DeclaredQuery declaredQuery) { + this.declaredQuery = declaredQuery; + } + + JpaQueryParser(String query) { + this(DeclaredQuery.of(query, false)); + } + + DeclaredQuery getDeclaredQuery() { + return declaredQuery; + } + + String getQuery() { + return getDeclaredQuery().getQueryString(); + } + + /** + * Generate a query using the original query with an @literal order by} clause added (or amended) based upon the + * provider {@link Sort} parameter. + * + * @param sort can be {@literal null} + */ + String createQuery(Sort sort) { + + try { + ParserRuleContext parsedQuery = parse(); + + if (parsedQuery == null) { + return ""; + } + + return render(doCreateQuery(parsedQuery, sort)); + } catch (JpaQueryParsingSyntaxError e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Generate a count-based query using the original query. + * + * @param countProjection + */ + String createCountQuery(@Nullable String countProjection) { + + try { + ParserRuleContext parsedQuery = parse(); + + if (parsedQuery == null) { + return ""; + } + + return render(doCreateCountQuery(parsedQuery, countProjection)); + } catch (JpaQueryParsingSyntaxError e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Find the projection of the query. + * + * @param parsedQuery + */ + String projection() { + + try { + ParserRuleContext parsedQuery = parse(); + + if (parsedQuery == null) { + return ""; + } + + return render(doFindProjection(parsedQuery)); + } catch (JpaQueryParsingSyntaxError e) { + return ""; + } + } + + /** + * Find the alias of the query's primary FROM clause + * + * @return can be {@literal null} + */ + String findAlias() { + + try { + ParserRuleContext parsedQuery = parse(); + + if (parsedQuery == null) { + return null; + } + + return doFindAlias(parsedQuery); + } catch (JpaQueryParsingSyntaxError e) { + return null; + } + } + + /** + * Discern if the query has a {@code new com.example.Dto()} DTO constructor in the select clause. + * + * @return Guaranteed to be {@literal true} or {@literal false}. + */ + boolean hasConstructorExpression() { + + try { + ParserRuleContext parsedQuery = parse(); + + if (parsedQuery == null) { + return false; + } + + return doCheckForConstructor(parsedQuery); + } catch (JpaQueryParsingSyntaxError e) { + return false; + } + } + + /** + * Check any given {@link JpaSort.JpaOrder#isUnsafe()} order for presence of at least one property offending the + * {@link #PUNCTUATION_PATTERN} and throw an {@link Exception} indicating potential unsafe order by expression. + * + * @param order + */ + static void checkSortExpression(Sort.Order order) { + + if (order instanceof JpaSort.JpaOrder && ((JpaSort.JpaOrder) order).isUnsafe()) { + return; + } + + if (PUNCTUATION_PATTERN.matcher(order.getProperty()).find()) { + throw new InvalidDataAccessApiUsageException(String.format(UNSAFE_PROPERTY_REFERENCE, order)); + } + } + + /** + * Parse the JPA query using its corresponding ANTLR parser. + */ + protected abstract ParserRuleContext parse(); + + /** + * Create a {@link JpaQueryParsingToken}-based query with an {@literal order by} applied/amended based upon the + * {@link Sort} parameter. + * + * @param parsedQuery + * @param sort can be {@literal null} + */ + protected abstract List doCreateQuery(ParserRuleContext parsedQuery, Sort sort); + + /** + * Create a {@link JpaQueryParsingToken}-based count query. + * + * @param parsedQuery + * @param countProjection + */ + protected abstract List doCreateCountQuery(ParserRuleContext parsedQuery, + @Nullable String countProjection); + + protected abstract String doFindAlias(ParserRuleContext parsedQuery); + + /** + * Find the projection of the query's primary SELECT clause. + * + * @param parsedQuery + */ + protected abstract List doFindProjection(ParserRuleContext parsedQuery); + + protected abstract boolean doCheckForConstructor(ParserRuleContext parsedQuery); + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingEnhancer.java new file mode 100644 index 0000000000..f6ca59eff7 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingEnhancer.java @@ -0,0 +1,143 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +/** + * Implementation of {@link QueryEnhancer} using a {@link JpaQueryParser}.
    + *
    + * NOTE: The parser can find everything it needs to created sorted and count queries. Thus, looking up the alias or the + * projection isn't needed for its primary function, and are simply implemented for test purposes. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpaQueryParsingEnhancer implements QueryEnhancer { + + private final JpaQueryParser queryParser; + + /** + * Initialize with an {@link JpaQueryParser}. + * + * @param queryParser + */ + public JpaQueryParsingEnhancer(JpaQueryParser queryParser) { + + Assert.notNull(queryParser, "queryParse must not be null!"); + this.queryParser = queryParser; + } + + public JpaQueryParser getQueryParsingStrategy() { + return queryParser; + } + + /** + * Adds an {@literal order by} clause to the JPA query. + * + * @param sort the sort specification to apply. + * @return + */ + @Override + public String applySorting(Sort sort) { + return queryParser.createQuery(sort); + } + + /** + * Because the parser can find the alias of the FROM clause, there is no need to "find it" in advance. + * + * @param sort the sort specification to apply. + * @param alias IGNORED + * @return + */ + @Override + public String applySorting(Sort sort, String alias) { + return applySorting(sort); + } + + /** + * Resolves the alias for the entity in the FROM clause from the JPA query. Since the {@link JpaQueryParser} can + * already find the alias when generating sorted and count queries, this is mainly to serve test cases. + */ + @Override + public String detectAlias() { + return queryParser.findAlias(); + } + + /** + * Creates a count query from the original query, with no count projection. + * + * @return Guaranteed to be not {@literal null}; + */ + @Override + public String createCountQueryFor() { + return createCountQueryFor(null); + } + + /** + * Create a count query from the original query, with potential custom projection. + * + * @param countProjection may be {@literal null}. + */ + @Override + public String createCountQueryFor(@Nullable String countProjection) { + return queryParser.createCountQuery(countProjection); + } + + /** + * Checks if the select clause has a new constructor instantiation in the JPA query. + * + * @return Guaranteed to return {@literal true} or {@literal false}. + */ + @Override + public boolean hasConstructorExpression() { + return queryParser.hasConstructorExpression(); + } + + /** + * Looks up the projection of the JPA query. Since the {@link JpaQueryParser} can already find the projection when + * generating sorted and count queries, this is mainly to serve test cases. + */ + @Override + public String getProjection() { + return queryParser.projection(); + } + + /** + * Since the {@link JpaQueryParser} can already fully transform sorted and count queries by itself, this is a + * placeholder method. + * + * @return empty set + */ + @Override + public Set getJoinAliases() { + return Set.of(); + } + + /** + * Look up the {@link DeclaredQuery} from the {@link JpaQueryParser}. + */ + @Override + public DeclaredQuery getQuery() { + return queryParser.getDeclaredQuery(); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxError.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxError.java new file mode 100644 index 0000000000..6224a8ce5b --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxError.java @@ -0,0 +1,32 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.springframework.dao.InvalidDataAccessResourceUsageException; + +/** + * An exception thrown if the JPQL query is invalid. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpaQueryParsingSyntaxError extends InvalidDataAccessResourceUsageException { + + public JpaQueryParsingSyntaxError(String message) { + super(message); + } + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxErrorListener.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxErrorListener.java new file mode 100644 index 0000000000..2017eb40a7 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxErrorListener.java @@ -0,0 +1,35 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +/** + * A {@link BaseErrorListener} that will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpaQueryParsingSyntaxErrorListener extends BaseErrorListener { + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, + String msg, RecognitionException e) { + throw new JpaQueryParsingSyntaxError("line " + line + ":" + charPositionInLine + " " + msg); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java new file mode 100644 index 0000000000..9ef60b9710 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java @@ -0,0 +1,182 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * A value type used to represent a JPA query token. NOTE: Sometimes the token's value is based upon a value found later + * in the parsing process, so the text itself is wrapped in a {@link Supplier}. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpaQueryParsingToken { + + /** + * Commonly use tokens. + */ + public static final JpaQueryParsingToken TOKEN_COMMA = new JpaQueryParsingToken(","); + public static final JpaQueryParsingToken TOKEN_DOT = new JpaQueryParsingToken(".", false); + public static final JpaQueryParsingToken TOKEN_EQUALS = new JpaQueryParsingToken("="); + public static final JpaQueryParsingToken TOKEN_OPEN_PAREN = new JpaQueryParsingToken("(", false); + public static final JpaQueryParsingToken TOKEN_CLOSE_PAREN = new JpaQueryParsingToken(")"); + public static final JpaQueryParsingToken TOKEN_ORDER_BY = new JpaQueryParsingToken("order by"); + public static final JpaQueryParsingToken TOKEN_LOWER_FUNC = new JpaQueryParsingToken("lower(", false); + public static final JpaQueryParsingToken TOKEN_SELECT_COUNT = new JpaQueryParsingToken("select count(", false); + public static final JpaQueryParsingToken TOKEN_PERCENT = new JpaQueryParsingToken("%"); + public static final JpaQueryParsingToken TOKEN_COUNT_FUNC = new JpaQueryParsingToken("count(", false); + public static final JpaQueryParsingToken TOKEN_DOUBLE_PIPE = new JpaQueryParsingToken("||"); + public static final JpaQueryParsingToken TOKEN_OPEN_SQUARE_BRACKET = new JpaQueryParsingToken("[", false); + public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET = new JpaQueryParsingToken("]"); + public static final JpaQueryParsingToken TOKEN_COLON = new JpaQueryParsingToken(":", false); + public static final JpaQueryParsingToken TOKEN_QUESTION_MARK = new JpaQueryParsingToken("?", false); + public static final JpaQueryParsingToken TOKEN_CLOSE_BRACE = new JpaQueryParsingToken("}"); + public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET_BRACE = new JpaQueryParsingToken("]}"); + public static final JpaQueryParsingToken TOKEN_CLOSE_PAREN_BRACE = new JpaQueryParsingToken(")}"); + + public static final JpaQueryParsingToken TOKEN_DESC = new JpaQueryParsingToken("desc", false); + + public static final JpaQueryParsingToken TOKEN_ASC = new JpaQueryParsingToken("asc", false); + /** + * The text value of the token. + */ + private final Supplier token; + + /** + * Space|NoSpace after token is rendered? + */ + private final boolean space; + + JpaQueryParsingToken(Supplier token, boolean space) { + + this.token = token; + this.space = space; + } + + JpaQueryParsingToken(String token, boolean space) { + this(() -> token, space); + } + + JpaQueryParsingToken(Supplier token) { + this(token, true); + } + + JpaQueryParsingToken(String token) { + this(() -> token, true); + } + + JpaQueryParsingToken(TerminalNode node, boolean space) { + this(node.getText(), space); + } + + JpaQueryParsingToken(TerminalNode node) { + this(node.getText()); + } + + JpaQueryParsingToken(Token token, boolean space) { + this(token.getText(), space); + } + + JpaQueryParsingToken(Token token) { + this(token.getText(), true); + } + + /** + * Extract the token's value from it's {@link Supplier}. + */ + String getToken() { + return this.token.get(); + } + + /** + * Should we render a space after the token? + */ + boolean getSpace() { + return this.space; + } + + /** + * Switch the last {@link JpaQueryParsingToken}'s spacing to {@literal true}. + */ + static void SPACE(List tokens) { + + if (!tokens.isEmpty()) { + + int index = tokens.size() - 1; + + JpaQueryParsingToken lastTokenWithSpacing = new JpaQueryParsingToken(tokens.get(index).token); + tokens.remove(index); + tokens.add(lastTokenWithSpacing); + } + } + + /** + * Switch the last {@link JpaQueryParsingToken}'s spacing to {@literal false}. + */ + static void NOSPACE(List tokens) { + + if (!tokens.isEmpty()) { + + int index = tokens.size() - 1; + + JpaQueryParsingToken lastTokenWithNoSpacing = new JpaQueryParsingToken(tokens.get(index).token, false); + tokens.remove(index); + tokens.add(lastTokenWithNoSpacing); + } + } + + /** + * Drop the last entry from the list of {@link JpaQueryParsingToken}s. + */ + static void CLIP(List tokens) { + + if (!tokens.isEmpty()) { + tokens.remove(tokens.size() - 1); + } + } + + /** + * Render a list of {@link JpaQueryParsingToken}s into a string. + * + * @param tokens + * @return rendered string containing either a query or some subset of that query + */ + static String render(List tokens) { + + if (tokens == null) { + return ""; + } + + StringBuilder results = new StringBuilder(); + + tokens.forEach(token -> { + + results.append(token.getToken()); + + if (token.getSpace()) { + results.append(" "); + } + }); + + return results.toString().trim(); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryParser.java new file mode 100644 index 0000000000..c7cfef600c --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryParser.java @@ -0,0 +1,136 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.List; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; + +/** + * Implements the parsing operations of a {@link JpaQueryParser} using the ANTLR-generated {@link JpqlParser} and + * {@link JpqlQueryTransformer}. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpqlQueryParser extends JpaQueryParser { + + JpqlQueryParser(DeclaredQuery declaredQuery) { + super(declaredQuery); + } + + JpqlQueryParser(String query) { + super(query); + } + + /** + * Convenience method to parse a JPQL query. Will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid. + * + * @param query + * @return a parsed query, ready for postprocessing + */ + static ParserRuleContext parse(String query) { + + JpqlLexer lexer = new JpqlLexer(CharStreams.fromString(query)); + JpqlParser parser = new JpqlParser(new CommonTokenStream(lexer)); + + parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener()); + + return parser.start(); + } + + /** + * Parse the query using {@link #parse(String)}. + * + * @return a parsed query + */ + @Override + protected ParserRuleContext parse() { + return parse(getQuery()); + } + + /** + * Use the {@link JpqlQueryTransformer} to transform the original query into a query with the {@link Sort} applied. + * + * @param parsedQuery + * @param sort can be {@literal null} + * @return list of {@link JpaQueryParsingToken}s + */ + @Override + protected List doCreateQuery(ParserRuleContext parsedQuery, Sort sort) { + return new JpqlQueryTransformer(sort).visit(parsedQuery); + } + + /** + * Use the {@link JpqlQueryTransformer} to transform the original query into a count query. + * + * @param parsedQuery + * @param countProjection + * @return list of {@link JpaQueryParsingToken}s + */ + @Override + protected List doCreateCountQuery(ParserRuleContext parsedQuery, + @Nullable String countProjection) { + return new JpqlQueryTransformer(true, countProjection).visit(parsedQuery); + } + + /** + * Run the parsed query through {@link JpqlQueryTransformer} to find the primary FROM clause's alias. + * + * @param parsedQuery + * @return can be {@literal null} + */ + @Override + protected String doFindAlias(ParserRuleContext parsedQuery) { + + JpqlQueryTransformer transformVisitor = new JpqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.getAlias(); + } + + /** + * Use {@link JpqlQueryTransformer} to find the projection of the query. + * + * @param parsedQuery + * @return + */ + @Override + protected List doFindProjection(ParserRuleContext parsedQuery) { + + JpqlQueryTransformer transformVisitor = new JpqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.getProjection(); + } + + /** + * Use {@link JpqlQueryTransformer} to detect if the query uses a {@code new com.example.Dto()} DTO constructor in the + * primary select clause. + * + * @param parsedQuery + * @return Guaranteed to be {@literal true} or {@literal false}. + */ + @Override + protected boolean doCheckForConstructor(ParserRuleContext parsedQuery) { + + JpqlQueryTransformer transformVisitor = new JpqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.hasConstructorExpression(); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java new file mode 100644 index 0000000000..e433713319 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -0,0 +1,2379 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders a JPQL query without making any changes. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpqlQueryRenderer extends JpqlBaseVisitor> { + + @Override + public List visitStart(JpqlParser.StartContext ctx) { + return visit(ctx.ql_statement()); + } + + @Override + public List visitQl_statement(JpqlParser.Ql_statementContext ctx) { + + if (ctx.select_statement() != null) { + return visit(ctx.select_statement()); + } else if (ctx.update_statement() != null) { + return visit(ctx.update_statement()); + } else if (ctx.delete_statement() != null) { + return visit(ctx.delete_statement()); + } else { + return List.of(); + } + } + + @Override + public List visitSelect_statement(JpqlParser.Select_statementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.select_clause())); + tokens.addAll(visit(ctx.from_clause())); + + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + + if (ctx.groupby_clause() != null) { + tokens.addAll(visit(ctx.groupby_clause())); + } + + if (ctx.having_clause() != null) { + tokens.addAll(visit(ctx.having_clause())); + } + + if (ctx.orderby_clause() != null) { + tokens.addAll(visit(ctx.orderby_clause())); + } + + return tokens; + } + + @Override + public List visitUpdate_statement(JpqlParser.Update_statementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.update_clause())); + + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + + return tokens; + } + + @Override + public List visitDelete_statement(JpqlParser.Delete_statementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.delete_clause())); + + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + + return tokens; + } + + @Override + public List visitFrom_clause(JpqlParser.From_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.FROM(), true)); + tokens.addAll(visit(ctx.identification_variable_declaration())); + + ctx.identificationVariableDeclarationOrCollectionMemberDeclaration() + .forEach(identificationVariableDeclarationOrCollectionMemberDeclarationContext -> { + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(identificationVariableDeclarationOrCollectionMemberDeclarationContext)); + }); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitIdentificationVariableDeclarationOrCollectionMemberDeclaration( + JpqlParser.IdentificationVariableDeclarationOrCollectionMemberDeclarationContext ctx) { + + if (ctx.identification_variable_declaration() != null) { + return visit(ctx.identification_variable_declaration()); + } else if (ctx.collection_member_declaration() != null) { + return visit(ctx.collection_member_declaration()); + } else { + return List.of(); + } + } + + @Override + public List visitIdentification_variable_declaration( + JpqlParser.Identification_variable_declarationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.range_variable_declaration())); + + ctx.join().forEach(joinContext -> { + tokens.addAll(visit(joinContext)); + }); + ctx.fetch_join().forEach(fetchJoinContext -> { + tokens.addAll(visit(fetchJoinContext)); + }); + + return tokens; + } + + @Override + public List visitRange_variable_declaration(JpqlParser.Range_variable_declarationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.entity_name())); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + tokens.addAll(visit(ctx.identification_variable())); + + return tokens; + } + + @Override + public List visitJoin(JpqlParser.JoinContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.join_spec())); + tokens.addAll(visit(ctx.join_association_path_expression())); + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + tokens.addAll(visit(ctx.identification_variable())); + if (ctx.join_condition() != null) { + tokens.addAll(visit(ctx.join_condition())); + } + + return tokens; + } + + @Override + public List visitFetch_join(JpqlParser.Fetch_joinContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.join_spec())); + tokens.add(new JpaQueryParsingToken(ctx.FETCH())); + tokens.addAll(visit(ctx.join_association_path_expression())); + + return tokens; + } + + @Override + public List visitJoin_spec(JpqlParser.Join_specContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.LEFT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LEFT())); + } + if (ctx.OUTER() != null) { + tokens.add(new JpaQueryParsingToken(ctx.OUTER())); + } + if (ctx.INNER() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INNER())); + } + if (ctx.JOIN() != null) { + tokens.add(new JpaQueryParsingToken(ctx.JOIN())); + } + + return tokens; + } + + @Override + public List visitJoin_condition(JpqlParser.Join_conditionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.ON())); + tokens.addAll(visit(ctx.conditional_expression())); + + return tokens; + } + + @Override + public List visitJoin_association_path_expression( + JpqlParser.Join_association_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.TREAT() == null) { + + if (ctx.join_collection_valued_path_expression() != null) { + tokens.addAll(visit(ctx.join_collection_valued_path_expression())); + } else if (ctx.join_single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.join_single_valued_path_expression())); + } + } else { + if (ctx.join_collection_valued_path_expression() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.join_collection_valued_path_expression())); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.subtype())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.join_single_valued_path_expression() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.join_single_valued_path_expression())); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.subtype())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + } + + return tokens; + } + + @Override + public List visitJoin_collection_valued_path_expression( + JpqlParser.Join_collection_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + + ctx.single_valued_embeddable_object_field().forEach(singleValuedEmbeddableObjectFieldContext -> { + tokens.addAll(visit(singleValuedEmbeddableObjectFieldContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + + tokens.addAll(visit(ctx.collection_valued_field())); + + return tokens; + } + + @Override + public List visitJoin_single_valued_path_expression( + JpqlParser.Join_single_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.identification_variable())); + tokens.add(TOKEN_DOT); + + ctx.single_valued_embeddable_object_field().forEach(singleValuedEmbeddableObjectFieldContext -> { + tokens.addAll(visit(singleValuedEmbeddableObjectFieldContext)); + tokens.add(TOKEN_DOT); + }); + + tokens.addAll(visit(ctx.single_valued_object_field())); + + return tokens; + } + + @Override + public List visitCollection_member_declaration( + JpqlParser.Collection_member_declarationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.IN(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.collection_valued_path_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + tokens.addAll(visit(ctx.identification_variable())); + + return tokens; + } + + @Override + public List visitQualified_identification_variable( + JpqlParser.Qualified_identification_variableContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.map_field_identification_variable() != null) { + tokens.addAll(visit(ctx.map_field_identification_variable())); + } else if (ctx.identification_variable() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ENTRY())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitMap_field_identification_variable( + JpqlParser.Map_field_identification_variableContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.KEY() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.KEY(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.VALUE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.VALUE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitSingle_valued_path_expression( + JpqlParser.Single_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.qualified_identification_variable() != null) { + tokens.addAll(visit(ctx.qualified_identification_variable())); + } else if (ctx.qualified_identification_variable() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.qualified_identification_variable())); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.subtype())); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.state_field_path_expression() != null) { + tokens.addAll(visit(ctx.state_field_path_expression())); + } else if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } + + return tokens; + } + + @Override + public List visitGeneral_identification_variable( + JpqlParser.General_identification_variableContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.map_field_identification_variable() != null) { + tokens.addAll(visit(ctx.map_field_identification_variable())); + } + + return tokens; + } + + @Override + public List visitGeneral_subpath(JpqlParser.General_subpathContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.simple_subpath() != null) { + tokens.addAll(visit(ctx.simple_subpath())); + } else if (ctx.treated_subpath() != null) { + + tokens.addAll(visit(ctx.treated_subpath())); + + ctx.single_valued_object_field().forEach(singleValuedObjectFieldContext -> { + tokens.add(TOKEN_DOT); + tokens.addAll(visit(singleValuedObjectFieldContext)); + }); + } + + return tokens; + } + + @Override + public List visitSimple_subpath(JpqlParser.Simple_subpathContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.general_identification_variable())); + NOSPACE(tokens); + + ctx.single_valued_object_field().forEach(singleValuedObjectFieldContext -> { + tokens.add(TOKEN_DOT); + tokens.addAll(visit(singleValuedObjectFieldContext)); + NOSPACE(tokens); + }); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitTreated_subpath(JpqlParser.Treated_subpathContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.general_subpath())); + SPACE(tokens); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.subtype())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitState_field_path_expression( + JpqlParser.State_field_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.general_subpath())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.state_field())); + + return tokens; + } + + @Override + public List visitState_valued_path_expression( + JpqlParser.State_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_field_path_expression() != null) { + tokens.addAll(visit(ctx.state_field_path_expression())); + } else if (ctx.general_identification_variable() != null) { + tokens.addAll(visit(ctx.general_identification_variable())); + } + + return tokens; + } + + @Override + public List visitSingle_valued_object_path_expression( + JpqlParser.Single_valued_object_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.general_subpath())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.single_valued_object_field())); + + return tokens; + } + + @Override + public List visitCollection_valued_path_expression( + JpqlParser.Collection_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.general_subpath())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.collection_value_field())); + + return tokens; + } + + @Override + public List visitUpdate_clause(JpqlParser.Update_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.UPDATE())); + tokens.addAll(visit(ctx.entity_name())); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + tokens.add(new JpaQueryParsingToken(ctx.SET())); + + ctx.update_item().forEach(updateItemContext -> { + tokens.addAll(visit(updateItemContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitUpdate_item(JpqlParser.Update_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + } + + ctx.single_valued_embeddable_object_field().forEach(singleValuedEmbeddableObjectFieldContext -> { + tokens.addAll(visit(singleValuedEmbeddableObjectFieldContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + + if (ctx.state_field() != null) { + tokens.addAll(visit(ctx.state_field())); + } else if (ctx.single_valued_object_field() != null) { + tokens.addAll(visit(ctx.single_valued_object_field())); + } + + tokens.add(TOKEN_EQUALS); + tokens.addAll(visit(ctx.new_value())); + + return tokens; + } + + @Override + public List visitNew_value(JpqlParser.New_valueContext ctx) { + + if (ctx.scalar_expression() != null) { + return visit(ctx.scalar_expression()); + } else if (ctx.simple_entity_expression() != null) { + return visit(ctx.simple_entity_expression()); + } else if (ctx.NULL() != null) { + return List.of(new JpaQueryParsingToken(ctx.NULL())); + } else { + return List.of(); + } + } + + @Override + public List visitDelete_clause(JpqlParser.Delete_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.DELETE())); + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + tokens.addAll(visit(ctx.entity_name())); + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitSelect_clause(JpqlParser.Select_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SELECT())); + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + ctx.select_item().forEach(selectItemContext -> { + tokens.addAll(visit(selectItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitSelect_item(JpqlParser.Select_itemContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.select_expression())); + SPACE(tokens); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + if (ctx.result_variable() != null) { + tokens.addAll(visit(ctx.result_variable())); + } + + return tokens; + } + + @Override + public List visitSelect_expression(JpqlParser.Select_expressionContext ctx) { + + if (ctx.single_valued_path_expression() != null) { + return visit(ctx.single_valued_path_expression()); + } else if (ctx.scalar_expression() != null) { + return visit(ctx.scalar_expression()); + } else if (ctx.aggregate_expression() != null) { + return visit(ctx.aggregate_expression()); + } else if (ctx.identification_variable() != null) { + + if (ctx.OBJECT() == null) { + return visit(ctx.identification_variable()); + } else { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.OBJECT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + } else if (ctx.constructor_expression() != null) { + return visit(ctx.constructor_expression()); + } else { + return List.of(); + } + } + + @Override + public List visitConstructor_expression(JpqlParser.Constructor_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.NEW())); + tokens.addAll(visit(ctx.constructor_name())); + tokens.add(TOKEN_OPEN_PAREN); + + ctx.constructor_item().forEach(constructorItemContext -> { + tokens.addAll(visit(constructorItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitConstructor_item(JpqlParser.Constructor_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_path_expression())); + } else if (ctx.scalar_expression() != null) { + tokens.addAll(visit(ctx.scalar_expression())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitAggregate_expression(JpqlParser.Aggregate_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.AVG() != null || ctx.MAX() != null || ctx.MIN() != null || ctx.SUM() != null) { + + if (ctx.AVG() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AVG(), false)); + } + if (ctx.MAX() != null) { + tokens.add(new JpaQueryParsingToken(ctx.MAX(), false)); + } + if (ctx.MIN() != null) { + tokens.add(new JpaQueryParsingToken(ctx.MIN(), false)); + } + if (ctx.SUM() != null) { + tokens.add(new JpaQueryParsingToken(ctx.SUM(), false)); + } + + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + tokens.addAll(visit(ctx.state_valued_path_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.COUNT() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.COUNT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } + + return tokens; + } + + @Override + public List visitWhere_clause(JpqlParser.Where_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHERE(), true)); + tokens.addAll(visit(ctx.conditional_expression())); + + return tokens; + } + + @Override + public List visitGroupby_clause(JpqlParser.Groupby_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.GROUP())); + tokens.add(new JpaQueryParsingToken(ctx.BY())); + ctx.groupby_item().forEach(groupbyItemContext -> { + tokens.addAll(visit(groupbyItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitGroupby_item(JpqlParser.Groupby_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_path_expression())); + } else if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitHaving_clause(JpqlParser.Having_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.HAVING())); + tokens.addAll(visit(ctx.conditional_expression())); + + return tokens; + } + + @Override + public List visitOrderby_clause(JpqlParser.Orderby_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.ORDER())); + tokens.add(new JpaQueryParsingToken(ctx.BY())); + + ctx.orderby_item().forEach(orderbyItemContext -> { + tokens.addAll(visit(orderbyItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitOrderby_item(JpqlParser.Orderby_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_field_path_expression() != null) { + tokens.addAll(visit(ctx.state_field_path_expression())); + } else if (ctx.general_identification_variable() != null) { + tokens.addAll(visit(ctx.general_identification_variable())); + } else if (ctx.result_variable() != null) { + tokens.addAll(visit(ctx.result_variable())); + } + + if (ctx.ASC() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ASC())); + } + if (ctx.DESC() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DESC())); + } + + return tokens; + } + + @Override + public List visitSubquery(JpqlParser.SubqueryContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.simple_select_clause())); + tokens.addAll(visit(ctx.subquery_from_clause())); + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + if (ctx.groupby_clause() != null) { + tokens.addAll(visit(ctx.groupby_clause())); + } + if (ctx.having_clause() != null) { + tokens.addAll(visit(ctx.having_clause())); + } + + return tokens; + } + + @Override + public List visitSubquery_from_clause(JpqlParser.Subquery_from_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + ctx.subselect_identification_variable_declaration().forEach(subselectIdentificationVariableDeclarationContext -> { + tokens.addAll(visit(subselectIdentificationVariableDeclarationContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitSubselect_identification_variable_declaration( + JpqlParser.Subselect_identification_variable_declarationContext ctx) { + return super.visitSubselect_identification_variable_declaration(ctx); + } + + @Override + public List visitDerived_path_expression(JpqlParser.Derived_path_expressionContext ctx) { + return super.visitDerived_path_expression(ctx); + } + + @Override + public List visitGeneral_derived_path(JpqlParser.General_derived_pathContext ctx) { + return super.visitGeneral_derived_path(ctx); + } + + @Override + public List visitSimple_derived_path(JpqlParser.Simple_derived_pathContext ctx) { + return super.visitSimple_derived_path(ctx); + } + + @Override + public List visitTreated_derived_path(JpqlParser.Treated_derived_pathContext ctx) { + return super.visitTreated_derived_path(ctx); + } + + @Override + public List visitDerived_collection_member_declaration( + JpqlParser.Derived_collection_member_declarationContext ctx) { + return super.visitDerived_collection_member_declaration(ctx); + } + + @Override + public List visitSimple_select_clause(JpqlParser.Simple_select_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SELECT())); + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + tokens.addAll(visit(ctx.simple_select_expression())); + + return tokens; + } + + @Override + public List visitSimple_select_expression(JpqlParser.Simple_select_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_path_expression())); + } else if (ctx.scalar_expression() != null) { + tokens.addAll(visit(ctx.scalar_expression())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitScalar_expression(JpqlParser.Scalar_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.arithmetic_expression() != null) { + tokens.addAll(visit(ctx.arithmetic_expression())); + } else if (ctx.string_expression() != null) { + tokens.addAll(visit(ctx.string_expression())); + } else if (ctx.enum_expression() != null) { + tokens.addAll(visit(ctx.enum_expression())); + } else if (ctx.datetime_expression() != null) { + tokens.addAll(visit(ctx.datetime_expression())); + } else if (ctx.boolean_expression() != null) { + tokens.addAll(visit(ctx.boolean_expression())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.entity_type_expression() != null) { + tokens.addAll(visit(ctx.entity_type_expression())); + } + + return tokens; + } + + @Override + public List visitConditional_expression(JpqlParser.Conditional_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.conditional_expression() != null) { + tokens.addAll(visit(ctx.conditional_expression())); + tokens.add(new JpaQueryParsingToken(ctx.OR())); + tokens.addAll(visit(ctx.conditional_term())); + } else { + tokens.addAll(visit(ctx.conditional_term())); + } + + return tokens; + } + + @Override + public List visitConditional_term(JpqlParser.Conditional_termContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.conditional_term() != null) { + tokens.addAll(visit(ctx.conditional_term())); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.conditional_factor())); + } else { + tokens.addAll(visit(ctx.conditional_factor())); + } + + return tokens; + } + + @Override + public List visitConditional_factor(JpqlParser.Conditional_factorContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + JpqlParser.Conditional_primaryContext conditionalPrimary = ctx.conditional_primary(); + List visitedConditionalPrimary = visit(conditionalPrimary); + tokens.addAll(visitedConditionalPrimary); + + return tokens; + } + + @Override + public List visitConditional_primary(JpqlParser.Conditional_primaryContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.simple_cond_expression() != null) { + tokens.addAll(visit(ctx.simple_cond_expression())); + } else if (ctx.conditional_expression() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.conditional_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitSimple_cond_expression(JpqlParser.Simple_cond_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.comparison_expression() != null) { + tokens.addAll(visit(ctx.comparison_expression())); + } else if (ctx.between_expression() != null) { + tokens.addAll(visit(ctx.between_expression())); + } else if (ctx.in_expression() != null) { + tokens.addAll(visit(ctx.in_expression())); + } else if (ctx.like_expression() != null) { + tokens.addAll(visit(ctx.like_expression())); + } else if (ctx.null_comparison_expression() != null) { + tokens.addAll(visit(ctx.null_comparison_expression())); + } else if (ctx.empty_collection_comparison_expression() != null) { + tokens.addAll(visit(ctx.empty_collection_comparison_expression())); + } else if (ctx.collection_member_expression() != null) { + tokens.addAll(visit(ctx.collection_member_expression())); + } else if (ctx.exists_expression() != null) { + tokens.addAll(visit(ctx.exists_expression())); + } + + return tokens; + } + + @Override + public List visitBetween_expression(JpqlParser.Between_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.arithmetic_expression(0) != null) { + + tokens.addAll(visit(ctx.arithmetic_expression(0))); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); + tokens.addAll(visit(ctx.arithmetic_expression(1))); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.arithmetic_expression(2))); + + } else if (ctx.string_expression(0) != null) { + + tokens.addAll(visit(ctx.string_expression(0))); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); + tokens.addAll(visit(ctx.string_expression(1))); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.string_expression(2))); + + } else if (ctx.datetime_expression(0) != null) { + + tokens.addAll(visit(ctx.datetime_expression(0))); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); + tokens.addAll(visit(ctx.datetime_expression(1))); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.datetime_expression(2))); + } + + return tokens; + } + + @Override + public List visitIn_expression(JpqlParser.In_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } + if (ctx.type_discriminator() != null) { + tokens.addAll(visit(ctx.type_discriminator())); + } + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + if (ctx.IN() != null) { + tokens.add(new JpaQueryParsingToken(ctx.IN())); + } + + if (ctx.in_item() != null && !ctx.in_item().isEmpty()) { + + tokens.add(TOKEN_OPEN_PAREN); + + ctx.in_item().forEach(inItemContext -> { + + tokens.addAll(visit(inItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.collection_valued_input_parameter() != null) { + tokens.addAll(visit(ctx.collection_valued_input_parameter())); + } + + return tokens; + } + + @Override + public List visitIn_item(JpqlParser.In_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.literal() != null) { + tokens.addAll(visit(ctx.literal())); + } else if (ctx.single_valued_input_parameter() != null) { + tokens.addAll(visit(ctx.single_valued_input_parameter())); + } + + return tokens; + } + + @Override + public List visitLike_expression(JpqlParser.Like_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.string_expression())); + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.LIKE())); + tokens.addAll(visit(ctx.pattern_value())); + + return tokens; + } + + @Override + public List visitNull_comparison_expression(JpqlParser.Null_comparison_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_path_expression())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } + + tokens.add(new JpaQueryParsingToken(ctx.IS())); + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.NULL())); + + return tokens; + } + + @Override + public List visitEmpty_collection_comparison_expression( + JpqlParser.Empty_collection_comparison_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.collection_valued_path_expression())); + tokens.add(new JpaQueryParsingToken(ctx.IS())); + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.EMPTY())); + + return tokens; + } + + @Override + public List visitCollection_member_expression( + JpqlParser.Collection_member_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.entity_or_value_expression())); + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.MEMBER())); + if (ctx.OF() != null) { + tokens.add(new JpaQueryParsingToken(ctx.OF())); + } + tokens.addAll(visit(ctx.collection_valued_path_expression())); + + return tokens; + } + + @Override + public List visitEntity_or_value_expression(JpqlParser.Entity_or_value_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } else if (ctx.state_field_path_expression() != null) { + tokens.addAll(visit(ctx.state_field_path_expression())); + } else if (ctx.simple_entity_or_value_expression() != null) { + tokens.addAll(visit(ctx.simple_entity_or_value_expression())); + } + + return tokens; + } + + @Override + public List visitSimple_entity_or_value_expression( + JpqlParser.Simple_entity_or_value_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.literal() != null) { + tokens.addAll(visit(ctx.literal())); + } + + return tokens; + } + + @Override + public List visitExists_expression(JpqlParser.Exists_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.EXISTS())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitAll_or_any_expression(JpqlParser.All_or_any_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.ALL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ALL())); + } else if (ctx.ANY() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ANY())); + } else if (ctx.SOME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.SOME())); + } + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitComparison_expression(JpqlParser.Comparison_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (!ctx.string_expression().isEmpty()) { + + tokens.addAll(visit(ctx.string_expression(0))); + tokens.addAll(visit(ctx.comparison_operator())); + + if (ctx.string_expression(1) != null) { + tokens.addAll(visit(ctx.string_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + } else if (!ctx.boolean_expression().isEmpty()) { + + tokens.addAll(visit(ctx.boolean_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + + if (ctx.boolean_expression(1) != null) { + tokens.addAll(visit(ctx.boolean_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + } else if (!ctx.enum_expression().isEmpty()) { + + tokens.addAll(visit(ctx.enum_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + + if (ctx.enum_expression(1) != null) { + tokens.addAll(visit(ctx.enum_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + } else if (!ctx.datetime_expression().isEmpty()) { + + tokens.addAll(visit(ctx.datetime_expression(0))); + tokens.addAll(visit(ctx.comparison_operator())); + + if (ctx.datetime_expression(1) != null) { + tokens.addAll(visit(ctx.datetime_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + } else if (!ctx.entity_expression().isEmpty()) { + + tokens.addAll(visit(ctx.entity_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + + if (ctx.entity_expression(1) != null) { + tokens.addAll(visit(ctx.entity_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + } else if (!ctx.arithmetic_expression().isEmpty()) { + + tokens.addAll(visit(ctx.arithmetic_expression(0))); + tokens.addAll(visit(ctx.comparison_operator())); + + if (ctx.arithmetic_expression(1) != null) { + tokens.addAll(visit(ctx.arithmetic_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + } else if (!ctx.entity_type_expression().isEmpty()) { + + tokens.addAll(visit(ctx.entity_type_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + tokens.addAll(visit(ctx.entity_type_expression(1))); + } + + return tokens; + } + + @Override + public List visitComparison_operator(JpqlParser.Comparison_operatorContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.op)); + } + + @Override + public List visitArithmetic_expression(JpqlParser.Arithmetic_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.arithmetic_expression() != null) { + + tokens.addAll(visit(ctx.arithmetic_expression())); + tokens.add(new JpaQueryParsingToken(ctx.op)); + tokens.addAll(visit(ctx.arithmetic_term())); + + } else { + tokens.addAll(visit(ctx.arithmetic_term())); + } + + return tokens; + } + + @Override + public List visitArithmetic_term(JpqlParser.Arithmetic_termContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.arithmetic_term() != null) { + + tokens.addAll(visit(ctx.arithmetic_term())); + NOSPACE(tokens); + tokens.add(new JpaQueryParsingToken(ctx.op, false)); + tokens.addAll(visit(ctx.arithmetic_factor())); + } else { + tokens.addAll(visit(ctx.arithmetic_factor())); + } + + return tokens; + } + + @Override + public List visitArithmetic_factor(JpqlParser.Arithmetic_factorContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.op != null) { + tokens.add(new JpaQueryParsingToken(ctx.op)); + } + tokens.addAll(visit(ctx.arithmetic_primary())); + + return tokens; + } + + @Override + public List visitArithmetic_primary(JpqlParser.Arithmetic_primaryContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.numeric_literal() != null) { + tokens.addAll(visit(ctx.numeric_literal())); + } else if (ctx.arithmetic_expression() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.functions_returning_numerics() != null) { + tokens.addAll(visit(ctx.functions_returning_numerics())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitString_expression(JpqlParser.String_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.string_literal() != null) { + tokens.addAll(visit(ctx.string_literal())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.functions_returning_strings() != null) { + tokens.addAll(visit(ctx.functions_returning_strings())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitDatetime_expression(JpqlParser.Datetime_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.functions_returning_datetime() != null) { + tokens.addAll(visit(ctx.functions_returning_datetime())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.date_time_timestamp_literal() != null) { + tokens.addAll(visit(ctx.date_time_timestamp_literal())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitBoolean_expression(JpqlParser.Boolean_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.boolean_literal() != null) { + tokens.addAll(visit(ctx.boolean_literal())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitEnum_expression(JpqlParser.Enum_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.enum_literal() != null) { + tokens.addAll(visit(ctx.enum_literal())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitEntity_expression(JpqlParser.Entity_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } else if (ctx.simple_entity_expression() != null) { + tokens.addAll(visit(ctx.simple_entity_expression())); + } + + return tokens; + } + + @Override + public List visitSimple_entity_expression(JpqlParser.Simple_entity_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } + + return tokens; + } + + @Override + public List visitEntity_type_expression(JpqlParser.Entity_type_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.type_discriminator() != null) { + tokens.addAll(visit(ctx.type_discriminator())); + } else if (ctx.entity_type_literal() != null) { + tokens.addAll(visit(ctx.entity_type_literal())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } + + return tokens; + } + + @Override + public List visitType_discriminator(JpqlParser.Type_discriminatorContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.TYPE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.general_identification_variable() != null) { + tokens.addAll(visit(ctx.general_identification_variable())); + } else if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitFunctions_returning_numerics( + JpqlParser.Functions_returning_numericsContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.LENGTH() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LENGTH(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.LOCATE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LOCATE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.string_expression(1))); + NOSPACE(tokens); + if (ctx.arithmetic_expression() != null) { + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + } + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.ABS() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ABS(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.CEILING() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.CEILING(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.EXP() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.EXP(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.FLOOR() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.FLOOR(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.LN() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LN(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.SIGN() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.SIGN(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.SQRT() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.SQRT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.MOD() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.MOD(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.arithmetic_expression(1))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.POWER() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.POWER(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.arithmetic_expression(1))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.ROUND() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ROUND(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.arithmetic_expression(1))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.SIZE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.SIZE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.collection_valued_path_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.INDEX() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.INDEX(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitFunctions_returning_datetime( + JpqlParser.Functions_returning_datetimeContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.CURRENT_DATE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_DATE())); + } else if (ctx.CURRENT_TIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_TIME())); + } else if (ctx.CURRENT_TIMESTAMP() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_TIMESTAMP())); + } else if (ctx.LOCAL() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LOCAL())); + + if (ctx.DATE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DATE())); + } else if (ctx.TIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.TIME())); + } else if (ctx.DATETIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DATETIME())); + } + } + + return tokens; + } + + @Override + public List visitFunctions_returning_strings( + JpqlParser.Functions_returning_stringsContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.CONCAT() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.CONCAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + ctx.string_expression().forEach(stringExpressionContext -> { + tokens.addAll(visit(stringExpressionContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.SUBSTRING() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.SUBSTRING(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + ctx.arithmetic_expression().forEach(arithmeticExpressionContext -> { + tokens.addAll(visit(arithmeticExpressionContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.TRIM() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TRIM(), false)); + tokens.add(TOKEN_OPEN_PAREN); + if (ctx.trim_specification() != null) { + tokens.addAll(visit(ctx.trim_specification())); + } + if (ctx.trim_character() != null) { + tokens.addAll(visit(ctx.trim_character())); + } + if (ctx.FROM() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + } + tokens.addAll(visit(ctx.string_expression(0))); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.LOWER() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LOWER(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.UPPER() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.UPPER(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitTrim_specification(JpqlParser.Trim_specificationContext ctx) { + + if (ctx.LEADING() != null) { + return List.of(new JpaQueryParsingToken(ctx.LEADING())); + } else if (ctx.TRAILING() != null) { + return List.of(new JpaQueryParsingToken(ctx.TRAILING())); + } else { + return List.of(new JpaQueryParsingToken(ctx.BOTH())); + } + } + + @Override + public List visitFunction_invocation(JpqlParser.Function_invocationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.FUNCTION(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.function_name())); + NOSPACE(tokens); + ctx.function_arg().forEach(functionArgContext -> { + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(functionArgContext)); + NOSPACE(tokens); + }); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitExtract_datetime_field(JpqlParser.Extract_datetime_fieldContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.EXTRACT())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.datetime_field())); + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + tokens.addAll(visit(ctx.datetime_expression())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitDatetime_field(JpqlParser.Datetime_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitExtract_datetime_part(JpqlParser.Extract_datetime_partContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.EXTRACT())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.datetime_part())); + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + tokens.addAll(visit(ctx.datetime_expression())); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitDatetime_part(JpqlParser.Datetime_partContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitFunction_arg(JpqlParser.Function_argContext ctx) { + + if (ctx.literal() != null) { + return visit(ctx.literal()); + } else if (ctx.state_valued_path_expression() != null) { + return visit(ctx.state_valued_path_expression()); + } else if (ctx.input_parameter() != null) { + return visit(ctx.input_parameter()); + } else { + return visit(ctx.scalar_expression()); + } + } + + @Override + public List visitCase_expression(JpqlParser.Case_expressionContext ctx) { + + if (ctx.general_case_expression() != null) { + return visit(ctx.general_case_expression()); + } else if (ctx.simple_case_expression() != null) { + return visit(ctx.simple_case_expression()); + } else if (ctx.coalesce_expression() != null) { + return visit(ctx.coalesce_expression()); + } else { + return visit(ctx.nullif_expression()); + } + } + + @Override + public List visitGeneral_case_expression(JpqlParser.General_case_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CASE())); + + ctx.when_clause().forEach(whenClauseContext -> { + tokens.addAll(visit(whenClauseContext)); + }); + + tokens.add(new JpaQueryParsingToken(ctx.ELSE())); + tokens.addAll(visit(ctx.scalar_expression())); + tokens.add(new JpaQueryParsingToken(ctx.END())); + + return tokens; + } + + @Override + public List visitWhen_clause(JpqlParser.When_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHEN())); + tokens.addAll(visit(ctx.conditional_expression())); + tokens.add(new JpaQueryParsingToken(ctx.THEN())); + tokens.addAll(visit(ctx.scalar_expression())); + + return tokens; + } + + @Override + public List visitSimple_case_expression(JpqlParser.Simple_case_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CASE())); + tokens.addAll(visit(ctx.case_operand())); + + ctx.simple_when_clause().forEach(simpleWhenClauseContext -> { + tokens.addAll(visit(simpleWhenClauseContext)); + }); + + tokens.add(new JpaQueryParsingToken(ctx.ELSE())); + tokens.addAll(visit(ctx.scalar_expression())); + tokens.add(new JpaQueryParsingToken(ctx.END())); + + return tokens; + } + + @Override + public List visitCase_operand(JpqlParser.Case_operandContext ctx) { + + if (ctx.state_valued_path_expression() != null) { + return visit(ctx.state_valued_path_expression()); + } else { + return visit(ctx.type_discriminator()); + } + } + + @Override + public List visitSimple_when_clause(JpqlParser.Simple_when_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHEN())); + tokens.addAll(visit(ctx.scalar_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.THEN())); + tokens.addAll(visit(ctx.scalar_expression(1))); + + return tokens; + } + + @Override + public List visitCoalesce_expression(JpqlParser.Coalesce_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.COALESCE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + ctx.scalar_expression().forEach(scalarExpressionContext -> { + tokens.addAll(visit(scalarExpressionContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitNullif_expression(JpqlParser.Nullif_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.NULLIF())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.scalar_expression(0))); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.scalar_expression(1))); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitTrim_character(JpqlParser.Trim_characterContext ctx) { + + if (ctx.CHARACTER() != null) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } else if (ctx.character_valued_input_parameter() != null) { + return visit(ctx.character_valued_input_parameter()); + } else { + return List.of(); + } + } + + @Override + public List visitIdentification_variable(JpqlParser.Identification_variableContext ctx) { + + if (ctx.IDENTIFICATION_VARIABLE() != null) { + return List.of(new JpaQueryParsingToken(ctx.IDENTIFICATION_VARIABLE())); + } else if (ctx.COUNT() != null) { + return List.of(new JpaQueryParsingToken(ctx.COUNT())); + } else if (ctx.ORDER() != null) { + return List.of(new JpaQueryParsingToken(ctx.ORDER())); + } else if (ctx.KEY() != null) { + return List.of(new JpaQueryParsingToken(ctx.KEY())); + } else if (ctx.spel_expression() != null) { + return visit(ctx.spel_expression()); + } else { + return List.of(); + } + } + + @Override + public List visitConstructor_name(JpqlParser.Constructor_nameContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.state_field_path_expression())); + NOSPACE(tokens); + + return tokens; + } + + @Override + public List visitLiteral(JpqlParser.LiteralContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.STRINGLITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.STRINGLITERAL())); + } else if (ctx.INTLITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INTLITERAL())); + } else if (ctx.FLOATLITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FLOATLITERAL())); + } else if (ctx.boolean_literal() != null) { + tokens.addAll(visit(ctx.boolean_literal())); + } else if (ctx.entity_type_literal() != null) { + tokens.addAll(visit(ctx.entity_type_literal())); + } + + return tokens; + } + + @Override + public List visitInput_parameter(JpqlParser.Input_parameterContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.INTLITERAL() != null) { + + tokens.add(TOKEN_QUESTION_MARK); + tokens.add(new JpaQueryParsingToken(ctx.INTLITERAL())); + } else if (ctx.identification_variable() != null) { + + tokens.add(TOKEN_COLON); + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitPattern_value(JpqlParser.Pattern_valueContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.string_expression())); + + return tokens; + } + + @Override + public List visitDate_time_timestamp_literal( + JpqlParser.Date_time_timestamp_literalContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.STRINGLITERAL())); + } + + @Override + public List visitEntity_type_literal(JpqlParser.Entity_type_literalContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitEscape_character(JpqlParser.Escape_characterContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } + + @Override + public List visitNumeric_literal(JpqlParser.Numeric_literalContext ctx) { + + if (ctx.INTLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.INTLITERAL())); + } else if (ctx.FLOATLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.FLOATLITERAL())); + } else { + return List.of(); + } + } + + @Override + public List visitBoolean_literal(JpqlParser.Boolean_literalContext ctx) { + + if (ctx.TRUE() != null) { + return List.of(new JpaQueryParsingToken(ctx.TRUE())); + } else if (ctx.FALSE() != null) { + return List.of(new JpaQueryParsingToken(ctx.FALSE())); + } else { + return List.of(); + } + } + + @Override + public List visitEnum_literal(JpqlParser.Enum_literalContext ctx) { + return visit(ctx.state_field_path_expression()); + } + + @Override + public List visitString_literal(JpqlParser.String_literalContext ctx) { + + if (ctx.CHARACTER() != null) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } else if (ctx.STRINGLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.STRINGLITERAL())); + } else { + return List.of(); + } + } + + @Override + public List visitSingle_valued_embeddable_object_field( + JpqlParser.Single_valued_embeddable_object_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitSubtype(JpqlParser.SubtypeContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitCollection_valued_field(JpqlParser.Collection_valued_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitSingle_valued_object_field(JpqlParser.Single_valued_object_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitState_field(JpqlParser.State_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitCollection_value_field(JpqlParser.Collection_value_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitEntity_name(JpqlParser.Entity_nameContext ctx) { + + List tokens = new ArrayList<>(); + + ctx.identification_variable().forEach(identificationVariableContext -> { + tokens.addAll(visit(identificationVariableContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitResult_variable(JpqlParser.Result_variableContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitSuperquery_identification_variable( + JpqlParser.Superquery_identification_variableContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitCollection_valued_input_parameter( + JpqlParser.Collection_valued_input_parameterContext ctx) { + return visit(ctx.input_parameter()); + } + + @Override + public List visitSingle_valued_input_parameter( + JpqlParser.Single_valued_input_parameterContext ctx) { + return visit(ctx.input_parameter()); + } + + @Override + public List visitFunction_name(JpqlParser.Function_nameContext ctx) { + return visit(ctx.string_literal()); + } + + @Override + public List visitSpel_expression(JpqlParser.Spel_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.prefix.equals("#{#")) { // #{#entityName} + + tokens.add(new JpaQueryParsingToken(ctx.prefix)); + ctx.identification_variable().forEach(identificationVariableContext -> { + tokens.addAll(visit(identificationVariableContext)); + tokens.add(TOKEN_DOT); + }); + CLIP(tokens); + tokens.add(TOKEN_CLOSE_BRACE); + + } else if (ctx.prefix.equals("#{#[")) { // #{[0]} + + tokens.add(new JpaQueryParsingToken(ctx.prefix)); + tokens.add(new JpaQueryParsingToken(ctx.INTLITERAL())); + tokens.add(TOKEN_CLOSE_SQUARE_BRACKET_BRACE); + + } else if (ctx.prefix.equals("#{")) {// #{escape([0])} or #{escape('foo')} + + tokens.add(new JpaQueryParsingToken(ctx.prefix)); + tokens.addAll(visit(ctx.identification_variable(0))); + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.string_literal() != null) { + tokens.addAll(visit(ctx.string_literal())); + } else if (ctx.INTLITERAL() != null) { + + tokens.add(TOKEN_OPEN_SQUARE_BRACKET); + tokens.add(new JpaQueryParsingToken(ctx.INTLITERAL())); + tokens.add(TOKEN_CLOSE_SQUARE_BRACKET); + } + + tokens.add(TOKEN_CLOSE_PAREN_BRACE); + } + + return tokens; + } + + @Override + public List visitCharacter_valued_input_parameter( + JpqlParser.Character_valued_input_parameterContext ctx) { + + if (ctx.CHARACTER() != null) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } else if (ctx.input_parameter() != null) { + return visit(ctx.input_parameter()); + } else { + return List.of(); + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java new file mode 100644 index 0000000000..fe030c598a --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java @@ -0,0 +1,229 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; + +/** + * An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed JPQL query. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpqlQueryTransformer extends JpqlQueryRenderer { + + @Nullable private Sort sort; + private boolean countQuery; + + @Nullable private String countProjection; + + @Nullable private String alias = null; + + private List projection = null; + + private boolean hasConstructorExpression = false; + + JpqlQueryTransformer() { + this(null, false, null); + } + + JpqlQueryTransformer(@Nullable Sort sort) { + this(sort, false, null); + } + + JpqlQueryTransformer(boolean countQuery, @Nullable String countProjection) { + this(null, countQuery, countProjection); + } + + private JpqlQueryTransformer(@Nullable Sort sort, boolean countQuery, @Nullable String countProjection) { + + this.sort = sort; + this.countQuery = countQuery; + this.countProjection = countProjection; + } + + @Nullable + public String getAlias() { + return this.alias; + } + + public List getProjection() { + return this.projection; + } + + public boolean hasConstructorExpression() { + return this.hasConstructorExpression; + } + + @Override + public List visitSelect_statement(JpqlParser.Select_statementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.select_clause())); + tokens.addAll(visit(ctx.from_clause())); + + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + + if (ctx.groupby_clause() != null) { + tokens.addAll(visit(ctx.groupby_clause())); + } + + if (ctx.having_clause() != null) { + tokens.addAll(visit(ctx.having_clause())); + } + + if (!countQuery) { + + if (ctx.orderby_clause() != null) { + tokens.addAll(visit(ctx.orderby_clause())); + } + + if (this.sort != null && this.sort.isSorted()) { + + if (ctx.orderby_clause() != null) { + + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + } else { + + SPACE(tokens); + tokens.add(TOKEN_ORDER_BY); + } + + this.sort.forEach(order -> { + + JpaQueryParser.checkSortExpression(order); + + if (order.isIgnoreCase()) { + tokens.add(TOKEN_LOWER_FUNC); + } + tokens.add(new JpaQueryParsingToken(() -> { + + if (order.getProperty().contains("(")) { + return order.getProperty(); + } + + return this.alias + "." + order.getProperty(); + }, true)); + if (order.isIgnoreCase()) { + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + tokens.add(order.isDescending() ? TOKEN_DESC : TOKEN_ASC); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + } + } + + return tokens; + } + + @Override + public List visitSelect_clause(JpqlParser.Select_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SELECT())); + + if (countQuery) { + tokens.add(TOKEN_COUNT_FUNC); + } + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + List selectItemTokens = new ArrayList<>(); + + ctx.select_item().forEach(selectItemContext -> { + selectItemTokens.addAll(visit(selectItemContext)); + NOSPACE(selectItemTokens); + selectItemTokens.add(TOKEN_COMMA); + }); + CLIP(selectItemTokens); + SPACE(selectItemTokens); + + if (countQuery) { + + if (countProjection != null) { + tokens.add(new JpaQueryParsingToken(countProjection)); + } else { + + if (ctx.DISTINCT() != null) { + + if (selectItemTokens.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) { + // constructor + tokens.add(new JpaQueryParsingToken(() -> this.alias)); + } else { + // keep all the select items to distinct against + tokens.addAll(selectItemTokens); + } + } else { + tokens.add(new JpaQueryParsingToken(() -> this.alias)); + } + } + + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else { + tokens.addAll(selectItemTokens); + } + + if (projection == null) { + this.projection = selectItemTokens; + } + + return tokens; + } + + @Override + public List visitRange_variable_declaration(JpqlParser.Range_variable_declarationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.entity_name())); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + tokens.addAll(visit(ctx.identification_variable())); + + if (this.alias == null) { + this.alias = tokens.get(tokens.size() - 1).getToken(); + } + + return tokens; + } + + @Override + public List visitConstructor_expression(JpqlParser.Constructor_expressionContext ctx) { + + this.hasConstructorExpression = true; + + return super.visitConstructor_expression(ctx); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java index d329251649..e74952e134 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java @@ -31,6 +31,8 @@ public final class QueryEnhancerFactory { private static final boolean JSQLPARSER_IN_CLASSPATH = isJSqlParserInClassPath(); + private static final boolean HIBERNATE_IN_CLASSPATH = isHibernateInClassPath(); + private QueryEnhancerFactory() {} /** @@ -41,10 +43,25 @@ private QueryEnhancerFactory() {} */ public static QueryEnhancer forQuery(DeclaredQuery query) { - if (qualifiesForJSqlParserUsage(query)) { - return new JSqlParserQueryEnhancer(query); + if (query.isNativeQuery()) { + + if (qualifiesForJSqlParserUsage(query)) { + /** + * If JSqlParser fails, throw some alert signaling that people should write a custom Impl. + */ + return new JSqlParserQueryEnhancer(query); + } else { + return new DefaultQueryEnhancer(query); + } } else { - return new DefaultQueryEnhancer(query); + + if (qualifiedForHqlParserUsage(query)) { + return new JpaQueryParsingEnhancer(new HqlQueryParser(query)); + } else if (qualifiesForJpqlParserUsage(query)) { + return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)); + } else { + return new DefaultQueryEnhancer(query); + } } } @@ -52,13 +69,33 @@ public static QueryEnhancer forQuery(DeclaredQuery query) { * Checks if a given query can be process with the JSqlParser under the condition that the parser is in the classpath. * * @param query the query we want to check - * @return true if JSqlParser is in the classpath and the query is classified as a native query otherwise - * false + * @return true if JSqlParser is in the classpath and the query is classified as a native query and not + * to be bypassed otherwise false */ private static boolean qualifiesForJSqlParserUsage(DeclaredQuery query) { return JSQLPARSER_IN_CLASSPATH && query.isNativeQuery(); } + /** + * Checks if the query is a candidate for the HQL parser. + * + * @param query the query we want to check + * @return true if Hibernate is in the classpath and the query is NOT classified as native + */ + private static boolean qualifiedForHqlParserUsage(DeclaredQuery query) { + return HIBERNATE_IN_CLASSPATH && !query.isNativeQuery(); + } + + /** + * Checks if the query is a candidate for the JPQL spec parser. + * + * @param query the query we want to check + * @return true if the query is NOT classified as a native query + */ + private static boolean qualifiesForJpqlParserUsage(DeclaredQuery query) { + return !query.isNativeQuery(); + } + /** * Checks whether JSqlParser is in classpath or not. * @@ -74,4 +111,15 @@ private static boolean isJSqlParserInClassPath() { return false; } } + + private static boolean isHibernateInClassPath() { + + try { + Class.forName("org.hibernate.query.TypedParameterValue", false, QueryEnhancerFactory.class.getClassLoader()); + LOG.info("Hibernate is in classpath; If applicable Hql61Parser will be used."); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 32b5e24301..84a23fa6cd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -15,9 +15,8 @@ */ package org.springframework.data.jpa.repository.query; -import static java.util.regex.Pattern.CASE_INSENSITIVE; -import static org.springframework.util.ObjectUtils.nullSafeEquals; -import static org.springframework.util.ObjectUtils.nullSafeHashCode; +import static java.util.regex.Pattern.*; +import static org.springframework.util.ObjectUtils.*; import java.lang.reflect.Array; import java.util.ArrayList; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 721256d614..4d5522244a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -15,10 +15,8 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.data.domain.Sort.Direction.ASC; -import static org.springframework.data.domain.Sort.Direction.DESC; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.domain.Sort.Direction.*; import jakarta.persistence.EntityManager; @@ -27,6 +25,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -234,6 +233,7 @@ void parametersForContainsGetProperlyEscaped() { .isEmpty(); } + @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1519 void escapingInLikeSpels() { @@ -244,6 +244,7 @@ void escapingInLikeSpels() { assertThat(userRepository.findContainingEscaped("att_")).containsExactly(extra); } + @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { @@ -253,6 +254,7 @@ void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { assertThat(userRepository.findContainingEscaped("att\\x")).containsExactly(withEscapeCharacter); } + @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapedWildcards() { 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 b450692a7d..9697e0beb3 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 @@ -16,21 +16,13 @@ package org.springframework.data.jpa.repository; import static java.util.Arrays.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.*; import static org.springframework.data.domain.Example.*; -import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; -import static org.springframework.data.domain.ExampleMatcher.StringMatcher; -import static org.springframework.data.domain.ExampleMatcher.matching; +import static org.springframework.data.domain.ExampleMatcher.*; import static org.springframework.data.domain.Sort.Direction.*; import static org.springframework.data.jpa.domain.Specification.*; import static org.springframework.data.jpa.domain.Specification.not; -import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasAgeLess; -import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstname; -import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstnameLike; -import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasLastname; -import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasLastnameLikeWithSort; +import static org.springframework.data.jpa.domain.sample.UserSpecifications.*; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -2710,19 +2702,22 @@ void handlesColonsFollowedByIntegerInStringLiteral() { assertThat(users).extracting(User::getId).containsExactly(expected.getId()); } + @Disabled("ORDER BY CASE appears to be a Hibernate-only feature") @Test // DATAJPA-1233 void handlesCountQueriesWithLessParametersSingleParam() { - repository.findAllOrderedBySpecialNameSingleParam("Oliver", PageRequest.of(2, 3)); + // repository.findAllOrderedBySpecialNameSingleParam("Oliver", PageRequest.of(2, 3)); } + @Disabled("ORDER BY CASE appears to be a Hibernate-only feature") @Test // DATAJPA-1233 void handlesCountQueriesWithLessParametersMoreThanOne() { - repository.findAllOrderedBySpecialNameMultipleParams("Oliver", "x", PageRequest.of(2, 3)); + // repository.findAllOrderedBySpecialNameMultipleParams("Oliver", "x", PageRequest.of(2, 3)); } + @Disabled("ORDER BY CASE appears to be a Hibernate-only feature") @Test // DATAJPA-1233 void handlesCountQueriesWithLessParametersMoreThanOneIndexed() { - repository.findAllOrderedBySpecialNameMultipleParamsIndexed("x", "Oliver", PageRequest.of(2, 3)); + // repository.findAllOrderedBySpecialNameMultipleParamsIndexed("x", "Oliver", PageRequest.of(2, 3)); } // DATAJPA-928 @@ -2946,12 +2941,12 @@ void deleteWithSpec() { @Test // GH-2045, GH-425 public void correctlyBuildSortClauseWhenSortingByFunctionAliasAndFunctionContainsPositionalParameters() { - repository.findAllAndSortByFunctionResultPositionalParameter("prefix", "suffix", Sort.by("idWithPrefixAndSuffix")); + repository.findAllAndSortByFunctionResultPositionalParameter("prefix", "suffix", Sort.by("id")); } @Test // GH-2045, GH-425 public void correctlyBuildSortClauseWhenSortingByFunctionAliasAndFunctionContainsNamedParameters() { - repository.findAllAndSortByFunctionResultNamedParameter("prefix", "suffix", Sort.by("idWithPrefixAndSuffix")); + repository.findAllAndSortByFunctionResultNamedParameter("prefix", "suffix", Sort.by("id")); } @Test // GH-2578 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index 874656158a..2678f9e2e6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -43,21 +44,22 @@ class ExpressionBasedStringQueryUnitTests { private static final SpelExpressionParser SPEL_PARSER = new SpelExpressionParser(); @Mock JpaEntityMetadata metadata; + @BeforeEach + void setUp() { + when(metadata.getEntityName()).thenReturn("User"); + } + @Test // DATAJPA-170 void shouldReturnQueryWithDomainTypeExpressionReplacedWithSimpleDomainTypeName() { - when(metadata.getEntityName()).thenReturn("User"); - - String source = "select from #{#entityName} u where u.firstname like :firstname"; + String source = "select u from #{#entityName} u where u.firstname like :firstname"; StringQuery query = new ExpressionBasedStringQuery(source, metadata, SPEL_PARSER, false); - assertThat(query.getQueryString()).isEqualTo("select from User u where u.firstname like :firstname"); + assertThat(query.getQueryString()).isEqualTo("select u from User u where u.firstname like :firstname"); } @Test // DATAJPA-424 void renderAliasInExpressionQueryCorrectly() { - when(metadata.getEntityName()).thenReturn("User"); - StringQuery query = new ExpressionBasedStringQuery("select u from #{#entityName} u", metadata, SPEL_PARSER, true); assertThat(query.getAlias()).isEqualTo("u"); assertThat(query.getQueryString()).isEqualTo("select u from User u"); @@ -67,10 +69,10 @@ void renderAliasInExpressionQueryCorrectly() { void shouldDetectBindParameterCountCorrectly() { StringQuery query = new ExpressionBasedStringQuery( - "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',:#{#networkRequest.name},'%')), '')) OR :#{#networkRequest.name} IS NULL )\"\n" - + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',:#{#networkRequest.server},'%')), '')) OR :#{#networkRequest.server} IS NULL)\"\n" - + "+ \"AND (n.createdAt >= :#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=:#{#networkRequest.createdTime.endDateTime})\"\n" - + "+ \"AND (n.updatedAt >= :#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=:#{#networkRequest.updatedTime.endDateTime})", + "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(:#{#networkRequest.name})) OR :#{#networkRequest.name} IS NULL " + + "AND (LOWER(n.server) LIKE LOWER(:#{#networkRequest.server})) OR :#{#networkRequest.server} IS NULL " + + "AND (n.createdAt >= :#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=:#{#networkRequest.createdTime.endDateTime}) " + + "AND (n.updatedAt >= :#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=:#{#networkRequest.updatedTime.endDateTime})", metadata, SPEL_PARSER, false); assertThat(query.getParameterBindings()).hasSize(8); @@ -80,10 +82,10 @@ void shouldDetectBindParameterCountCorrectly() { void shouldDetectBindParameterCountCorrectlyWithJDBCStyleParameters() { StringQuery query = new ExpressionBasedStringQuery( - "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )\"\n" - + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)\"\n" - + "+ \"AND (n.createdAt >= ?#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=?#{#networkRequest.createdTime.endDateTime})\"\n" - + "+ \"AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", + "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )" + + "AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)" + + "AND (n.createdAt >= ?#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=?#{#networkRequest.createdTime.endDateTime})" + + "AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", metadata, SPEL_PARSER, false); assertThat(query.getParameterBindings()).hasSize(8); @@ -93,10 +95,10 @@ void shouldDetectBindParameterCountCorrectlyWithJDBCStyleParameters() { void shouldDetectComplexNativeQueriesWithSpelAsNonNative() { StringQuery query = new ExpressionBasedStringQuery( - "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )\"\n" - + "+ \"AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)\"\n" - + "+ \"AND (n.createdAt >= ?#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=?#{#networkRequest.createdTime.endDateTime})\"\n" - + "+ \"AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", + "select n from #{#entityName} n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.name},'%')), '')) OR ?#{#networkRequest.name} IS NULL )" + + "AND (LOWER(n.server) LIKE LOWER(NULLIF(text(concat('%',?#{#networkRequest.server},'%')), '')) OR ?#{#networkRequest.server} IS NULL)" + + "AND (n.createdAt >= ?#{#networkRequest.createdTime.startDateTime}) AND (n.createdAt <=?#{#networkRequest.createdTime.endDateTime})" + + "AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", metadata, SPEL_PARSER, true); assertThat(query.isNativeQuery()).isFalse(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java new file mode 100644 index 0000000000..256f7af4fd --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assumptions.*; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * TCK Tests for {@link HqlQueryParser} mixed into {@link JpaQueryParsingEnhancer}. + * + * @author Greg Turnquist + * @since 3.1 + */ +public class HqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { + + public static final String HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES = "HqlParser does not support native queries"; + + @Override + QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { + return new JpaQueryParsingEnhancer(new HqlQueryParser(declaredQuery)); + } + + @Override + @ParameterizedTest // GH-2773 + @MethodSource("jpqlCountQueries") + void shouldDeriveJpqlCountQuery(String query, String expected) { + + assumeThat(query).as("HqlParser replaces the column name with alias name for count queries") // + .doesNotContain("SELECT name FROM table_name some_alias"); + + assumeThat(expected).as("HqlParser does turn 'select a.b' into 'select count(a.b)'") // + .doesNotContain("select count(a.b"); + + super.shouldDeriveJpqlCountQuery(query, expected); + } + + @Disabled(HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) + @Override + void findProjectionClauseWithIncludedFrom() {} + + @Disabled(HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) + @Override + void shouldDeriveNativeCountQuery(String query, String expected) {} + + @Disabled(HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) + @Override + void shouldDeriveNativeCountQueryWithVariable(String query, String expected) {} + +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java new file mode 100644 index 0000000000..2a1c9aff02 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -0,0 +1,1433 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Tests built around examples of HQL found in + * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc and + * https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#query-language
    + *
    + * IMPORTANT: Purely verifies the parser without any transformations. + * + * @author Greg Turnquist + * @since 3.1 + */ +class HqlQueryRendererTests { + + private static final String SPEC_FAULT = "Disabled due to spec fault> "; + + /** + * Parse the query using {@link HqlParser} then run it through the query-preserving {@link HqlQueryRenderer}. + * + * @param query + */ + private static String parseWithoutChanges(String query) { + + HqlLexer lexer = new HqlLexer(CharStreams.fromString(query)); + HqlParser parser = new HqlParser(new CommonTokenStream(lexer)); + + parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener()); + + HqlParser.StartContext parsedQuery = parser.start(); + + return render(new HqlQueryRenderer().visit(parsedQuery)); + } + + private void assertQuery(String query) { + + String slimmedDownQuery = reduceWhitespace(query); + assertThat(parseWithoutChanges(slimmedDownQuery)).isEqualTo(slimmedDownQuery); + } + + private String reduceWhitespace(String original) { + + return original // + .replaceAll("[ \\t\\n]{1,}", " ") // + .trim(); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + */ + @Test + void joinExample1() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order AS o JOIN o.lineItems AS l + WHERE l.shipped = FALSE + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables + */ + @Test + void joinExample2() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l JOIN l.product p + WHERE p.productType = 'office_supplies' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations + */ + @Test + void rangeVariableDeclarations() { + + assertQuery(""" + SELECT DISTINCT o1 + FROM Order o1, Order o2 + WHERE o1.quantity > o2.quantity AND + o2.customer.lastname = 'Smith' AND + o2.customer.firstname = 'John' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample1() { + + assertQuery(""" + SELECT i.name, VALUE(p) + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample2() { + + assertQuery(""" + SELECT i.name, p + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample3() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo.phones p + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample4() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE e.contactInfo.address.zipcode = '95054' + """); + } + + @Test + void pathExpressionSyntaxExample1() { + + assertQuery(""" + SELECT DISTINCT l.product + FROM Order AS o JOIN o.lineItems l + """); + } + + @Test + void joinsExample1() { + + assertQuery(""" + SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize + """); + } + + @Test + void joinsExample2() { + + assertQuery(""" + SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInnerExample() { + + assertQuery(""" + SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 + """); + } + + @Disabled("Deprecated syntax dating back to EJB-QL prior to EJB 3, required by JPA, never documented in Hibernate") + @Test + void joinsInExample() { + + assertQuery(""" + SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 + """); + } + + @Test + void doubleJoinExample() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE c.address.zipcode = '95054' + """); + } + + @Test + void leftJoinExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + GROUP BY s.name + """); + } + + @Test + void leftJoinOnExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + ON p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinWhereExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + WHERE p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinFetchExample() { + + assertQuery(""" + SELECT d + FROM Department d LEFT JOIN FETCH d.employees + WHERE d.deptno = 1 + """); + } + + @Test + void collectionMemberExample() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void collectionMemberInExample() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o , IN(o.lineItems) l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void fromClauseExample() { + + assertQuery(""" + SELECT o + FROM Order AS o JOIN o.lineItems l JOIN l.product p + """); + } + + @Test + void fromClauseDowncastingExample1() { + + assertQuery(""" + SELECT b.name, b.ISBN + FROM Order o JOIN TREAT(o.product AS Book) b + """); + } + + @Test + void fromClauseDowncastingExample2() { + + assertQuery(""" + SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp + WHERE lp.budget > 1000 + """); + } + + /** + * @see #fromClauseDowncastingExample3fixed() + */ + @Test + @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") + void fromClauseDowncastingExample3_SPEC_BUG() { + + assertQuery(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE "cost overrun" + """); + } + + @Test + void fromClauseDowncastingExample3fixed() { + + assertQuery(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE 'cost overrun' + """); + } + + @Test + void fromClauseDowncastingExample4() { + + assertQuery(""" + SELECT e FROM Employee e + WHERE TREAT(e AS Exempt).vacationDays > 10 + OR TREAT(e AS Contractor).hours > 100 + """); + } + + @Test + void pathExpressionsNamedParametersExample() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.status = :stat + """); + } + + @Test + void betweenExpressionsExample() { + + assertQuery(""" + SELECT t + FROM CreditCard c JOIN c.transactionHistory t + WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 + """); + } + + @Test + void isEmptyExample() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void memberOfExample() { + + assertQuery(""" + SELECT p + FROM Person p + WHERE 'Joe' MEMBER OF p.nicknames + """); + } + + @Test + void existsSubSelectExample1() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS (SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void allExample() { + + assertQuery(""" + SELECT emp + FROM Employee emp + WHERE emp.salary > ALL(SELECT m.salary + FROM Manager m + WHERE m.department = emp.department) + """); + } + + @Test + void existsSubSelectExample2() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS (SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void subselectNumericComparisonExample1() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 + """); + } + + @Test + void subselectNumericComparisonExample2() { + + assertQuery(""" + SELECT goodCustomer + FROM Customer goodCustomer + WHERE goodCustomer.balanceOwed < (SELECT AVG(c.balanceOwed)/2.0 FROM Customer c) + """); + } + + @Test + void indexExample() { + + assertQuery(""" + SELECT w.name + FROM Course c JOIN c.studentWaitlist w + WHERE c.name = 'Calculus' + AND INDEX(w) = 0 + """); + } + + /** + * @see #functionInvocationExampleWithCorrection() + */ + @Test + @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") + void functionInvocationExample_SPEC_BUG() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) + """); + } + + @Test + void functionInvocationExampleWithCorrection() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE + """); + } + + @Test + void updateCaseExample1() { + + assertQuery(""" + UPDATE Employee e + SET e.salary = + CASE WHEN e.rating = 1 THEN e.salary*1.1 + WHEN e.rating = 2 THEN e.salary*1.05 + ELSE e.salary*1.01 + END + """); + } + + @Test + void updateCaseExample2() { + + assertQuery(""" + UPDATE Employee e + SET e.salary = + CASE e.rating WHEN 1 THEN e.salary*1.1 + WHEN 2 THEN e.salary*1.05 + ELSE e.salary*1.01 + END + """); + } + + @Test + void selectCaseExample1() { + + assertQuery(""" + SELECT e.name, + CASE TYPE(e) WHEN Exempt THEN 'Exempt' + WHEN Contractor THEN 'Contractor' + WHEN Intern THEN 'Intern' + ELSE 'NonExempt' + END + FROM Employee e + WHERE e.dept.name = 'Engineering' + """); + } + + @Test + void selectCaseExample2() { + + assertQuery(""" + SELECT e.name, + f.name, + CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' + WHEN f.annualMiles > 25000 THEN 'Gold ' + ELSE '' + END, + 'Frequent Flyer') + FROM Employee e JOIN e.frequentFlierPlan f + """); + } + + @Test + void theRest() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (Exempt, Contractor) + """); + } + + @Test + void theRest2() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (:empType1, :empType2) + """); + } + + @Test + void theRest3() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN :empTypes + """); + } + + @Test + void theRest4() { + + assertQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) <> Exempt + """); + } + + @Test + void theRest5() { + + assertQuery(""" + SELECT c.status, AVG(c.filledOrderCount), COUNT(c) + FROM Customer c + GROUP BY c.status + HAVING c.status IN (1, 2) + """); + } + + @Test + void theRest6() { + + assertQuery(""" + SELECT c.country, COUNT(c) + FROM Customer c + GROUP BY c.country + HAVING COUNT(c) > 30 + """); + } + + @Test + void theRest7() { + + assertQuery(""" + SELECT c, COUNT(o) + FROM Customer c JOIN c.orders o + GROUP BY c + HAVING COUNT(o) >= 5 + """); + } + + @Test + void theRest8() { + + assertQuery(""" + SELECT c.id, c.status + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest9() { + + assertQuery(""" + SELECT v.location.street, KEY(i).title, VALUE(i) + FROM VideoStore v JOIN v.videoInventory i + WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 + """); + } + + @Test + void theRest10() { + + assertQuery(""" + SELECT o.lineItems FROM Order AS o + """); + } + + @Test + void theRest11() { + + assertQuery(""" + SELECT c, COUNT(l) AS itemCount + FROM Customer c JOIN c.Orders o JOIN o.lineItems l + WHERE c.address.state = 'CA' + GROUP BY c + ORDER BY itemCount + """); + } + + @Test + void theRest12() { + + assertQuery(""" + SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest13() { + + assertQuery(""" + SELECT e.address AS addr + FROM Employee e + """); + } + + @Test + void theRest14() { + + assertQuery(""" + SELECT AVG(o.quantity) FROM Order o + """); + } + + @Test + void theRest15() { + + assertQuery(""" + SELECT SUM(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest16() { + + assertQuery(""" + SELECT COUNT(o) FROM Order o + """); + } + + @Test + void theRest17() { + + assertQuery(""" + SELECT COUNT(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest18() { + + assertQuery(""" + SELECT COUNT(l) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL + """); + } + + @Test + void theRest19() { + + assertQuery(""" + SELECT o + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity DESC, o.totalcost + """); + } + + @Test + void theRest20() { + + assertQuery(""" + SELECT o.quantity, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity, a.zipcode + """); + } + + @Test + void theRest21() { + + assertQuery(""" + SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' AND a.county = 'Santa Clara' + ORDER BY o.quantity, taxedCost, a.zipcode + """); + } + + @Test + void theRest22() { + + assertQuery(""" + SELECT AVG(o.quantity) as q, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + GROUP BY a.zipcode + ORDER BY q DESC + """); + } + + @Test + void theRest23() { + + assertQuery(""" + SELECT p.product_name + FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY p.price + """); + } + + /** + * This query is specifically dubbed illegal in the spec, but apparently works with Hibernate. + */ + @Test + void theRest24() { + + assertQuery(""" + SELECT p.product_name + FROM Order o , IN(o.lineItems) l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY o.quantity + """); + } + + @Test + void theRest25() { + + assertQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + """); + } + + @Test + void theRest26() { + + assertQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + AND c.orders IS EMPTY + """); + } + + @Test + void theRest27() { + + assertQuery(""" + UPDATE Customer c + SET c.status = 'outstanding' + WHERE c.balance < 10000 + """); + } + + @Test + void theRest28() { + + assertQuery(""" + UPDATE Employee e + SET e.address.building = 22 + WHERE e.address.building = 14 + AND e.address.city = 'Santa Clara' + AND e.project = 'Jakarta EE' + """); + } + + @Test + void theRest29() { + + assertQuery(""" + SELECT o + FROM Order o + """); + } + + @Test + void theRest30() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.shippingAddress.state = 'CA' + """); + } + + @Test + void theRest31() { + + assertQuery(""" + SELECT DISTINCT o.shippingAddress.state + FROM Order o + """); + } + + @Test + void theRest32() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + """); + } + + @Test + void theRest33() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS NOT EMPTY + """); + } + + @Test + void theRest34() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void theRest35() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.shipped = FALSE + """); + } + + @Test + void theRest36() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE + NOT (o.shippingAddress.state = o.billingAddress.state AND + o.shippingAddress.city = o.billingAddress.city AND + o.shippingAddress.street = o.billingAddress.street) + """); + } + + @Test + void theRest37() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.shippingAddress <> o.billingAddress + """); + } + + @Test + void theRest38() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.name = ?1 + """); + } + + @Test + void hqlQueries() { + + parseWithoutChanges("from Person"); + parseWithoutChanges("select local datetime"); + parseWithoutChanges("from Person p select p.name"); + parseWithoutChanges("update Person set nickName = 'Nacho' " + // + "where name = 'Ignacio'"); + parseWithoutChanges("update Person p " + // + "set p.name = :newName " + // + "where p.name = :oldName"); + parseWithoutChanges("update Person " + // + "set name = :newName " + // + "where name = :oldName"); + parseWithoutChanges("update versioned Person " + // + "set name = :newName " + // + "where name = :oldName"); + parseWithoutChanges("insert Person (id, name) " + // + "values (100L, 'Jane Doe')"); + parseWithoutChanges("insert Person (id, name) " + // + "values (101L, 'J A Doe III'), " + // + "(102L, 'J X Doe'), " + // + "(103L, 'John Doe, Jr')"); + parseWithoutChanges("insert into Partner (id, name) " + // + "select p.id, p.name " + // + "from Person p "); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.name like 'Joe'"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.name like 'Joe''s'"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.id = 1"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.id = 1L"); + parseWithoutChanges("select c " + // + "from Call c " + // + "where c.duration > 100.5"); + parseWithoutChanges("select c " + // + "from Call c " + // + "where c.duration > 100.5F"); + parseWithoutChanges("select c " + // + "from Call c " + // + "where c.duration > 1e+2"); + parseWithoutChanges("select c " + // + "from Call c " + // + "where c.duration > 1e+2F"); + parseWithoutChanges("from Phone ph " + // + "where ph.type = LAND_LINE"); + parseWithoutChanges("select java.lang.Math.PI"); + parseWithoutChanges("select 'Customer ' || p.name " + // + "from Person p " + // + "where p.id = 1"); + parseWithoutChanges("select sum(ch.duration) * :multiplier " + // + "from Person pr " + // + "join pr.phones ph " + // + "join ph.callHistory ch " + // + "where ph.id = 1L "); + parseWithoutChanges("select year(local date) - year(p.createdOn) " + // + "from Person p " + // + "where p.id = 1L"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where year(local date) - year(p.createdOn) > 1"); + parseWithoutChanges("select " + // + " case p.nickName " + // + " when 'NA' " + // + " then '' " + // + " else p.nickName " + // + " end " + // + "from Person p"); + parseWithoutChanges("select " + // + " case " + // + " when p.nickName is null " + // + " then " + // + " case " + // + " when p.name is null " + // + " then '' " + // + " else p.name " + // + " end" + // + " else p.nickName " + // + " end " + // + "from Person p"); + parseWithoutChanges("select " + // + " case when p.nickName is null " + // + " then p.id * 1000 " + // + " else p.id " + // + " end " + // + "from Person p " + // + "order by p.id"); + parseWithoutChanges("select p " + // + "from Payment p " + // + "where type(p) = CreditCardPayment"); + parseWithoutChanges("select p " + // + "from Payment p " + // + "where type(p) = :type"); + parseWithoutChanges("select p " + // + "from Payment p " + // + "where length(treat(p as CreditCardPayment).cardNumber) between 16 and 20"); + parseWithoutChanges("select nullif(p.nickName, p.name) " + // + "from Person p"); + parseWithoutChanges("select " + // + " case" + // + " when p.nickName = p.name" + // + " then null" + // + " else p.nickName" + // + " end " + // + "from Person p"); + parseWithoutChanges("select coalesce(p.nickName, '') " + // + "from Person p"); + parseWithoutChanges("select coalesce(p.nickName, p.name, '') " + // + "from Person p"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where size(p.phones) >= 2"); + parseWithoutChanges("select concat(p.number, ' : ' , cast(c.duration as string)) " + // + "from Call c " + // + "join c.phone p"); + parseWithoutChanges("select substring(p.number, 1, 2) " + // + "from Call c " + // + "join c.phone p"); + parseWithoutChanges("select upper(p.name) " + // + "from Person p "); + parseWithoutChanges("select lower(p.name) " + // + "from Person p "); + parseWithoutChanges("select trim(p.name) " + // + "from Person p "); + parseWithoutChanges("select trim(leading ' ' from p.name) " + // + "from Person p "); + parseWithoutChanges("select length(p.name) " + // + "from Person p "); + parseWithoutChanges("select locate('John', p.name) " + // + "from Person p "); + parseWithoutChanges("select abs(c.duration) " + // + "from Call c "); + parseWithoutChanges("select mod(c.duration, 10) " + // + "from Call c "); + parseWithoutChanges("select sqrt(c.duration) " + // + "from Call c "); + parseWithoutChanges("select cast(c.duration as String) " + // + "from Call c "); + parseWithoutChanges("select str(c.timestamp) " + // + "from Call c "); + parseWithoutChanges("select str(cast(duration as float) / 60, 4, 2) " + // + "from Call c "); + parseWithoutChanges("select c " + // + "from Call c " + // + "where extract(date from c.timestamp) = local date"); + parseWithoutChanges("select extract(year from c.timestamp) " + // + "from Call c "); + parseWithoutChanges("select year(c.timestamp) " + // + "from Call c "); + parseWithoutChanges("select var_samp(c.duration) as sampvar, var_pop(c.duration) as popvar " + // + "from Call c "); + parseWithoutChanges("select bit_length(c.phone.number) " + // + "from Call c "); + parseWithoutChanges("select c " + // + "from Call c " + // + "where c.duration < 30 "); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.name like 'John%' "); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.createdOn > '1950-01-01' "); + parseWithoutChanges("select p " + // + "from Phone p " + // + "where p.type = 'MOBILE' "); + parseWithoutChanges("select p " + // + "from Payment p " + // + "where p.completed = true "); + parseWithoutChanges("select p " + // + "from Payment p " + // + "where type(p) = WireTransferPayment "); + parseWithoutChanges("select p " + // + "from Payment p, Phone ph " + // + "where p.person = ph.person "); + parseWithoutChanges("select p " + // + "from Person p " + // + "join p.phones ph " + // + "where p.id = 1L and index(ph) between 0 and 3"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.createdOn between '1999-01-01' and '2001-01-02'"); + parseWithoutChanges("select c " + // + "from Call c " + // + "where c.duration between 5 and 20"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.name between 'H' and 'M'"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.nickName is not null"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.nickName is null"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.name like 'Jo%'"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.name not like 'Jo%'"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.name like 'Dr|_%' escape '|'"); + parseWithoutChanges("select p " + // + "from Payment p " + // + "where type(p) in (CreditCardPayment, WireTransferPayment)"); + parseWithoutChanges("select p " + // + "from Phone p " + // + "where type in ('MOBILE', 'LAND_LINE')"); + parseWithoutChanges("select p " + // + "from Phone p " + // + "where type in :types"); + parseWithoutChanges("select distinct p " + // + "from Phone p " + // + "where p.person.id in (" + // + " select py.person.id " + // + " from Payment py" + // + " where py.completed = true and py.amount > 50 " + // + ")"); + parseWithoutChanges("select distinct p " + // + "from Phone p " + // + "where p.person in (" + // + " select py.person " + // + " from Payment py" + // + " where py.completed = true and py.amount > 50 " + // + ")"); + parseWithoutChanges("select distinct p " + // + "from Payment p " + // + "where (p.amount, p.completed) in (" + // + " (50, true)," + // + " (100, true)," + // + " (5, false)" + // + ")"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where 1 in indices(p.phones)"); + parseWithoutChanges("select distinct p.person " + // + "from Phone p " + // + "join p.calls c " + // + "where 50 > all (" + // + " select duration" + // + " from Call" + // + " where phone = p " + // + ") "); + parseWithoutChanges("select p " + // + "from Phone p " + // + "where local date > all elements(p.repairTimestamps)"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where :phone = some elements(p.phones)"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where :phone member of p.phones"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where exists elements(p.phones)"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.phones is empty"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.phones is not empty"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.phones is not empty"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where 'Home address' member of p.addresses"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where 'Home address' not member of p.addresses"); + parseWithoutChanges("select p " + // + "from Person p"); + parseWithoutChanges("select p " + // + "from org.hibernate.userguide.model.Person p"); + parseWithoutChanges("select distinct pr, ph " + // + "from Person pr, Phone ph " + // + "where ph.person = pr and ph is not null"); + parseWithoutChanges("select distinct pr1 " + // + "from Person pr1, Person pr2 " + // + "where pr1.id <> pr2.id " + // + " and pr1.address = pr2.address " + // + " and pr1.createdOn < pr2.createdOn"); + parseWithoutChanges("select distinct pr, ph " + // + "from Person pr cross join Phone ph " + // + "where ph.person = pr and ph is not null"); + parseWithoutChanges("select p " + // + "from Payment p "); + parseWithoutChanges("select d.owner, d.payed " + // + "from (" + // + " select p.person as owner, c.payment is not null as payed " + // + " from Call c " + // + " join c.phone p " + // + " where p.number = :phoneNumber) d"); + parseWithoutChanges("select distinct pr " + // + "from Person pr " + // + "join Phone ph on ph.person = pr " + // + "where ph.type = :phoneType"); + parseWithoutChanges("select distinct pr " + // + "from Person pr " + // + "join pr.phones ph " + // + "where ph.type = :phoneType"); + parseWithoutChanges("select distinct pr " + // + "from Person pr " + // + "inner join pr.phones ph " + // + "where ph.type = :phoneType"); + parseWithoutChanges("select distinct pr " + // + "from Person pr " + // + "left join pr.phones ph " + // + "where ph is null " + // + " or ph.type = :phoneType"); + parseWithoutChanges("select distinct pr " + // + "from Person pr " + // + "left outer join pr.phones ph " + // + "where ph is null " + // + " or ph.type = :phoneType"); + parseWithoutChanges("select pr.name, ph.number " + // + "from Person pr " + // + "left join pr.phones ph with ph.type = :phoneType "); + parseWithoutChanges("select pr.name, ph.number " + // + "from Person pr " + // + "left join pr.phones ph on ph.type = :phoneType "); + parseWithoutChanges("select distinct pr " + // + "from Person pr " + // + "left join fetch pr.phones "); + parseWithoutChanges("select a, ccp " + // + "from Account a " + // + "join treat(a.payments as CreditCardPayment) ccp " + // + "where length(ccp.cardNumber) between 16 and 20"); + parseWithoutChanges("select c, ccp " + // + "from Call c " + // + "join treat(c.payment as CreditCardPayment) ccp " + // + "where length(ccp.cardNumber) between 16 and 20"); + parseWithoutChanges("select longest.duration " + // + "from Phone p " + // + "left join lateral (" + // + " select c.duration as duration " + // + " from p.calls c" + // + " order by c.duration desc" + // + " limit 1 " + // + " ) longest " + // + "where p.number = :phoneNumber"); + parseWithoutChanges("select ph " + // + "from Phone ph " + // + "where ph.person.address = :address "); + parseWithoutChanges("select ph " + // + "from Phone ph " + // + "join ph.person pr " + // + "where pr.address = :address "); + parseWithoutChanges("select ph " + // + "from Phone ph " + // + "where ph.person.address = :address " + // + " and ph.person.createdOn > :timestamp"); + parseWithoutChanges("select ph " + // + "from Phone ph " + // + "inner join ph.person pr " + // + "where pr.address = :address " + // + " and pr.createdOn > :timestamp"); + parseWithoutChanges("select ph " + // + "from Person pr " + // + "join pr.phones ph " + // + "join ph.calls c " + // + "where pr.address = :address " + // + " and c.duration > :duration"); + parseWithoutChanges("select ch " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + parseWithoutChanges("select value(ch) " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + parseWithoutChanges("select key(ch) " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + parseWithoutChanges("select key(ch) " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + parseWithoutChanges("select entry(ch) " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + parseWithoutChanges("select sum(ch.duration) " + // + "from Person pr " + // + "join pr.phones ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id " + // + " and index(ph) = :phoneIndex"); + parseWithoutChanges("select value(ph.callHistory) " + // + "from Phone ph " + // + "where ph.id = :id "); + parseWithoutChanges("select key(ph.callHistory) " + // + "from Phone ph " + // + "where ph.id = :id "); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.phones[0].type = LAND_LINE"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where p.addresses['HOME'] = :address"); + parseWithoutChanges("select pr " + // + "from Person pr " + // + "where pr.phones[max(indices(pr.phones))].type = 'LAND_LINE'"); + parseWithoutChanges("select p.name, p.nickName " + // + "from Person p "); + parseWithoutChanges("select p.name as name, p.nickName as nickName " + // + "from Person p "); + parseWithoutChanges("select new org.hibernate.userguide.hql.CallStatistics(" + // + " count(c), " + // + " sum(c.duration), " + // + " min(c.duration), " + // + " max(c.duration), " + // + " avg(c.duration)" + // + ") " + // + "from Call c "); + parseWithoutChanges("select new map(" + // + " p.number as phoneNumber , " + // + " sum(c.duration) as totalDuration, " + // + " avg(c.duration) as averageDuration " + // + ") " + // + "from Call c " + // + "join c.phone p " + // + "group by p.number "); + parseWithoutChanges("select new list(" + // + " p.number, " + // + " c.duration " + // + ") " + // + "from Call c " + // + "join c.phone p "); + parseWithoutChanges("select distinct p.lastName " + // + "from Person p"); + parseWithoutChanges("select " + // + " count(c), " + // + " sum(c.duration), " + // + " min(c.duration), " + // + " max(c.duration), " + // + " avg(c.duration) " + // + "from Call c "); + parseWithoutChanges("select count(distinct c.phone) " + // + "from Call c "); + parseWithoutChanges("select p.number, count(c) " + // + "from Call c " + // + "join c.phone p " + // + "group by p.number"); + parseWithoutChanges("select p " + // + "from Phone p " + // + "where max(elements(p.calls)) = :call"); + parseWithoutChanges("select p " + // + "from Phone p " + // + "where min(elements(p.calls)) = :call"); + parseWithoutChanges("select p " + // + "from Person p " + // + "where max(indices(p.phones)) = 0"); + parseWithoutChanges("select count(c) filter (where c.duration < 30) " + // + "from Call c "); + parseWithoutChanges("select p.number, count(c) filter (where c.duration < 30) " + // + "from Call c " + // + "join c.phone p " + // + "group by p.number"); + parseWithoutChanges("select listagg(p.number, ', ') within group (order by p.type,p.number) " + // + "from Phone p " + // + "group by p.person"); + parseWithoutChanges("select sum(c.duration) " + // + "from Call c "); + parseWithoutChanges("select p.name, sum(c.duration) " + // + "from Call c " + // + "join c.phone ph " + // + "join ph.person p " + // + "group by p.name"); + parseWithoutChanges("select p, sum(c.duration) " + // + "from Call c " + // + "join c.phone ph " + // + "join ph.person p " + // + "group by p"); + parseWithoutChanges("select p.name, sum(c.duration) " + // + "from Call c " + // + "join c.phone ph " + // + "join ph.person p " + // + "group by p.name " + // + "having sum(c.duration) > 1000"); + parseWithoutChanges("select p.name from Person p " + // + "union " + // + "select p.nickName from Person p where p.nickName is not null"); + parseWithoutChanges("select p " + // + "from Person p " + // + "order by p.name"); + parseWithoutChanges("select p.name, sum(c.duration) as total " + // + "from Call c " + // + "join c.phone ph " + // + "join ph.person p " + // + "group by p.name " + // + "order by total"); + parseWithoutChanges("select c " + // + "from Call c " + // + "join c.phone p " + // + "order by p.number " + // + "limit 50"); + parseWithoutChanges("select c " + // + "from Call c " + // + "join c.phone p " + // + "order by p.number " + // + "fetch first 50 rows only"); + parseWithoutChanges("select p " + // + "from Phone p " + // + "join fetch p.calls " + // + "order by p " + // + "limit 50"); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java new file mode 100644 index 0000000000..b8906ddc77 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -0,0 +1,815 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import java.util.regex.Pattern; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.JpaSort; +import org.springframework.lang.Nullable; + +/** + * Verify that HQL queries are properly transformed through the {@link JpaQueryParsingEnhancer} and the + * {@link HqlQueryParser}. + * + * @author Greg Turnquist + * @since 3.1 + */ +class HqlQueryTransformerTests { + + private static final String QUERY = "select u from User u"; + private static final String FQ_QUERY = "select u from org.acme.domain.User$Foo_Bar u"; + private static final String SIMPLE_QUERY = "from User u"; + private static final String COUNT_QUERY = "select count(u) from User u"; + + private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?1"; + private static final Pattern MULTI_WHITESPACE = Pattern.compile("\\s+"); + + @Test + void applyingSortShouldIntroduceOrderByCriteriaWhereNoneExists() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name"; + var sort = Sort.by("first_name", "last_name"); + + // when + var results = createQueryFor(original, sort); + + // then + assertThat(original).doesNotContainIgnoringCase("order by"); + assertThat(results).contains("order by e.first_name asc, e.last_name asc"); + } + + @Test + void applyingSortShouldCreateAdditionalOrderByCriteria() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.role, e.hire_date"; + var sort = Sort.by("first_name", "last_name"); + + // when + var results = createQueryFor(original, sort); + + // then + assertThat(results).contains("ORDER BY e.role, e.hire_date, e.first_name asc, e.last_name asc"); + } + + @Test + void applyCountToSimpleQuery() { + + // given + var original = "FROM Employee e where e.name = :name"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("select count(e) FROM Employee e where e.name = :name"); + } + + @Test + void applyCountToMoreComplexQuery() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.modified_date"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("SELECT count(e) FROM Employee e where e.name = :name"); + } + + @Test + void applyCountToAlreadySortedQuery() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.modified_date"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("SELECT count(e) FROM Employee e where e.name = :name"); + } + + @Test + void multipleAliasesShouldBeGathered() { + + // given + var original = "select e from Employee e join e.manager m"; + + // when + var results = createQueryFor(original, null); + + // then + assertThat(results).isEqualTo("select e from Employee e join e.manager m"); + } + + @Test + void createsCountQueryCorrectly() { + assertCountQuery(QUERY, COUNT_QUERY); + } + + @Test + void createsCountQueriesCorrectlyForCapitalLetterHQL() { + + assertCountQuery("select u FROM User u WHERE u.foo.bar = ?1", "select count(u) FROM User u WHERE u.foo.bar = ?1"); + assertCountQuery("SELECT u FROM User u where u.foo.bar = ?1", "SELECT count(u) FROM User u where u.foo.bar = ?1"); + } + + @Test + void createsCountQueryForDistinctQueries() { + + assertCountQuery("select distinct u from User u where u.foo = ?1", + "select count(distinct u) from User u where u.foo = ?1"); + } + + @Test + void createsCountQueryForConstructorQueries() { + + assertCountQuery("select distinct new com.example.User(u.name) from User u where u.foo = ?1", + "select count(distinct u) from User u where u.foo = ?1"); + } + + @Test + void createsCountQueryForJoins() { + + assertCountQuery("select distinct new com.User(u.name) from User u left outer join u.roles r WHERE r = ?1", + "select count(distinct u) from User u left outer join u.roles r WHERE r = ?1"); + } + + @Test + void createsCountQueryForQueriesWithSubSelectsSelectQuery() { + + assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role r)", + "select count(u) from User u left outer join u.roles r where r in (select r from Role r)"); + } + + @Test + void createsCountQueryForQueriesWithSubSelects() { + + assertCountQuery("from User u left outer join u.roles r where r in (select r from Role r) select u ", + "from User u left outer join u.roles r where r in (select r from Role r) select count(u)"); + } + + @Test + void createsCountQueryForAliasesCorrectly() { + assertCountQuery("select u from User as u", "select count(u) from User as u"); + } + + @Test + void allowsShortJpaSyntax() { + assertCountQuery(SIMPLE_QUERY, COUNT_QUERY); + } + + @Test // GH-2260 + void detectsAliasCorrectly() { + + assertThat(alias(QUERY)).isEqualTo("u"); + assertThat(alias(SIMPLE_QUERY)).isEqualTo("u"); + assertThat(alias(COUNT_QUERY)).isEqualTo("u"); + assertThat(alias(QUERY_WITH_AS)).isEqualTo("u"); + assertThat(alias("SELECT u FROM USER U")).isEqualTo("U"); + assertThat(alias("select u from User u")).isEqualTo("u"); + assertThat(alias("select new com.acme.UserDetails(u.id, u.name) from User u")).isEqualTo("u"); + assertThat(alias("select u from T05User u")).isEqualTo("u"); + assertThat(alias("select u from User u where not exists (select m from User m where m = u.manager) ")) + .isEqualTo("u"); + assertThat(alias("select u from User u where not exists (select u2 from User u2)")).isEqualTo("u"); + assertThat(alias( + "select u from User u where not exists (select u2 from User u2 where not exists (select u3 from User u3))")) + .isEqualTo("u"); + assertThat(alias( + "SELECT e FROM DbEvent e WHERE TREAT(modifiedFrom AS date) IS NULL OR e.modificationDate >= :modifiedFrom")) + .isEqualTo("e"); + } + + @Test // GH-2557 + void applySortingAccountsForNewlinesInSubselect() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + assertThat(new JpaQueryParsingEnhancer(new HqlQueryParser("select u\n" + // + "from user u\n" + // + "where exists (select u2\n" + // + "from user u2\n" + // + ")\n" + // + "")).applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + // + "from user u\n" + // + "where exists (select u2\n" + // + "from user u2\n" + // + ")\n" + // + " order by u.age desc"); + } + + @Test // GH-2563 + void aliasDetectionProperlyHandlesNewlinesInSubselects() { + + assertThat(alias(""" + SELECT o + FROM Order o + WHERE EXISTS( SELECT 1 + FROM Vehicle vehicle + WHERE vehicle.vehicleOrderId = o.id + AND LOWER(COALESCE(vehicle.make, '')) LIKE :query) + """)).isEqualTo("o"); + } + + @Test // DATAJPA-252 + void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { + + String query = "select p from Person p left join p.address address"; + Sort sort = Sort.by("address.city"); + assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc"); + } + + @Test // DATAJPA-252 + void extendsExistingOrderByClausesCorrectly() { + + String query = "select p from Person p order by p.lastname asc"; + Sort sort = Sort.by("firstname"); + assertThat(createQueryFor(query, sort)) + .isEqualTo("select p from Person p order by p.lastname asc, p.firstname asc"); + } + + @Test // DATAJPA-296 + void appliesIgnoreCaseOrderingCorrectly() { + + String query = "select p from Person p"; + Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); + + assertThat(createQueryFor(query, sort)).endsWith("order by lower(p.firstname) asc"); + } + + @Test // DATAJPA-296 + void appendsIgnoreCaseOrderingCorrectly() { + + String query = "select p from Person p order by p.lastname asc"; + Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); + + assertThat(createQueryFor(query, sort)) + .isEqualTo("select p from Person p order by p.lastname asc, lower(p.firstname) asc"); + } + + @Test // DATAJPA-342 + void usesReturnedVariableInCountProjectionIfSet() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", + "select count(distinct m.genre) from Media m where m.user = ?1"); + } + + @Test // DATAJPA-343 + void projectsCountQueriesForQueriesWithSubselects() { + + // given + var original = "select o from Foo o where cb.id in (select b from Bar b)"; + + // when + var results = createQueryFor(original, Sort.by("first_name", "last_name")); + + // then + assertThat(results).isEqualTo( + "select o from Foo o where cb.id in (select b from Bar b) order by o.first_name asc, o.last_name asc"); + + assertCountQuery("select o from Foo o where cb.id in (select b from Bar b)", + "select count(o) from Foo o where cb.id in (select b from Bar b)"); + } + + @Test // DATAJPA-148 + void doesNotPrefixSortsIfFunction() { + + Sort sort = Sort.by("sum(foo)"); + + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> createQueryFor("select p from Person p", sort)); + } + + @Test // DATAJPA-377 + void removesOrderByInGeneratedCountQueryFromOriginalQueryIfPresent() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 OrDer By m.genre ASC", + "select count(distinct m.genre) from Media m where m.user = ?1"); + } + + @Test // DATAJPA-375 + void findsExistingOrderByIndependentOfCase() { + + Sort sort = Sort.by("lastname"); + String query = createQueryFor("select p from Person p ORDER BY p.firstname", sort); + assertThat(query).endsWith("ORDER BY p.firstname, p.lastname asc"); + } + + @Test // DATAJPA-409 + void createsCountQueryForNestedReferenceCorrectly() { + assertCountQuery("select a.b from A a", "select count(a) from A a"); + } + + @Test // DATAJPA-420 + void createsCountQueryForScalarSelects() { + assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p"); + } + + @Test // DATAJPA-456 + void createCountQueryFromTheGivenCountProjection() { + assertThat(createCountQueryFor("select p.lastname,p.firstname from Person p", "p.lastname")) + .isEqualTo("select count(p.lastname) from Person p"); + } + + @Test // DATAJPA-736 + void supportsNonAsciiCharactersInEntityNames() { + assertThat(createCountQueryFor("select u from Usèr u")).isEqualTo("select count(u) from Usèr u"); + } + + @Test // DATAJPA-798 + void detectsAliasInQueryContainingLineBreaks() { + assertThat(alias("select \n u \n from \n User \nu")).isEqualTo("u"); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionInDistinctQuery() { + assertThat(hasConstructorExpression("select distinct new com.example.Foo(b.name) from Bar b")).isTrue(); + } + + @Test // DATAJPA-938 + void detectsComplexConstructorExpression() { + + assertThat(hasConstructorExpression("select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) " // + + "from Bar lp join lp.investmentProduct ip " // + + "where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId " + // + + "group by ip.id, ip.name, lp.accountId " // + + "order by ip.name ASC")).isTrue(); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionWithLineBreaks() { + assertThat(hasConstructorExpression("select new foo.bar.FooBar(\na.id) from DtoA a ")).isTrue(); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotAllowWhitespaceInSort() { + + Sort sort = Sort.by("case when foo then bar"); + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> createQueryFor("select p from Person p", sort)); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixUnsafeJpaSortFunctionCalls() { + + JpaSort sort = JpaSort.unsafe("sum(foo)"); + assertThat(createQueryFor("select p from Person p", sort)).endsWith("order by sum(foo) asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixMultipleAliasedFunctionCalls() { + + String query = "SELECT AVG(m.price) AS avgPrice, SUM(m.stocks) AS sumStocks FROM Magazine m"; + Sort sort = Sort.by("avgPrice", "sumStocks"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc, sumStocks asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixSingleAliasedFunctionCalls() { + + String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("avgPrice"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { + + String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("someOtherProperty"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by m.someOtherProperty asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainsAliasedFunctionForDifferentProperty() { + + String query = "SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("name", "avgPrice"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by m.name asc, avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() { + + String query = "SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m"; + Sort sort = Sort.by("trimmedName"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by trimmedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() { + + String query = "SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m"; + Sort sort = Sort.by("extendedName"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by extendedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { + + String query = "SELECT AVG(m.price) AS avg_price FROM Magazine m"; + Sort sort = Sort.by("avg_price"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by avg_price asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithDots() { + + String query = "SELECT AVG(m.price) AS m.avg FROM Magazine m"; + Sort sort = Sort.by("m.avg"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by m.avg asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpaces() { + + String query = "SELECT AVG( m.price ) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("avgPrice"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc"); + } + + @Test // DATAJPA-1506 + void detectsAliasWithGroupAndOrderBy() { + + assertThat(alias("select * from User group by name")).isNull(); + assertThat(alias("select * from User order by name")).isNull(); + assertThat(alias("select u from User u group by name")).isEqualTo("u"); + assertThat(alias("select u from User u order by name")).isEqualTo("u"); + } + + @Test // DATAJPA-1500 + void createCountQuerySupportsWhitespaceCharacters() { + + assertThat(createCountQueryFor("select user from User user\n" + // + " where user.age = 18\n" + // + " order by user.name\n ")).isEqualToIgnoringWhitespace("select count(user) from User user\n" + // + " where user.age = 18\n "); + } + + @Test + void createCountQuerySupportsLineBreaksInSelectClause() { + + assertThat(createCountQueryFor("select user.age,\n" + // + " user.name\n" + // + " from User user\n" + // + " where user.age = 18\n" + // + " order\nby\nuser.name\n ")).isEqualToIgnoringWhitespace("select count(user) from User user\n" + // + " where user.age = 18\n "); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFieldAliases() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("authorName"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by m.authorName asc"); + } + + @Test // GH-2280 + void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { + + String query = "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer"; + Sort sort = Sort.by(Sort.Order.by("name").ignoreCase()); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).isEqualTo( + "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(customer.name) asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFunctionAliases() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("title"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by m.title asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForSimpleField() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("price"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by m.price asc"); + } + + @Test + void createCountQuerySupportsLineBreakRightAfterDistinct() { + + assertThat(createCountQueryFor("select\ndistinct\nuser.age,\n" + // + "user.name\n" + // + "from\nUser\nuser")).isEqualTo(createCountQueryFor("select\ndistinct user.age,\n" + // + "user.name\n" + // + "from\nUser\nuser")); + } + + @Test + void detectsAliasWithGroupAndOrderByWithLineBreaks() { + + assertThat(alias("select * from User group\nby name")).isNull(); + assertThat(alias("select * from User order\nby name")).isNull(); + assertThat(alias("select u from User u group\nby name")).isEqualTo("u"); + assertThat(alias("select u from User u order\nby name")).isEqualTo("u"); + assertThat(alias("select u from User\nu\norder \n by name")).isEqualTo("u"); + } + + @Test // DATAJPA-1679 + void findProjectionClauseWithDistinct() { + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(projection("select a,b,c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select a, b, c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select distinct a, b, c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select DISTINCT a, b, c from Entity x")).isEqualTo("a, b, c"); + }); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithSubselect() { + + // This is not a required behavior, in fact the opposite is, + // but it documents a current limitation. + // to fix this without breaking findProjectionClauseWithIncludedFrom we need a more sophisticated parser. + assertThat(projection("select * from (select x from y)")).isNotEqualTo("*"); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithIncludedFrom() { + assertThat(projection("select x, frommage, y from t")).isEqualTo("x, frommage, y"); + } + + @Test // GH-2341 + void countProjectionDistrinctQueryIncludesNewLineAfterFromAndBeforeJoin() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1\nLEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test // GH-2341 + void countProjectionDistinctQueryIncludesNewLineAfterEntity() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test // GH-2341 + void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key where entity1.id = 1799"); + } + + @Test // GH-2393 + void createCountQueryStartsWithWhitespace() { + + assertThat(createCountQueryFor(" \nselect u from User u where u.age > :age")) + .isEqualTo("select count(u) from User u where u.age > :age"); + + assertThat(createCountQueryFor(" \nselect u from User u where u.age > :age")) + .isEqualTo("select count(u) from User u where u.age > :age"); + } + + @Test // GH-2260 + void applySortingAccountsForNativeWindowFunction() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + // order by absent + assertThat(createQueryFor("select u from user u", sort)).isEqualTo("select u from user u order by u.age desc"); + + // order by present + assertThat(createQueryFor("select u from user u order by u.lastname", sort)) + .isEqualTo("select u from user u order by u.lastname, u.age desc"); + + // partition by + assertThat(createQueryFor("select dense_rank() over (partition by age) from user u", sort)) + .isEqualTo("select dense_rank() over (partition by age) from user u order by u.age desc"); + + // order by in over clause + assertThat(createQueryFor("select dense_rank() over (order by lastname) from user u", sort)) + .isEqualTo("select dense_rank() over (order by lastname) from user u order by u.age desc"); + + // order by in over clause (additional spaces) + assertThat(createQueryFor("select dense_rank() over ( order by lastname ) from user u", sort)) + .isEqualTo("select dense_rank() over (order by lastname) from user u order by u.age desc"); + + // order by in over clause + at the end + assertThat(createQueryFor("select dense_rank() over (order by lastname) from user u order by u.lastname", sort)) + .isEqualTo("select dense_rank() over (order by lastname) from user u order by u.lastname, u.age desc"); + + // partition by + order by in over clause + assertThat(createQueryFor("select dense_rank() over (partition by active, age order by lastname) from user u", + sort)).isEqualTo( + "select dense_rank() over (partition by active, age order by lastname) from user u order by u.age desc"); + + // partition by + order by in over clause + order by at the end + assertThat(createQueryFor( + "select dense_rank() over (partition by active, age order by lastname) from user u order by active", sort)) + .isEqualTo( + "select dense_rank() over (partition by active, age order by lastname) from user u order by active, u.age desc"); + + // partition by + order by in over clause + frame clause + assertThat(createQueryFor( + "select dense_rank() over ( partition by active, age order by username rows between current row and unbounded following ) from user u", + sort)).isEqualTo( + "select dense_rank() over (partition by active, age order by username rows between current row and unbounded following) from user u order by u.age desc"); + + // partition by + order by in over clause + frame clause + order by at the end + assertThat(createQueryFor( + "select dense_rank() over ( partition by active, age order by username rows between current row and unbounded following ) from user u order by active", + sort)).isEqualTo( + "select dense_rank() over (partition by active, age order by username rows between current row and unbounded following) from user u order by active, u.age desc"); + + // order by in subselect (select expression) + assertThat(createQueryFor("select lastname, (select i.id from item i order by i.id limit 1) from user u", sort)) + .isEqualTo("select lastname, (select i.id from item i order by i.id limit 1) from user u order by u.age desc"); + + // order by in subselect (select expression) + at the end + assertThat(createQueryFor( + "select lastname, (select i.id from item i order by 1 limit 1) from user u order by active", sort)).isEqualTo( + "select lastname, (select i.id from item i order by 1 limit 1) from user u order by active, u.age desc"); + + // order by in subselect (from expression) + assertThat(createQueryFor("select u from (select u2 from user u2 order by age desc limit 10) u", sort)) + .isEqualTo("select u from (select u2 from user u2 order by age desc limit 10 ) u order by u.age desc"); + + // order by in subselect (from expression) + at the end + assertThat(createQueryFor( + "select u from (select u2 from user u2 order by 1, 2, 3 desc limit 10) u order by u.active asc", sort)) + .isEqualTo( + "select u from (select u2 from user u2 order by 1, 2, 3 desc limit 10 ) u order by u.active asc, u.age desc"); + } + + @Test // GH-2511 + void countQueryUsesCorrectVariable() { + + assertThat(createCountQueryFor("SELECT e FROM User e WHERE created_at > $1")) + .isEqualTo("SELECT count(e) FROM User e WHERE created_at > $1"); + + assertThat( + createCountQueryFor("SELECT e FROM mytable e WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'")) + .isEqualTo("SELECT count(e) FROM mytable e WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); + + assertThat(createCountQueryFor("SELECT e FROM context e ORDER BY time")) + .isEqualTo("SELECT count(e) FROM context e"); + + assertThat(createCountQueryFor("select e FROM users_statuses e WHERE (user_created_at BETWEEN $1 AND $2)")) + .isEqualTo("select count(e) FROM users_statuses e WHERE (user_created_at BETWEEN $1 AND $2)"); + + assertThat( + createCountQueryFor("SELECT us FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)")) + .isEqualTo("SELECT count(us) FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); + } + + @Test // GH-2496, GH-2522, GH-2537, GH-2045 + void orderByShouldWorkWithSubSelectStatements() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + assertThat(createQueryFor("SELECT\n" // + + " foo_bar\n" // + + "FROM\n" // + + " foo foo\n" // + + "INNER JOIN\n" // + + " foo_bar_dnrmv foo_bar ON\n" // + + " foo_bar.foo_id = foo.foo_id\n" // + + "INNER JOIN\n" // + + " (\n" // + + " SELECT\n" // + + " foo_bar_action\n" // + + " FROM\n" // + + " foo_bar_action\n" // + + " WHERE\n" // + + " foo_bar_action.deleted_ts IS NULL)\n" // + + " foo_bar_action ON\n" // + + " foo_bar.foo_bar_id = foo_bar_action.foo_bar_id\n" // + + " AND ranking = 1\n" // + + "INNER JOIN\n" // + + " bar bar ON\n" // + + " foo_bar.bar_id = bar.bar_id\n" // + + "INNER JOIN\n" // + + " bar_metadata bar_metadata ON\n" // + + " bar.bar_metadata_key = bar_metadata.bar_metadata_key\n" // + + "WHERE\n" // + + " foo.tenant_id =:tenantId", sort)).endsWith("order by foo.age desc"); + + assertThat(createQueryFor("select r " // + + "From DataRecord r " // + + "where " // + + " ( " // + + " r.adusrId = :userId " // + + " or EXISTS( select 1 FROM DataRecordDvsRight dr WHERE dr.adusrId = :userId AND dr.dataRecord = r ) " // + + ")", sort)).endsWith("order by r.age desc"); + + assertThat(createQueryFor("select distinct u " // + + "from FooBar u " // + + "where u.role = 'redacted' " // + + "and (" // + + " not exists (" // + + " from FooBarGroup group " // + + " where group in :excludedGroups " // + + " and group in elements(u.groups)" // + + " )" // + + ")", sort)).endsWith("order by u.age desc"); + + assertThat(createQueryFor("SELECT i " // + + " FROM Item i " // + + " WHERE i.id IN (" // + + " SELECT max(i2.id) FROM Item i2 " // + + " WHERE i2.field.id = :fieldId " // + + " GROUP BY i2.field.id, i2.version)", sort)).endsWith("order by i.age desc"); + + assertThat(createQueryFor("select \n" // + + " f.id,\n" // + + " (\n" // + + " select timestamp from bar\n" // + + " where date(bar.timestamp) > '2022-05-21'\n" // + + " and bar.foo_id = f.id \n" // + + " order by date(bar.timestamp) desc\n" // + + " limit 1\n" // + + ") as timestamp\n" // + + "from foo f", sort)).endsWith("order by f.age desc"); + } + + private void assertCountQuery(String originalQuery, String countQuery) { + assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); + } + + private String createQueryFor(String query, Sort sort) { + return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).applySorting(sort); + } + + private String createCountQueryFor(String query) { + return createCountQueryFor(query, null); + } + + private String createCountQueryFor(String query, @Nullable String countProjection) { + return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).createCountQueryFor(countProjection); + } + + private String alias(String query) { + return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).detectAlias(); + } + + private boolean hasConstructorExpression(String query) { + return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).hasConstructorExpression(); + } + + private String projection(String query) { + return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).getProjection(); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java new file mode 100644 index 0000000000..179855736c --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java @@ -0,0 +1,1401 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Tests built around examples of HQL found in + * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc and + * https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#query-language
    + *
    + * IMPORTANT: Purely verifies the parser without any transformations. + * + * @author Greg Turnquist + * @since 3.1 + */ +class HqlSpecificationTests { + + private static final String SPEC_FAULT = "Disabled due to spec fault> "; + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + */ + @Test + void joinExample1() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order AS o JOIN o.lineItems AS l + WHERE l.shipped = FALSE + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables + */ + @Test + void joinExample2() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l JOIN l.product p + WHERE p.productType = 'office_supplies' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations + */ + @Test + void rangeVariableDeclarations() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o1 + FROM Order o1, Order o2 + WHERE o1.quantity > o2.quantity AND + o2.customer.lastname = 'Smith' AND + o2.customer.firstname= 'John' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample1() { + + HqlQueryParser.parse(""" + SELECT i.name, VALUE(p) + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample2() { + + HqlQueryParser.parse(""" + SELECT i.name, p + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample3() { + + HqlQueryParser.parse(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo.phones p + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample4() { + + HqlQueryParser.parse(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE e.contactInfo.address.zipcode = '95054' + """); + } + + @Test + void pathExpressionSyntaxExample1() { + + HqlQueryParser.parse(""" + SELECT DISTINCT l.product + FROM Order AS o JOIN o.lineItems l + """); + } + + @Test + void joinsExample1() { + + HqlQueryParser.parse(""" + SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize + """); + } + + @Test + void joinsExample2() { + + HqlQueryParser.parse(""" + SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInnerExample() { + + HqlQueryParser.parse(""" + SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInExample() { + + HqlQueryParser.parse(""" + SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 + """); + } + + @Test + void doubleJoinExample() { + + HqlQueryParser.parse(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE c.address.zipcode = '95054' + """); + } + + @Test + void leftJoinExample() { + + HqlQueryParser.parse(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + GROUP BY s.name + """); + } + + @Test + void leftJoinOnExample() { + + HqlQueryParser.parse(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + ON p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinWhereExample() { + + HqlQueryParser.parse(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + WHERE p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinFetchExample() { + + HqlQueryParser.parse(""" + SELECT d + FROM Department d LEFT JOIN FETCH d.employees + WHERE d.deptno = 1 + """); + } + + @Test + void collectionMemberExample() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void collectionMemberInExample() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o, IN(o.lineItems) l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void fromClauseExample() { + + HqlQueryParser.parse(""" + SELECT o + FROM Order AS o JOIN o.lineItems l JOIN l.product p + """); + } + + @Test + void fromClauseDowncastingExample1() { + + HqlQueryParser.parse(""" + SELECT b.name, b.ISBN + FROM Order o JOIN TREAT(o.product AS Book) b + """); + } + + @Test + void fromClauseDowncastingExample2() { + + HqlQueryParser.parse(""" + SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp + WHERE lp.budget > 1000 + """); + } + + /** + * @see #fromClauseDowncastingExample3fixed() + */ + @Test + @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") + void fromClauseDowncastingExample3_SPEC_BUG() { + + HqlQueryParser.parse(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE "cost overrun" + """); + } + + @Test + void fromClauseDowncastingExample3fixed() { + + HqlQueryParser.parse(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE 'cost overrun' + """); + } + + @Test + void fromClauseDowncastingExample4() { + + HqlQueryParser.parse(""" + SELECT e FROM Employee e + WHERE TREAT(e AS Exempt).vacationDays > 10 + OR TREAT(e AS Contractor).hours > 100 + """); + } + + @Test + void pathExpressionsNamedParametersExample() { + + HqlQueryParser.parse(""" + SELECT c + FROM Customer c + WHERE c.status = :stat + """); + } + + @Test + void betweenExpressionsExample() { + + HqlQueryParser.parse(""" + SELECT t + FROM CreditCard c JOIN c.transactionHistory t + WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 + """); + } + + @Test + void isEmptyExample() { + + HqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void memberOfExample() { + + HqlQueryParser.parse(""" + SELECT p + FROM Person p + WHERE 'Joe' MEMBER OF p.nicknames + """); + } + + @Test + void existsSubSelectExample1() { + + HqlQueryParser.parse(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS ( + SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void allExample() { + + HqlQueryParser.parse(""" + SELECT emp + FROM Employee emp + WHERE emp.salary > ALL ( + SELECT m.salary + FROM Manager m + WHERE m.department = emp.department) + """); + } + + @Test + void existsSubSelectExample2() { + + HqlQueryParser.parse(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS ( + SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void subselectNumericComparisonExample1() { + + HqlQueryParser.parse(""" + SELECT c + FROM Customer c + WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 + """); + } + + @Test + void subselectNumericComparisonExample2() { + + HqlQueryParser.parse(""" + SELECT goodCustomer + FROM Customer goodCustomer + WHERE goodCustomer.balanceOwed < ( + SELECT AVG(c.balanceOwed)/2.0 FROM Customer c) + """); + } + + @Test + void indexExample() { + + HqlQueryParser.parse(""" + SELECT w.name + FROM Course c JOIN c.studentWaitlist w + WHERE c.name = 'Calculus' + AND INDEX(w) = 0 + """); + } + + /** + * @see #functionInvocationExampleWithCorrection() + */ + @Test + @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") + void functionInvocationExample_SPEC_BUG() { + + HqlQueryParser.parse(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) + """); + } + + @Test + void functionInvocationExampleWithCorrection() { + + HqlQueryParser.parse(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE + """); + } + + @Test + void updateCaseExample1() { + + HqlQueryParser.parse(""" + UPDATE Employee e + SET e.salary = + CASE WHEN e.rating = 1 THEN e.salary * 1.1 + WHEN e.rating = 2 THEN e.salary * 1.05 + ELSE e.salary * 1.01 + END + """); + } + + @Test + void updateCaseExample2() { + + HqlQueryParser.parse(""" + UPDATE Employee e + SET e.salary = + CASE e.rating WHEN 1 THEN e.salary * 1.1 + WHEN 2 THEN e.salary * 1.05 + ELSE e.salary * 1.01 + END + """); + } + + @Test + void selectCaseExample1() { + + HqlQueryParser.parse(""" + SELECT e.name, + CASE TYPE(e) WHEN Exempt THEN 'Exempt' + WHEN Contractor THEN 'Contractor' + WHEN Intern THEN 'Intern' + ELSE 'NonExempt' + END + FROM Employee e + WHERE e.dept.name = 'Engineering' + """); + } + + @Test + void selectCaseExample2() { + + HqlQueryParser.parse(""" + SELECT e.name, + f.name, + CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' + WHEN f.annualMiles > 25000 THEN 'Gold ' + ELSE '' + END, + 'Frequent Flyer') + FROM Employee e JOIN e.frequentFlierPlan f + """); + } + + @Test + void theRest() { + + HqlQueryParser.parse(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (Exempt, Contractor) + """); + } + + @Test + void theRest2() { + + HqlQueryParser.parse(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (:empType1, :empType2) + """); + } + + @Test + void theRest3() { + + HqlQueryParser.parse(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN :empTypes + """); + } + + @Test + void theRest4() { + + HqlQueryParser.parse(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) <> Exempt + """); + } + + @Test + void theRest5() { + + HqlQueryParser.parse(""" + SELECT c.status, AVG(c.filledOrderCount), COUNT(c) + FROM Customer c + GROUP BY c.status + HAVING c.status IN (1, 2) + """); + } + + @Test + void theRest6() { + + HqlQueryParser.parse(""" + SELECT c.country, COUNT(c) + FROM Customer c + GROUP BY c.country + HAVING COUNT(c) > 30 + """); + } + + @Test + void theRest7() { + + HqlQueryParser.parse(""" + SELECT c, COUNT(o) + FROM Customer c JOIN c.orders o + GROUP BY c + HAVING COUNT(o) >= 5 + """); + } + + @Test + void theRest8() { + + HqlQueryParser.parse(""" + SELECT c.id, c.status + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest9() { + + HqlQueryParser.parse(""" + SELECT v.location.street, KEY(i).title, VALUE(i) + FROM VideoStore v JOIN v.videoInventory i + WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 + """); + } + + @Test + void theRest10() { + + HqlQueryParser.parse(""" + SELECT o.lineItems FROM Order AS o + """); + } + + @Test + void theRest11() { + + HqlQueryParser.parse(""" + SELECT c, COUNT(l) AS itemCount + FROM Customer c JOIN c.Orders o JOIN o.lineItems l + WHERE c.address.state = 'CA' + GROUP BY c + ORDER BY itemCount + """); + } + + @Test + void theRest12() { + + HqlQueryParser.parse(""" + SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest13() { + + HqlQueryParser.parse(""" + SELECT e.address AS addr + FROM Employee e + """); + } + + @Test + void theRest14() { + + HqlQueryParser.parse(""" + SELECT AVG(o.quantity) FROM Order o + """); + } + + @Test + void theRest15() { + + HqlQueryParser.parse(""" + SELECT SUM(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest16() { + + HqlQueryParser.parse(""" + SELECT COUNT(o) FROM Order o + """); + } + + @Test + void theRest17() { + + HqlQueryParser.parse(""" + SELECT COUNT(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest18() { + + HqlQueryParser.parse(""" + SELECT COUNT(l) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL + """); + } + + @Test + void theRest19() { + + HqlQueryParser.parse(""" + SELECT o + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity DESC, o.totalcost + """); + } + + @Test + void theRest20() { + + HqlQueryParser.parse(""" + SELECT o.quantity, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity, a.zipcode + """); + } + + @Test + void theRest21() { + + HqlQueryParser.parse(""" + SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' AND a.county = 'Santa Clara' + ORDER BY o.quantity, taxedCost, a.zipcode + """); + } + + @Test + void theRest22() { + + HqlQueryParser.parse(""" + SELECT AVG(o.quantity) as q, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + GROUP BY a.zipcode + ORDER BY q DESC + """); + } + + @Test + void theRest23() { + + HqlQueryParser.parse(""" + SELECT p.product_name + FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY p.price + """); + } + + /** + * This query is specifically dubbed illegal in the spec, but apparently works with Hibernate. + */ + @Test + void theRest24() { + + HqlQueryParser.parse(""" + SELECT p.product_name + FROM Order o, IN(o.lineItems) l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY o.quantity + """); + } + + @Test + void theRest25() { + + HqlQueryParser.parse(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + """); + } + + @Test + void theRest26() { + + HqlQueryParser.parse(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + AND c.orders IS EMPTY + """); + } + + @Test + void theRest27() { + + HqlQueryParser.parse(""" + UPDATE Customer c + SET c.status = 'outstanding' + WHERE c.balance < 10000 + """); + } + + @Test + void theRest28() { + + HqlQueryParser.parse(""" + UPDATE Employee e + SET e.address.building = 22 + WHERE e.address.building = 14 + AND e.address.city = 'Santa Clara' + AND e.project = 'Jakarta EE' + """); + } + + @Test + void theRest29() { + + HqlQueryParser.parse(""" + SELECT o + FROM Order o + """); + } + + @Test + void theRest30() { + + HqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.shippingAddress.state = 'CA' + """); + } + + @Test + void theRest31() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o.shippingAddress.state + FROM Order o + """); + } + + @Test + void theRest32() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + """); + } + + @Test + void theRest33() { + + HqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.lineItems IS NOT EMPTY + """); + } + + @Test + void theRest34() { + + HqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void theRest35() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.shipped = FALSE + """); + } + + @Test + void theRest36() { + + HqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE + NOT (o.shippingAddress.state = o.billingAddress.state AND + o.shippingAddress.city = o.billingAddress.city AND + o.shippingAddress.street = o.billingAddress.street) + """); + } + + @Test + void theRest37() { + + HqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.shippingAddress <> o.billingAddress + """); + } + + @Test + void theRest38() { + + HqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.name = ?1 + """); + } + + @Test + void hqlQueries() { + + HqlQueryParser.parse("from Person"); + HqlQueryParser.parse("select local datetime"); + HqlQueryParser.parse("from Person p select p.name"); + HqlQueryParser.parse("update Person set nickName = 'Nacho' " + // + "where name = 'Ignacio'"); + HqlQueryParser.parse("update Person p " + // + "set p.name = :newName " + // + "where p.name = :oldName"); + HqlQueryParser.parse("update Person " + // + "set name = :newName " + // + "where name = :oldName"); + HqlQueryParser.parse("update versioned Person " + // + "set name = :newName " + // + "where name = :oldName"); + HqlQueryParser.parse("insert Person (id, name) " + // + "values (100L, 'Jane Doe')"); + HqlQueryParser.parse("insert Person (id, name) " + // + "values (101L, 'J A Doe III'), " + // + "(102L, 'J X Doe'), " + // + "(103L, 'John Doe, Jr')"); + HqlQueryParser.parse("insert into Partner (id, name) " + // + "select p.id, p.name " + // + "from Person p "); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.name like 'Joe'"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.name like 'Joe''s'"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.id = 1"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.id = 1L"); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "where c.duration > 100.5"); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "where c.duration > 100.5F"); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "where c.duration > 1e+2"); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "where c.duration > 1e+2F"); + HqlQueryParser.parse("from Phone ph " + // + "where ph.type = LAND_LINE"); + HqlQueryParser.parse("select java.lang.Math.PI"); + HqlQueryParser.parse("select 'Customer ' || p.name " + // + "from Person p " + // + "where p.id = 1"); + HqlQueryParser.parse("select sum(ch.duration) * :multiplier " + // + "from Person pr " + // + "join pr.phones ph " + // + "join ph.callHistory ch " + // + "where ph.id = 1L "); + HqlQueryParser.parse("select year(local date) - year(p.createdOn) " + // + "from Person p " + // + "where p.id = 1L"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where year(local date) - year(p.createdOn) > 1"); + HqlQueryParser.parse("select " + // + " case p.nickName " + // + " when 'NA' " + // + " then '' " + // + " else p.nickName " + // + " end " + // + "from Person p"); + HqlQueryParser.parse("select " + // + " case " + // + " when p.nickName is null " + // + " then " + // + " case " + // + " when p.name is null " + // + " then '' " + // + " else p.name " + // + " end" + // + " else p.nickName " + // + " end " + // + "from Person p"); + HqlQueryParser.parse("select " + // + " case when p.nickName is null " + // + " then p.id * 1000 " + // + " else p.id " + // + " end " + // + "from Person p " + // + "order by p.id"); + HqlQueryParser.parse("select p " + // + "from Payment p " + // + "where type(p) = CreditCardPayment"); + HqlQueryParser.parse("select p " + // + "from Payment p " + // + "where type(p) = :type"); + HqlQueryParser.parse("select p " + // + "from Payment p " + // + "where length(treat(p as CreditCardPayment).cardNumber) between 16 and 20"); + HqlQueryParser.parse("select nullif(p.nickName, p.name) " + // + "from Person p"); + HqlQueryParser.parse("select " + // + " case" + // + " when p.nickName = p.name" + // + " then null" + // + " else p.nickName" + // + " end " + // + "from Person p"); + HqlQueryParser.parse("select coalesce(p.nickName, '') " + // + "from Person p"); + HqlQueryParser.parse("select coalesce(p.nickName, p.name, '') " + // + "from Person p"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where size(p.phones) >= 2"); + HqlQueryParser.parse("select concat(p.number, ' : ' , cast(c.duration as string)) " + // + "from Call c " + // + "join c.phone p"); + HqlQueryParser.parse("select substring(p.number, 1, 2) " + // + "from Call c " + // + "join c.phone p"); + HqlQueryParser.parse("select upper(p.name) " + // + "from Person p "); + HqlQueryParser.parse("select lower(p.name) " + // + "from Person p "); + HqlQueryParser.parse("select trim(p.name) " + // + "from Person p "); + HqlQueryParser.parse("select trim(leading ' ' from p.name) " + // + "from Person p "); + HqlQueryParser.parse("select length(p.name) " + // + "from Person p "); + HqlQueryParser.parse("select locate('John', p.name) " + // + "from Person p "); + HqlQueryParser.parse("select abs(c.duration) " + // + "from Call c "); + HqlQueryParser.parse("select mod(c.duration, 10) " + // + "from Call c "); + HqlQueryParser.parse("select sqrt(c.duration) " + // + "from Call c "); + HqlQueryParser.parse("select cast(c.duration as String) " + // + "from Call c "); + HqlQueryParser.parse("select str(c.timestamp) " + // + "from Call c "); + HqlQueryParser.parse("select str(cast(duration as float) / 60, 4, 2) " + // + "from Call c "); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "where extract(date from c.timestamp) = local date"); + HqlQueryParser.parse("select extract(year from c.timestamp) " + // + "from Call c "); + HqlQueryParser.parse("select year(c.timestamp) " + // + "from Call c "); + HqlQueryParser.parse("select var_samp(c.duration) as sampvar, var_pop(c.duration) as popvar " + // + "from Call c "); + HqlQueryParser.parse("select bit_length(c.phone.number) " + // + "from Call c "); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "where c.duration < 30 "); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.name like 'John%' "); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.createdOn > '1950-01-01' "); + HqlQueryParser.parse("select p " + // + "from Phone p " + // + "where p.type = 'MOBILE' "); + HqlQueryParser.parse("select p " + // + "from Payment p " + // + "where p.completed = true "); + HqlQueryParser.parse("select p " + // + "from Payment p " + // + "where type(p) = WireTransferPayment "); + HqlQueryParser.parse("select p " + // + "from Payment p, Phone ph " + // + "where p.person = ph.person "); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "join p.phones ph " + // + "where p.id = 1L and index(ph) between 0 and 3"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.createdOn between '1999-01-01' and '2001-01-02'"); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "where c.duration between 5 and 20"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.name between 'H' and 'M'"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.nickName is not null"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.nickName is null"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.name like 'Jo%'"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.name not like 'Jo%'"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.name like 'Dr|_%' escape '|'"); + HqlQueryParser.parse("select p " + // + "from Payment p " + // + "where type(p) in (CreditCardPayment, WireTransferPayment)"); + HqlQueryParser.parse("select p " + // + "from Phone p " + // + "where type in ('MOBILE', 'LAND_LINE')"); + HqlQueryParser.parse("select p " + // + "from Phone p " + // + "where type in :types"); + HqlQueryParser.parse("select distinct p " + // + "from Phone p " + // + "where p.person.id in (" + // + " select py.person.id " + // + " from Payment py" + // + " where py.completed = true and py.amount > 50 " + // + ")"); + HqlQueryParser.parse("select distinct p " + // + "from Phone p " + // + "where p.person in (" + // + " select py.person " + // + " from Payment py" + // + " where py.completed = true and py.amount > 50 " + // + ")"); + HqlQueryParser.parse("select distinct p " + // + "from Payment p " + // + "where (p.amount, p.completed) in (" + // + " (50, true)," + // + " (100, true)," + // + " (5, false)" + // + ")"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where 1 in indices(p.phones)"); + HqlQueryParser.parse("select distinct p.person " + // + "from Phone p " + // + "join p.calls c " + // + "where 50 > all (" + // + " select duration" + // + " from Call" + // + " where phone = p " + // + ") "); + HqlQueryParser.parse("select p " + // + "from Phone p " + // + "where local date > all elements(p.repairTimestamps)"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where :phone = some elements(p.phones)"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where :phone member of p.phones"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where exists elements(p.phones)"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.phones is empty"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.phones is not empty"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.phones is not empty"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where 'Home address' member of p.addresses"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where 'Home address' not member of p.addresses"); + HqlQueryParser.parse("select p " + // + "from Person p"); + HqlQueryParser.parse("select p " + // + "from org.hibernate.userguide.model.Person p"); + HqlQueryParser.parse("select distinct pr, ph " + // + "from Person pr, Phone ph " + // + "where ph.person = pr and ph is not null"); + HqlQueryParser.parse("select distinct pr1 " + // + "from Person pr1, Person pr2 " + // + "where pr1.id <> pr2.id " + // + " and pr1.address = pr2.address " + // + " and pr1.createdOn < pr2.createdOn"); + HqlQueryParser.parse("select distinct pr, ph " + // + "from Person pr cross join Phone ph " + // + "where ph.person = pr and ph is not null"); + HqlQueryParser.parse("select p " + // + "from Payment p "); + HqlQueryParser.parse("select d.owner, d.payed " + // + "from (" + // + " select p.person as owner, c.payment is not null as payed " + // + " from Call c " + // + " join c.phone p " + // + " where p.number = :phoneNumber) d"); + HqlQueryParser.parse("select distinct pr " + // + "from Person pr " + // + "join Phone ph on ph.person = pr " + // + "where ph.type = :phoneType"); + HqlQueryParser.parse("select distinct pr " + // + "from Person pr " + // + "join pr.phones ph " + // + "where ph.type = :phoneType"); + HqlQueryParser.parse("select distinct pr " + // + "from Person pr " + // + "inner join pr.phones ph " + // + "where ph.type = :phoneType"); + HqlQueryParser.parse("select distinct pr " + // + "from Person pr " + // + "left join pr.phones ph " + // + "where ph is null " + // + " or ph.type = :phoneType"); + HqlQueryParser.parse("select distinct pr " + // + "from Person pr " + // + "left outer join pr.phones ph " + // + "where ph is null " + // + " or ph.type = :phoneType"); + HqlQueryParser.parse("select pr.name, ph.number " + // + "from Person pr " + // + "left join pr.phones ph with ph.type = :phoneType "); + HqlQueryParser.parse("select pr.name, ph.number " + // + "from Person pr " + // + "left join pr.phones ph on ph.type = :phoneType "); + HqlQueryParser.parse("select distinct pr " + // + "from Person pr " + // + "left join fetch pr.phones "); + HqlQueryParser.parse("select a, ccp " + // + "from Account a " + // + "join treat(a.payments as CreditCardPayment) ccp " + // + "where length(ccp.cardNumber) between 16 and 20"); + HqlQueryParser.parse("select c, ccp " + // + "from Call c " + // + "join treat(c.payment as CreditCardPayment) ccp " + // + "where length(ccp.cardNumber) between 16 and 20"); + HqlQueryParser.parse("select longest.duration " + // + "from Phone p " + // + "left join lateral (" + // + " select c.duration as duration " + // + " from p.calls c" + // + " order by c.duration desc" + // + " limit 1 " + // + " ) longest " + // + "where p.number = :phoneNumber"); + HqlQueryParser.parse("select ph " + // + "from Phone ph " + // + "where ph.person.address = :address "); + HqlQueryParser.parse("select ph " + // + "from Phone ph " + // + "join ph.person pr " + // + "where pr.address = :address "); + HqlQueryParser.parse("select ph " + // + "from Phone ph " + // + "where ph.person.address = :address " + // + " and ph.person.createdOn > :timestamp"); + HqlQueryParser.parse("select ph " + // + "from Phone ph " + // + "inner join ph.person pr " + // + "where pr.address = :address " + // + " and pr.createdOn > :timestamp"); + HqlQueryParser.parse("select ph " + // + "from Person pr " + // + "join pr.phones ph " + // + "join ph.calls c " + // + "where pr.address = :address " + // + " and c.duration > :duration"); + HqlQueryParser.parse("select ch " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + HqlQueryParser.parse("select value(ch) " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + HqlQueryParser.parse("select key(ch) " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + HqlQueryParser.parse("select key(ch) " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + HqlQueryParser.parse("select entry(ch) " + // + "from Phone ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id "); + HqlQueryParser.parse("select sum(ch.duration) " + // + "from Person pr " + // + "join pr.phones ph " + // + "join ph.callHistory ch " + // + "where ph.id = :id " + // + " and index(ph) = :phoneIndex"); + HqlQueryParser.parse("select value(ph.callHistory) " + // + "from Phone ph " + // + "where ph.id = :id "); + HqlQueryParser.parse("select key(ph.callHistory) " + // + "from Phone ph " + // + "where ph.id = :id "); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.phones[0].type = LAND_LINE"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where p.addresses['HOME'] = :address"); + HqlQueryParser.parse("select pr " + // + "from Person pr " + // + "where pr.phones[max(indices(pr.phones))].type = 'LAND_LINE'"); + HqlQueryParser.parse("select p.name, p.nickName " + // + "from Person p "); + HqlQueryParser.parse("select p.name as name, p.nickName as nickName " + // + "from Person p "); + HqlQueryParser.parse("select new org.hibernate.userguide.hql.CallStatistics(" + // + " count(c), " + // + " sum(c.duration), " + // + " min(c.duration), " + // + " max(c.duration), " + // + " avg(c.duration)" + // + ") " + // + "from Call c "); + HqlQueryParser.parse("select new map(" + // + " p.number as phoneNumber , " + // + " sum(c.duration) as totalDuration, " + // + " avg(c.duration) as averageDuration " + // + ") " + // + "from Call c " + // + "join c.phone p " + // + "group by p.number "); + HqlQueryParser.parse("select new list(" + // + " p.number, " + // + " c.duration " + // + ") " + // + "from Call c " + // + "join c.phone p "); + HqlQueryParser.parse("select distinct p.lastName " + // + "from Person p"); + HqlQueryParser.parse("select " + // + " count(c), " + // + " sum(c.duration), " + // + " min(c.duration), " + // + " max(c.duration), " + // + " avg(c.duration) " + // + "from Call c "); + HqlQueryParser.parse("select count(distinct c.phone) " + // + "from Call c "); + HqlQueryParser.parse("select p.number, count(c) " + // + "from Call c " + // + "join c.phone p " + // + "group by p.number"); + HqlQueryParser.parse("select p " + // + "from Phone p " + // + "where max(elements(p.calls)) = :call"); + HqlQueryParser.parse("select p " + // + "from Phone p " + // + "where min(elements(p.calls)) = :call"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "where max(indices(p.phones)) = 0"); + HqlQueryParser.parse("select count(c) filter (where c.duration < 30) " + // + "from Call c "); + HqlQueryParser.parse("select p.number, count(c) filter (where c.duration < 30) " + // + "from Call c " + // + "join c.phone p " + // + "group by p.number"); + HqlQueryParser.parse("select listagg(p.number, ', ') within group (order by p.type,p.number) " + // + "from Phone p " + // + "group by p.person"); + HqlQueryParser.parse("select sum(c.duration) " + // + "from Call c "); + HqlQueryParser.parse("select p.name, sum(c.duration) " + // + "from Call c " + // + "join c.phone ph " + // + "join ph.person p " + // + "group by p.name"); + HqlQueryParser.parse("select p, sum(c.duration) " + // + "from Call c " + // + "join c.phone ph " + // + "join ph.person p " + // + "group by p"); + HqlQueryParser.parse("select p.name, sum(c.duration) " + // + "from Call c " + // + "join c.phone ph " + // + "join ph.person p " + // + "group by p.name " + // + "having sum(c.duration) > 1000"); + HqlQueryParser.parse("select p.name from Person p " + // + "union " + // + "select p.nickName from Person p where p.nickName is not null"); + HqlQueryParser.parse("select p " + // + "from Person p " + // + "order by p.name"); + HqlQueryParser.parse("select p.name, sum(c.duration) as total " + // + "from Call c " + // + "join c.phone ph " + // + "join ph.person p " + // + "group by p.name " + // + "order by total"); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "join c.phone p " + // + "order by p.number " + // + "limit 50"); + HqlQueryParser.parse("select c " + // + "from Call c " + // + "join c.phone p " + // + "order by p.number " + // + "fetch first 50 rows only"); + HqlQueryParser.parse("select p " + // + "from Phone p " + // + "join fetch p.calls " + // + "order by p " + // + "limit 50"); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java index a425dbce4f..41355c6e91 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java @@ -16,7 +16,6 @@ package org.springframework.data.jpa.repository.query; import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import jakarta.persistence.EntityManager; @@ -26,7 +25,9 @@ import java.lang.reflect.Method; import java.util.List; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -122,10 +123,10 @@ void considersNamedCountQuery() throws Exception { EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); when(namedQueries.hasQuery("foo.count")).thenReturn(true); - when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); + when(namedQueries.getQuery("foo.count")).thenReturn("select count(foo) from Foo foo"); when(namedQueries.hasQuery("User.findByNamedQuery")).thenReturn(true); - when(namedQueries.getQuery("User.findByNamedQuery")).thenReturn("select foo"); + when(namedQueries.getQuery("User.findByNamedQuery")).thenReturn("select foo from Foo foo"); Method method = UserRepository.class.getMethod("findByNamedQuery", String.class, Pageable.class); RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); @@ -133,8 +134,8 @@ void considersNamedCountQuery() throws Exception { RepositoryQuery repositoryQuery = strategy.resolveQuery(method, metadata, projectionFactory, namedQueries); assertThat(repositoryQuery).isInstanceOf(SimpleJpaQuery.class); SimpleJpaQuery query = (SimpleJpaQuery) repositoryQuery; - assertThat(query.getQuery().getQueryString()).isEqualTo("select foo"); - assertThat(query.getCountQuery().getQueryString()).isEqualTo("foo count"); + assertThat(query.getQuery().getQueryString()).isEqualTo("select foo from Foo foo"); + assertThat(query.getCountQuery().getQueryString()).isEqualTo("select count(foo) from Foo foo"); } @Test // GH-2217 @@ -144,7 +145,7 @@ void considersNamedCountOnStringQueryQuery() throws Exception { EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT); when(namedQueries.hasQuery("foo.count")).thenReturn(true); - when(namedQueries.getQuery("foo.count")).thenReturn("foo count"); + when(namedQueries.getQuery("foo.count")).thenReturn("select count(foo) from Foo foo"); Method method = UserRepository.class.getMethod("findByStringQueryWithNamedCountQuery", String.class, Pageable.class); @@ -153,7 +154,7 @@ void considersNamedCountOnStringQueryQuery() throws Exception { RepositoryQuery repositoryQuery = strategy.resolveQuery(method, metadata, projectionFactory, namedQueries); assertThat(repositoryQuery).isInstanceOf(SimpleJpaQuery.class); SimpleJpaQuery query = (SimpleJpaQuery) repositoryQuery; - assertThat(query.getCountQuery().getQueryString()).isEqualTo("foo count"); + assertThat(query.getCountQuery().getQueryString()).isEqualTo("select count(foo) from Foo foo"); } @Test // GH-2319 @@ -193,6 +194,7 @@ void noQueryShouldNotBeInvoked() { assertThatIllegalStateException().isThrownBy(() -> query.getQueryMethod()); } + @Disabled("invalid to both JpqlParse and to JSqlParser") @Test // GH-2551 void customQueryWithQuestionMarksShouldWork() throws NoSuchMethodException { @@ -240,7 +242,7 @@ interface UserRepository extends Repository { @Query(countName = "foo.count") Page findByNamedQuery(String foo, Pageable pageable); - @Query(value = "foo.query", countName = "foo.count") + @Query(value = "select foo from Foo foo", countName = "foo.count") Page findByStringQueryWithNamedCountQuery(String foo, Pageable pageable); @Query(value = "something absurd", name = "my-query-name") diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java new file mode 100644 index 0000000000..0d65402c33 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assumptions.*; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * TCK Tests for {@link JpqlQueryParser} mixed into {@link JpaQueryParsingEnhancer}. + * + * @author Greg Turnquist + * @since 3.1 + */ +public class JpqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { + + public static final String JPQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES = "JpqlParser does not support native queries"; + + @Override + QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { + return new JpaQueryParsingEnhancer(new JpqlQueryParser(declaredQuery)); + } + + @Override + @ParameterizedTest // GH-2773 + @MethodSource("jpqlCountQueries") + void shouldDeriveJpqlCountQuery(String query, String expected) { + + assumeThat(query).as("JpqlParser replaces the column name with alias name for count queries") // + .doesNotContain("SELECT name FROM table_name some_alias"); + + assumeThat(query).as("JpqlParser does not support simple JPQL syntax") // + .doesNotStartWithIgnoringCase("FROM"); + + assumeThat(expected).as("JpqlParser does turn 'select a.b' into 'select count(a.b)'") // + .doesNotContain("select count(a.b"); + + super.shouldDeriveJpqlCountQuery(query, expected); + } + + @Disabled(JPQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) + @Override + void findProjectionClauseWithIncludedFrom() {} + + @Disabled(JPQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) + @Override + void shouldDeriveNativeCountQuery(String query, String expected) {} + + @Disabled(JPQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) + @Override + void shouldDeriveNativeCountQueryWithVariable(String query, String expected) {} +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java new file mode 100644 index 0000000000..fecc3470ca --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -0,0 +1,917 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Tests built around examples of JPQL found in the JPA spec + * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc
    + *
    + * IMPORTANT: Purely verifies the parser without any transformations. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpqlQueryRendererTests { + + private static final String SPEC_FAULT = "Disabled due to spec fault> "; + + /** + * Parse the query using {@link HqlParser} then run it through the query-preserving {@link HqlQueryRenderer}. + * + * @param query + */ + private static String parseWithoutChanges(String query) { + + JpqlLexer lexer = new JpqlLexer(CharStreams.fromString(query)); + JpqlParser parser = new JpqlParser(new CommonTokenStream(lexer)); + + parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener()); + + JpqlParser.StartContext parsedQuery = parser.start(); + + return render(new JpqlQueryRenderer().visit(parsedQuery)); + } + + private void assertQuery(String query) { + + String slimmedDownQuery = reduceWhitespace(query); + assertThat(parseWithoutChanges(slimmedDownQuery)).isEqualTo(slimmedDownQuery); + } + + private String reduceWhitespace(String original) { + + return original // + .replaceAll("[ \\t\\n]{1,}", " ") // + .trim(); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + */ + @Test + void joinExample1() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order AS o JOIN o.lineItems AS l + WHERE l.shipped = FALSE + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables + */ + @Test + void joinExample2() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l JOIN l.product p + WHERE p.productType = 'office_supplies' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations + */ + @Test + void rangeVariableDeclarations() { + + assertQuery(""" + SELECT DISTINCT o1 + FROM Order o1, Order o2 + WHERE o1.quantity > o2.quantity AND + o2.customer.lastname = 'Smith' AND + o2.customer.firstname = 'John' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample1() { + + assertQuery(""" + SELECT i.name, VALUE(p) + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample2() { + + assertQuery(""" + SELECT i.name, p + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample3() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo.phones p + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample4() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE e.contactInfo.address.zipcode = '95054' + """); + } + + @Test + void pathExpressionSyntaxExample1() { + + assertQuery(""" + SELECT DISTINCT l.product + FROM Order AS o JOIN o.lineItems l + """); + } + + @Test + void joinsExample1() { + + assertQuery(""" + SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize + """); + } + + @Test + void joinsExample2() { + + assertQuery(""" + SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInnerExample() { + + assertQuery(""" + SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInExample() { + + assertQuery(""" + SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 + """); + } + + @Test + void doubleJoinExample() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE c.address.zipcode = '95054' + """); + } + + @Test + void leftJoinExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + GROUP BY s.name + """); + } + + @Test + void leftJoinOnExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + ON p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinWhereExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + WHERE p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinFetchExample() { + + assertQuery(""" + SELECT d + FROM Department d LEFT JOIN FETCH d.employees + WHERE d.deptno = 1 + """); + } + + @Test + void collectionMemberExample() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void collectionMemberInExample() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o, IN(o.lineItems) l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void fromClauseExample() { + + assertQuery(""" + SELECT o + FROM Order AS o JOIN o.lineItems l JOIN l.product p + """); + } + + @Test + void fromClauseDowncastingExample1() { + + assertQuery(""" + SELECT b.name, b.ISBN + FROM Order o JOIN TREAT(o.product AS Book) b + """); + } + + @Test + void fromClauseDowncastingExample2() { + + assertQuery(""" + SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp + WHERE lp.budget > 1000 + """); + } + + /** + * @see #fromClauseDowncastingExample3fixed() + */ + @Test + @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") + void fromClauseDowncastingExample3_SPEC_BUG() { + + assertQuery(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE "cost overrun" + """); + } + + @Test + void fromClauseDowncastingExample3fixed() { + + assertQuery(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE 'cost overrun' + """); + } + + @Test + void fromClauseDowncastingExample4() { + + assertQuery(""" + SELECT e FROM Employee e + WHERE TREAT(e AS Exempt).vacationDays > 10 + OR TREAT(e AS Contractor).hours > 100 + """); + } + + @Test + void pathExpressionsNamedParametersExample() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.status = :stat + """); + } + + @Test + void betweenExpressionsExample() { + + assertQuery(""" + SELECT t + FROM CreditCard c JOIN c.transactionHistory t + WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 + """); + } + + @Test + void isEmptyExample() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void memberOfExample() { + + assertQuery(""" + SELECT p + FROM Person p + WHERE 'Joe' MEMBER OF p.nicknames + """); + } + + @Test + void existsSubSelectExample1() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS (SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void allExample() { + + assertQuery(""" + SELECT emp + FROM Employee emp + WHERE emp.salary > ALL (SELECT m.salary + FROM Manager m + WHERE m.department = emp.department) + """); + } + + @Test + void existsSubSelectExample2() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS (SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void subselectNumericComparisonExample1() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 + """); + } + + @Test + void subselectNumericComparisonExample2() { + + assertQuery(""" + SELECT goodCustomer + FROM Customer goodCustomer + WHERE goodCustomer.balanceOwed < (SELECT AVG(c.balanceOwed)/2.0 FROM Customer c) + """); + } + + @Test + void indexExample() { + + assertQuery(""" + SELECT w.name + FROM Course c JOIN c.studentWaitlist w + WHERE c.name = 'Calculus' + AND INDEX(w) = 0 + """); + } + + /** + * @see #functionInvocationExampleWithCorrection() + */ + @Test + @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") + void functionInvocationExample_SPEC_BUG() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) + """); + } + + @Test + void functionInvocationExampleWithCorrection() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE + """); + } + + @Test + void updateCaseExample1() { + + assertQuery(""" + UPDATE Employee e + SET e.salary = + CASE WHEN e.rating = 1 THEN e.salary*1.1 + WHEN e.rating = 2 THEN e.salary*1.05 + ELSE e.salary*1.01 + END + """); + } + + @Test + void updateCaseExample2() { + + assertQuery(""" + UPDATE Employee e + SET e.salary = + CASE e.rating WHEN 1 THEN e.salary*1.1 + WHEN 2 THEN e.salary*1.05 + ELSE e.salary*1.01 + END + """); + } + + @Test + void selectCaseExample1() { + + assertQuery(""" + SELECT e.name, + CASE TYPE(e) WHEN Exempt THEN 'Exempt' + WHEN Contractor THEN 'Contractor' + WHEN Intern THEN 'Intern' + ELSE 'NonExempt' + END + FROM Employee e + WHERE e.dept.name = 'Engineering' + """); + } + + @Test + void selectCaseExample2() { + + assertQuery(""" + SELECT e.name, + f.name, + CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' + WHEN f.annualMiles > 25000 THEN 'Gold ' + ELSE '' + END, + 'Frequent Flyer') + FROM Employee e JOIN e.frequentFlierPlan f + """); + } + + @Test + void theRest() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (Exempt, Contractor) + """); + } + + @Test + void theRest2() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (:empType1, :empType2) + """); + } + + @Test + void theRest3() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN :empTypes + """); + } + + @Test + void theRest4() { + + assertQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) <> Exempt + """); + } + + @Test + void theRest5() { + + assertQuery(""" + SELECT c.status, AVG(c.filledOrderCount), COUNT(c) + FROM Customer c + GROUP BY c.status + HAVING c.status IN (1, 2) + """); + } + + @Test + void theRest6() { + + assertQuery(""" + SELECT c.country, COUNT(c) + FROM Customer c + GROUP BY c.country + HAVING COUNT(c) > 30 + """); + } + + @Test + void theRest7() { + + assertQuery(""" + SELECT c, COUNT(o) + FROM Customer c JOIN c.orders o + GROUP BY c + HAVING COUNT(o) >= 5 + """); + } + + @Test + void theRest8() { + + assertQuery(""" + SELECT c.id, c.status + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest9() { + + assertQuery(""" + SELECT v.location.street, KEY(i).title, VALUE(i) + FROM VideoStore v JOIN v.videoInventory i + WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 + """); + } + + @Test + void theRest10() { + + assertQuery(""" + SELECT o.lineItems FROM Order AS o + """); + } + + @Test + void theRest11() { + + assertQuery(""" + SELECT c, COUNT(l) AS itemCount + FROM Customer c JOIN c.Orders o JOIN o.lineItems l + WHERE c.address.state = 'CA' + GROUP BY c + ORDER BY itemCount + """); + } + + @Test + void theRest12() { + + assertQuery(""" + SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest13() { + + assertQuery(""" + SELECT e.address AS addr + FROM Employee e + """); + } + + @Test + void theRest14() { + + assertQuery(""" + SELECT AVG(o.quantity) FROM Order o + """); + } + + @Test + void theRest15() { + + assertQuery(""" + SELECT SUM(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest16() { + + assertQuery(""" + SELECT COUNT(o) FROM Order o + """); + } + + @Test + void theRest17() { + + assertQuery(""" + SELECT COUNT(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest18() { + + assertQuery(""" + SELECT COUNT(l) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL + """); + } + + @Test + void theRest19() { + + assertQuery(""" + SELECT o + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity DESC, o.totalcost + """); + } + + @Test + void theRest20() { + + assertQuery(""" + SELECT o.quantity, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity, a.zipcode + """); + } + + @Test + void theRest21() { + + assertQuery(""" + SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' AND a.county = 'Santa Clara' + ORDER BY o.quantity, taxedCost, a.zipcode + """); + } + + @Test + void theRest22() { + + assertQuery(""" + SELECT AVG(o.quantity) as q, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + GROUP BY a.zipcode + ORDER BY q DESC + """); + } + + @Test + void theRest23() { + + assertQuery(""" + SELECT p.product_name + FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY p.price + """); + } + + /** + * This query is specifically dubbed illegal in the spec. It may actually be failing for a different reason. + */ + @Test + void theRest24() { + + assertThatExceptionOfType(JpaQueryParsingSyntaxError.class).isThrownBy(() -> { + assertQuery(""" + SELECT p.product_name + FROM Order o, IN(o.lineItems) l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY o.quantity + """); + }); + } + + @Test + void theRest25() { + + assertQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + """); + } + + @Test + void theRest26() { + + assertQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + AND c.orders IS EMPTY + """); + } + + @Test + void theRest27() { + + assertQuery(""" + UPDATE Customer c + SET c.status = 'outstanding' + WHERE c.balance < 10000 + """); + } + + @Test + void theRest28() { + + assertQuery(""" + UPDATE Employee e + SET e.address.building = 22 + WHERE e.address.building = 14 + AND e.address.city = 'Santa Clara' + AND e.project = 'Jakarta EE' + """); + } + + @Test + void theRest29() { + + assertQuery(""" + SELECT o + FROM Order o + """); + } + + @Test + void theRest30() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.shippingAddress.state = 'CA' + """); + } + + @Test + void theRest31() { + + assertQuery(""" + SELECT DISTINCT o.shippingAddress.state + FROM Order o + """); + } + + @Test + void theRest32() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + """); + } + + @Test + void theRest33() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS NOT EMPTY + """); + } + + @Test + void theRest34() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void theRest35() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.shipped = FALSE + """); + } + + @Test + void theRest36() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE + NOT (o.shippingAddress.state = o.billingAddress.state AND + o.shippingAddress.city = o.billingAddress.city AND + o.shippingAddress.street = o.billingAddress.street) + """); + } + + @Test + void theRest37() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.shippingAddress <> o.billingAddress + """); + } + + @Test + void theRest38() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.name = ?1 + """); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java new file mode 100644 index 0000000000..c09469742a --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java @@ -0,0 +1,704 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import java.util.regex.Pattern; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.JpaSort; +import org.springframework.lang.Nullable; + +/** + * Verify that JPQL queries are properly transformed through the {@link JpaQueryParsingEnhancer} and the + * {@link JpqlQueryParser}. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpqlQueryTransformerTests { + + private static final String QUERY = "select u from User u"; + private static final String SIMPLE_QUERY = "select u from User u"; + private static final String COUNT_QUERY = "select count(u) from User u"; + + private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?1"; + private static final Pattern MULTI_WHITESPACE = Pattern.compile("\\s+"); + + @Test + void applyingSortShouldIntroduceOrderByCriteriaWhereNoneExists() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name"; + var sort = Sort.by("first_name", "last_name"); + + // when + var results = createQueryFor(original, sort); + + // then + assertThat(original).doesNotContainIgnoringCase("order by"); + assertThat(results).contains("order by e.first_name asc, e.last_name asc"); + } + + @Test + void applyingSortShouldCreateAdditionalOrderByCriteria() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.role, e.hire_date"; + var sort = Sort.by("first_name", "last_name"); + + // when + var results = createQueryFor(original, sort); + + // then + assertThat(results).contains("ORDER BY e.role, e.hire_date, e.first_name asc, e.last_name asc"); + } + + @Test + void applyCountToSimpleQuery() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("SELECT count(e) FROM Employee e where e.name = :name"); + } + + @Test + void applyCountToMoreComplexQuery() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.modified_date"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("SELECT count(e) FROM Employee e where e.name = :name"); + } + + @Test + void applyCountToAlreadySorteQuery() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.modified_date"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("SELECT count(e) FROM Employee e where e.name = :name"); + } + + @Test + void multipleAliasesShouldBeGathered() { + + // given + var original = "select e from Employee e join e.manager m"; + + // when + var results = createQueryFor(original, null); + + // then + assertThat(results).isEqualTo("select e from Employee e join e.manager m"); + } + + @Test + void createsCountQueryCorrectly() { + assertCountQuery(QUERY, COUNT_QUERY); + } + + @Test + void createsCountQueriesCorrectlyForCapitalLetterJPQL() { + + assertCountQuery("select u FROM User u WHERE u.foo.bar = ?1", "select count(u) FROM User u WHERE u.foo.bar = ?1"); + assertCountQuery("SELECT u FROM User u where u.foo.bar = ?1", "SELECT count(u) FROM User u where u.foo.bar = ?1"); + } + + @Test + void createsCountQueryForDistinctQueries() { + + assertCountQuery("select distinct u from User u where u.foo = ?1", + "select count(distinct u) from User u where u.foo = ?1"); + } + + @Test + void createsCountQueryForConstructorQueries() { + + assertCountQuery("select distinct new com.example.User(u.name) from User u where u.foo = ?1", + "select count(distinct u) from User u where u.foo = ?1"); + } + + @Test + void createsCountQueryForJoins() { + + assertCountQuery("select distinct new com.User(u.name) from User u left outer join u.roles r WHERE r = ?1", + "select count(distinct u) from User u left outer join u.roles r WHERE r = ?1"); + } + + @Test + void createsCountQueryForQueriesWithSubSelects() { + + assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role r)", + "select count(u) from User u left outer join u.roles r where r in (select r from Role r)"); + } + + @Test + void createsCountQueryForAliasesCorrectly() { + assertCountQuery("select u from User as u", "select count(u) from User as u"); + } + + @Test + void allowsShortJpaSyntax() { + assertCountQuery(SIMPLE_QUERY, COUNT_QUERY); + } + + @Test // GH-2260 + void detectsAliasCorrectly() { + + assertThat(alias(QUERY)).isEqualTo("u"); + assertThat(alias(SIMPLE_QUERY)).isEqualTo("u"); + assertThat(alias(COUNT_QUERY)).isEqualTo("u"); + assertThat(alias(QUERY_WITH_AS)).isEqualTo("u"); + assertThat(alias("SELECT u FROM USER U")).isEqualTo("U"); + assertThat(alias("select u from User u")).isEqualTo("u"); + assertThat(alias("select new com.acme.UserDetails(u.id, u.name) from User u")).isEqualTo("u"); + assertThat(alias("select u from T05User u")).isEqualTo("u"); + assertThat(alias("select u from User u where not exists (select m from User m where m = u.manager) ")) + .isEqualTo("u"); + assertThat(alias("select u from User u where not exists (select u2 from User u2)")).isEqualTo("u"); + assertThat(alias( + "select u from User u where not exists (select u2 from User u2 where not exists (select u3 from User u3))")) + .isEqualTo("u"); + } + + @Test // GH-2557 + void applySortingAccountsForNewlinesInSubselect() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + assertThat(new JpaQueryParsingEnhancer(new JpqlQueryParser("select u\n" + // + "from user u\n" + // + "where exists (select u2\n" + // + "from user u2\n" + // + ")\n" + // + "")).applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + // + "from user u\n" + // + "where exists (select u2\n" + // + "from user u2\n" + // + ")\n" + // + " order by u.age desc"); + } + + @Test // GH-2563 + void aliasDetectionProperlyHandlesNewlinesInSubselects() { + + assertThat(alias(""" + SELECT o + FROM Order o + WHERE EXISTS( SELECT 1 + FROM Vehicle vehicle + WHERE vehicle.vehicleOrderId = o.id + AND LOWER(COALESCE(vehicle.make, '')) LIKE :query) + """)).isEqualTo("o"); + } + + @Test // DATAJPA-252 + void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { + + String query = "select p from Person p left join p.address address"; + Sort sort = Sort.by("address.city"); + assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc"); + } + + @Test // DATAJPA-252 + void extendsExistingOrderByClausesCorrectly() { + + String query = "select p from Person p order by p.lastname asc"; + Sort sort = Sort.by("firstname"); + assertThat(createQueryFor(query, sort)).endsWith("order by p.lastname asc, p.firstname asc"); + } + + @Test // DATAJPA-296 + void appliesIgnoreCaseOrderingCorrectly() { + + String query = "select p from Person p"; + Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); + + assertThat(createQueryFor(query, sort)).endsWith("order by lower(p.firstname) asc"); + } + + @Test // DATAJPA-296 + void appendsIgnoreCaseOrderingCorrectly() { + + String query = "select p from Person p order by p.lastname asc"; + Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); + + assertThat(createQueryFor(query, sort)) + .isEqualTo("select p from Person p order by p.lastname asc, lower(p.firstname) asc"); + } + + @Test // DATAJPA-342 + void usesReturnedVariableInCountProjectionIfSet() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", + "select count(distinct m.genre) from Media m where m.user = ?1"); + } + + @Test // DATAJPA-343 + void projectsCountQueriesForQueriesWithSubselects() { + + // given + var original = "select o from Foo o where cb.id in (select b from Bar b)"; + + // when + var results = createQueryFor(original, Sort.by("first_name", "last_name")); + + // then + assertThat(results).isEqualTo( + "select o from Foo o where cb.id in (select b from Bar b) order by o.first_name asc, o.last_name asc"); + + assertCountQuery("select o from Foo o where cb.id in (select b from Bar b)", + "select count(o) from Foo o where cb.id in (select b from Bar b)"); + } + + @Test // DATAJPA-148 + void doesNotPrefixSortsIfFunction() { + + Sort sort = Sort.by("sum(foo)"); + + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> createQueryFor("select p from Person p", sort)); + } + + @Test // DATAJPA-377 + void removesOrderByInGeneratedCountQueryFromOriginalQueryIfPresent() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 OrDer By m.genre ASC", + "select count(distinct m.genre) from Media m where m.user = ?1"); + } + + @Test // DATAJPA-375 + void findsExistingOrderByIndependentOfCase() { + + Sort sort = Sort.by("lastname"); + String query = createQueryFor("select p from Person p ORDER BY p.firstname", sort); + assertThat(query).endsWith("ORDER BY p.firstname, p.lastname asc"); + } + + @Test // DATAJPA-409 + void createsCountQueryForNestedReferenceCorrectly() { + assertCountQuery("select a.b from A a", "select count(a) from A a"); + } + + @Test // DATAJPA-420 + void createsCountQueryForScalarSelects() { + assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p"); + } + + @Test // DATAJPA-456 + void createCountQueryFromTheGivenCountProjection() { + + assertThat(createCountQueryFor("select p.lastname,p.firstname from Person p", "p.lastname")) + .isEqualTo("select count(p.lastname) from Person p"); + } + + @Test // DATAJPA-736 + void supportsNonAsciiCharactersInEntityNames() { + assertThat(createCountQueryFor("select u from Usèr u")).isEqualTo("select count(u) from Usèr u"); + } + + @Test // DATAJPA-798 + void detectsAliasInQueryContainingLineBreaks() { + assertThat(alias("select \n u \n from \n User \nu")).isEqualTo("u"); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionInDistinctQuery() { + assertThat(hasConstructorExpression("select distinct new com.example.Foo(b.name) from Bar b")).isTrue(); + } + + @Test // DATAJPA-938 + void detectsComplexConstructorExpression() { + + assertThat(hasConstructorExpression("select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) " // + + "from Bar lp join lp.investmentProduct ip " // + + "where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId " + // + + "group by ip.id, ip.name, lp.accountId " // + + "order by ip.name ASC")).isTrue(); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionWithLineBreaks() { + assertThat(hasConstructorExpression("select new foo.bar.FooBar(\na.id) from DtoA a ")).isTrue(); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotAllowWhitespaceInSort() { + + Sort sort = Sort.by("case when foo then bar"); + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> createQueryFor("select p from Person p", sort)); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixUnsafeJpaSortFunctionCalls() { + + JpaSort sort = JpaSort.unsafe("sum(foo)"); + assertThat(createQueryFor("select p from Person p", sort)).endsWith("order by sum(foo) asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixMultipleAliasedFunctionCalls() { + + String query = "SELECT AVG(m.price) AS avgPrice, SUM(m.stocks) AS sumStocks FROM Magazine m"; + Sort sort = Sort.by("avgPrice", "sumStocks"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc, sumStocks asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixSingleAliasedFunctionCalls() { + + String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("avgPrice"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { + + String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("someOtherProperty"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by m.someOtherProperty asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainsAliasedFunctionForDifferentProperty() { + + String query = "SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("name", "avgPrice"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by m.name asc, avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() { + + String query = "SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m"; + Sort sort = Sort.by("trimmedName"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by trimmedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() { + + String query = "SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m"; + Sort sort = Sort.by("extendedName"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by extendedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { + + String query = "SELECT AVG(m.price) AS avg_price FROM Magazine m"; + Sort sort = Sort.by("avg_price"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by avg_price asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWithDots() { + + String query = "SELECT AVG(m.price) AS m.avg FROM Magazine m"; + Sort sort = Sort.by("m.avg"); + + // TODO: Add support for aliased functions + // assertThat(query(query, (Sort) "m")).endsWith("order by m.avg asc"); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpaces() { + + String query = "SELECT AVG( m.price ) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("avgPrice"); + + // TODO: Add support for aliased functions + // assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc"); + } + + @Test // DATAJPA-1506 + void detectsAliasWithGroupAndOrderBy() { + + assertThat(alias("select * from User group by name")).isNull(); + assertThat(alias("select * from User order by name")).isNull(); + assertThat(alias("select u from User u group by name")).isEqualTo("u"); + assertThat(alias("select u from User u order by name")).isEqualTo("u"); + } + + @Test // DATAJPA-1500 + void createCountQuerySupportsWhitespaceCharacters() { + + assertThat(createCountQueryFor("select user from User user\n" + // + " where user.age = 18\n" + // + " order by user.name\n ")).isEqualToIgnoringWhitespace("select count(user) from User user\n" + // + " where user.age = 18\n "); + } + + @Test + void createCountQuerySupportsLineBreaksInSelectClause() { + + assertThat(createCountQueryFor("select user.age,\n" + // + " user.name\n" + // + " from User user\n" + // + " where user.age = 18\n" + // + " order\nby\nuser.name\n ")).isEqualToIgnoringWhitespace("select count(user) from User user\n" + // + " where user.age = 18\n "); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFieldAliases() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("authorName"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by m.authorName asc"); + } + + @Test // GH-2280 + void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { + + String query = "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer"; + Sort sort = Sort.by(Sort.Order.by("name").ignoreCase()); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).isEqualTo( + "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(customer.name) asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFunctionAliases() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("title"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by m.title asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForSimpleField() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("price"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by m.price asc"); + } + + @Test + void createCountQuerySupportsLineBreakRightAfterDistinct() { + + assertThat(createCountQueryFor("select\ndistinct\nuser.age,\n" + // + "user.name\n" + // + "from\nUser\nuser")).isEqualTo(createCountQueryFor("select\ndistinct user.age,\n" + // + "user.name\n" + // + "from\nUser\nuser")); + } + + @Test + void detectsAliasWithGroupAndOrderByWithLineBreaks() { + + assertThat(alias("select * from User group\nby name")).isNull(); + assertThat(alias("select * from User order\nby name")).isNull(); + assertThat(alias("select u from User u group\nby name")).isEqualTo("u"); + assertThat(alias("select u from User u order\nby name")).isEqualTo("u"); + assertThat(alias("select u from User\nu\norder \n by name")).isEqualTo("u"); + } + + @Test // DATAJPA-1679 + void findProjectionClauseWithDistinct() { + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(projection("select a,b,c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select a, b, c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select distinct a, b, c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select DISTINCT a, b, c from Entity x")).isEqualTo("a, b, c"); + }); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithSubselect() { + + // This is not a required behavior, in fact the opposite is, + // but it documents a current limitation. + // to fix this without breaking findProjectionClauseWithIncludedFrom we need a more sophisticated parser. + assertThat(projection("select * from (select x from y)")).isNotEqualTo("*"); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithIncludedFrom() { + assertThat(projection("select x, frommage, y from Element t")).isEqualTo("x, frommage, y"); + } + + @Test // GH-2341 + void countProjectionDistrinctQueryIncludesNewLineAfterFromAndBeforeJoin() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1\nLEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test // GH-2341 + void countProjectionDistinctQueryIncludesNewLineAfterEntity() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test // GH-2341 + void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key where entity1.id = 1799"); + } + + @Test // GH-2393 + void createCountQueryStartsWithWhitespace() { + + assertThat(createCountQueryFor(" \nselect u from User u where u.age > :age")) + .isEqualTo("select count(u) from User u where u.age > :age"); + + assertThat(createCountQueryFor(" \nselect u from User u where u.age > :age")) + .isEqualTo("select count(u) from User u where u.age > :age"); + } + + @Test // GH-2260 + void applySortingAccountsForNativeWindowFunction() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + // order by absent + assertThat(createQueryFor("select u from user u", sort)).isEqualTo("select u from user u order by u.age desc"); + + // order by present + assertThat(createQueryFor("select u from user u order by u.lastname", sort)) + .isEqualTo("select u from user u order by u.lastname, u.age desc"); + } + + @Test // GH-2511 + void countQueryUsesCorrectVariable() { + + assertThat(createCountQueryFor("SELECT e FROM User e WHERE created_at > $1")) + .isEqualTo("SELECT count(e) FROM User e WHERE created_at > $1"); + + assertThat( + createCountQueryFor("SELECT t FROM mytable t WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'")) + .isEqualTo("SELECT count(t) FROM mytable t WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); + + assertThat(createCountQueryFor("select s FROM users_statuses s WHERE (user_created_at BETWEEN $1 AND $2)")) + .isEqualTo("select count(s) FROM users_statuses s WHERE (user_created_at BETWEEN $1 AND $2)"); + + assertThat( + createCountQueryFor("SELECT us FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)")) + .isEqualTo("SELECT count(us) FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); + } + + @Test // GH-2496, GH-2522, GH-2537, GH-2045 + void orderByShouldWorkWithSubSelectStatements() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + assertThat(createQueryFor("select r " // + + "From DataRecord r " // + + "where " // + + " ( " // + + " r.adusrId = :userId " // + + " or EXISTS( select 1 FROM DataRecordDvsRight dr WHERE dr.adusrId = :userId AND dr.dataRecord = r ) " // + + ")", sort)).endsWith("order by r.age desc"); + + assertThat(createQueryFor("select distinct u " // + + "from FooBar u " // + + "where u.role = 'redacted' " // + + "and (" // + + " not exists (" // + + " select g from FooBarGroup g " // + + " where g in :excludedGroups " // + + " )" // + + ")", sort)).endsWith("order by u.age desc"); + + assertThat(createQueryFor("SELECT i " // + + "FROM Item i " // + + "WHERE i.id IN ( " // + + "SELECT max(i2.id) FROM Item i2 " // + + "WHERE i2.field.id = :fieldId " // + + "GROUP BY i2.field.id, i2.version)", sort)).endsWith("order by i.age desc"); + } + + private void assertCountQuery(String originalQuery, String countQuery) { + assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); + } + + private String createQueryFor(String query, Sort sort) { + return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).applySorting(sort); + } + + private String createCountQueryFor(String query) { + return createCountQueryFor(query, null); + } + + private String createCountQueryFor(String original, @Nullable String countProjection) { + return new JpaQueryParsingEnhancer(new JpqlQueryParser(original)).createCountQueryFor(countProjection); + } + + private String alias(String query) { + return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).detectAlias(); + } + + private boolean hasConstructorExpression(String query) { + return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).hasConstructorExpression(); + } + + private String projection(String query) { + return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).getProjection(); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java new file mode 100644 index 0000000000..1451e72ced --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java @@ -0,0 +1,888 @@ +/* + * Copyright 2022-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Tests built around examples of JPQL found in the JPA spec + * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc
    + *
    + * IMPORTANT: Purely verifies the parser without any transformations. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpqlSpecificationTests { + + private static final String SPEC_FAULT = "Disabled due to spec fault> "; + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + */ + @Test + void joinExample1() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order AS o JOIN o.lineItems AS l + WHERE l.shipped = FALSE + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables + */ + @Test + void joinExample2() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l JOIN l.product p + WHERE p.productType = 'office_supplies' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations + */ + @Test + void rangeVariableDeclarations() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o1 + FROM Order o1, Order o2 + WHERE o1.quantity > o2.quantity AND + o2.customer.lastname = 'Smith' AND + o2.customer.firstname= 'John' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample1() { + + JpqlQueryParser.parse(""" + SELECT i.name, VALUE(p) + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample2() { + + JpqlQueryParser.parse(""" + SELECT i.name, p + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample3() { + + JpqlQueryParser.parse(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo.phones p + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample4() { + + JpqlQueryParser.parse(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE e.contactInfo.address.zipcode = '95054' + """); + } + + @Test + void pathExpressionSyntaxExample1() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT l.product + FROM Order AS o JOIN o.lineItems l + """); + } + + @Test + void joinsExample1() { + + JpqlQueryParser.parse(""" + SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize + """); + } + + @Test + void joinsExample2() { + + JpqlQueryParser.parse(""" + SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInnerExample() { + + JpqlQueryParser.parse(""" + SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInExample() { + + JpqlQueryParser.parse(""" + SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 + """); + } + + @Test + void doubleJoinExample() { + + JpqlQueryParser.parse(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE c.address.zipcode = '95054' + """); + } + + @Test + void leftJoinExample() { + + JpqlQueryParser.parse(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + GROUP BY s.name + """); + } + + @Test + void leftJoinOnExample() { + + JpqlQueryParser.parse(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + ON p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinWhereExample() { + + JpqlQueryParser.parse(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + WHERE p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinFetchExample() { + + JpqlQueryParser.parse(""" + SELECT d + FROM Department d LEFT JOIN FETCH d.employees + WHERE d.deptno = 1 + """); + } + + @Test + void collectionMemberExample() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void collectionMemberInExample() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o, IN(o.lineItems) l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void fromClauseExample() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Order AS o JOIN o.lineItems l JOIN l.product p + """); + } + + @Test + void fromClauseDowncastingExample1() { + + JpqlQueryParser.parse(""" + SELECT b.name, b.ISBN + FROM Order o JOIN TREAT(o.product AS Book) b + """); + } + + @Test + void fromClauseDowncastingExample2() { + + JpqlQueryParser.parse(""" + SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp + WHERE lp.budget > 1000 + """); + } + + /** + * @see #fromClauseDowncastingExample3fixed() + */ + @Test + @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") + void fromClauseDowncastingExample3_SPEC_BUG() { + + JpqlQueryParser.parse(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE "cost overrun" + """); + } + + @Test + void fromClauseDowncastingExample3fixed() { + + JpqlQueryParser.parse(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE 'cost overrun' + """); + } + + @Test + void fromClauseDowncastingExample4() { + + JpqlQueryParser.parse(""" + SELECT e FROM Employee e + WHERE TREAT(e AS Exempt).vacationDays > 10 + OR TREAT(e AS Contractor).hours > 100 + """); + } + + @Test + void pathExpressionsNamedParametersExample() { + + JpqlQueryParser.parse(""" + SELECT c + FROM Customer c + WHERE c.status = :stat + """); + } + + @Test + void betweenExpressionsExample() { + + JpqlQueryParser.parse(""" + SELECT t + FROM CreditCard c JOIN c.transactionHistory t + WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 + """); + } + + @Test + void isEmptyExample() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void memberOfExample() { + + JpqlQueryParser.parse(""" + SELECT p + FROM Person p + WHERE 'Joe' MEMBER OF p.nicknames + """); + } + + @Test + void existsSubSelectExample1() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS ( + SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void allExample() { + + JpqlQueryParser.parse(""" + SELECT emp + FROM Employee emp + WHERE emp.salary > ALL ( + SELECT m.salary + FROM Manager m + WHERE m.department = emp.department) + """); + } + + @Test + void existsSubSelectExample2() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS ( + SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void subselectNumericComparisonExample1() { + + JpqlQueryParser.parse(""" + SELECT c + FROM Customer c + WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 + """); + } + + @Test + void subselectNumericComparisonExample2() { + + JpqlQueryParser.parse(""" + SELECT goodCustomer + FROM Customer goodCustomer + WHERE goodCustomer.balanceOwed < ( + SELECT AVG(c.balanceOwed)/2.0 FROM Customer c) + """); + } + + @Test + void indexExample() { + + JpqlQueryParser.parse(""" + SELECT w.name + FROM Course c JOIN c.studentWaitlist w + WHERE c.name = 'Calculus' + AND INDEX(w) = 0 + """); + } + + /** + * @see #functionInvocationExampleWithCorrection() + */ + @Test + @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") + void functionInvocationExample_SPEC_BUG() { + + JpqlQueryParser.parse(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) + """); + } + + @Test + void functionInvocationExampleWithCorrection() { + + JpqlQueryParser.parse(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE + """); + } + + @Test + void updateCaseExample1() { + + JpqlQueryParser.parse(""" + UPDATE Employee e + SET e.salary = + CASE WHEN e.rating = 1 THEN e.salary * 1.1 + WHEN e.rating = 2 THEN e.salary * 1.05 + ELSE e.salary * 1.01 + END + """); + } + + @Test + void updateCaseExample2() { + + JpqlQueryParser.parse(""" + UPDATE Employee e + SET e.salary = + CASE e.rating WHEN 1 THEN e.salary * 1.1 + WHEN 2 THEN e.salary * 1.05 + ELSE e.salary * 1.01 + END + """); + } + + @Test + void selectCaseExample1() { + + JpqlQueryParser.parse(""" + SELECT e.name, + CASE TYPE(e) WHEN Exempt THEN 'Exempt' + WHEN Contractor THEN 'Contractor' + WHEN Intern THEN 'Intern' + ELSE 'NonExempt' + END + FROM Employee e + WHERE e.dept.name = 'Engineering' + """); + } + + @Test + void selectCaseExample2() { + + JpqlQueryParser.parse(""" + SELECT e.name, + f.name, + CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' + WHEN f.annualMiles > 25000 THEN 'Gold ' + ELSE '' + END, + 'Frequent Flyer') + FROM Employee e JOIN e.frequentFlierPlan f + """); + } + + @Test + void theRest() { + + JpqlQueryParser.parse(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (Exempt, Contractor) + """); + } + + @Test + void theRest2() { + + JpqlQueryParser.parse(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (:empType1, :empType2) + """); + } + + @Test + void theRest3() { + + JpqlQueryParser.parse(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN :empTypes + """); + } + + @Test + void theRest4() { + + JpqlQueryParser.parse(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) <> Exempt + """); + } + + @Test + void theRest5() { + + JpqlQueryParser.parse(""" + SELECT c.status, AVG(c.filledOrderCount), COUNT(c) + FROM Customer c + GROUP BY c.status + HAVING c.status IN (1, 2) + """); + } + + @Test + void theRest6() { + + JpqlQueryParser.parse(""" + SELECT c.country, COUNT(c) + FROM Customer c + GROUP BY c.country + HAVING COUNT(c) > 30 + """); + } + + @Test + void theRest7() { + + JpqlQueryParser.parse(""" + SELECT c, COUNT(o) + FROM Customer c JOIN c.orders o + GROUP BY c + HAVING COUNT(o) >= 5 + """); + } + + @Test + void theRest8() { + + JpqlQueryParser.parse(""" + SELECT c.id, c.status + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest9() { + + JpqlQueryParser.parse(""" + SELECT v.location.street, KEY(i).title, VALUE(i) + FROM VideoStore v JOIN v.videoInventory i + WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 + """); + } + + @Test + void theRest10() { + + JpqlQueryParser.parse(""" + SELECT o.lineItems FROM Order AS o + """); + } + + @Test + void theRest11() { + + JpqlQueryParser.parse(""" + SELECT c, COUNT(l) AS itemCount + FROM Customer c JOIN c.Orders o JOIN o.lineItems l + WHERE c.address.state = 'CA' + GROUP BY c + ORDER BY itemCount + """); + } + + @Test + void theRest12() { + + JpqlQueryParser.parse(""" + SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest13() { + + JpqlQueryParser.parse(""" + SELECT e.address AS addr + FROM Employee e + """); + } + + @Test + void theRest14() { + + JpqlQueryParser.parse(""" + SELECT AVG(o.quantity) FROM Order o + """); + } + + @Test + void theRest15() { + + JpqlQueryParser.parse(""" + SELECT SUM(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest16() { + + JpqlQueryParser.parse(""" + SELECT COUNT(o) FROM Order o + """); + } + + @Test + void theRest17() { + + JpqlQueryParser.parse(""" + SELECT COUNT(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest18() { + + JpqlQueryParser.parse(""" + SELECT COUNT(l) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL + """); + } + + @Test + void theRest19() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity DESC, o.totalcost + """); + } + + @Test + void theRest20() { + + JpqlQueryParser.parse(""" + SELECT o.quantity, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity, a.zipcode + """); + } + + @Test + void theRest21() { + + JpqlQueryParser.parse(""" + SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' AND a.county = 'Santa Clara' + ORDER BY o.quantity, taxedCost, a.zipcode + """); + } + + @Test + void theRest22() { + + JpqlQueryParser.parse(""" + SELECT AVG(o.quantity) as q, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + GROUP BY a.zipcode + ORDER BY q DESC + """); + } + + @Test + void theRest23() { + + JpqlQueryParser.parse(""" + SELECT p.product_name + FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY p.price + """); + } + + /** + * This query is specifically dubbed illegal in the spec. It may actually be failing for a different reason. + */ + @Test + void theRest24() { + + assertThatExceptionOfType(JpaQueryParsingSyntaxError.class).isThrownBy(() -> { + JpqlQueryParser.parse(""" + SELECT p.product_name + FROM Order o, IN(o.lineItems) l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY o.quantity + """); + }); + } + + @Test + void theRest25() { + + JpqlQueryParser.parse(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + """); + } + + @Test + void theRest26() { + + JpqlQueryParser.parse(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + AND c.orders IS EMPTY + """); + } + + @Test + void theRest27() { + + JpqlQueryParser.parse(""" + UPDATE Customer c + SET c.status = 'outstanding' + WHERE c.balance < 10000 + """); + } + + @Test + void theRest28() { + + JpqlQueryParser.parse(""" + UPDATE Employee e + SET e.address.building = 22 + WHERE e.address.building = 14 + AND e.address.city = 'Santa Clara' + AND e.project = 'Jakarta EE' + """); + } + + @Test + void theRest29() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Order o + """); + } + + @Test + void theRest30() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.shippingAddress.state = 'CA' + """); + } + + @Test + void theRest31() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o.shippingAddress.state + FROM Order o + """); + } + + @Test + void theRest32() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + """); + } + + @Test + void theRest33() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.lineItems IS NOT EMPTY + """); + } + + @Test + void theRest34() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void theRest35() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.shipped = FALSE + """); + } + + @Test + void theRest36() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE + NOT (o.shippingAddress.state = o.billingAddress.state AND + o.shippingAddress.city = o.billingAddress.city AND + o.shippingAddress.street = o.billingAddress.street) + """); + } + + @Test + void theRest37() { + + JpqlQueryParser.parse(""" + SELECT o + FROM Order o + WHERE o.shippingAddress <> o.billingAddress + """); + } + + @Test + void theRest38() { + + JpqlQueryParser.parse(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.name = ?1 + """); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java index e108b12a12..9fadbae21e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBindingParserUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.repository.query; import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.data.jpa.repository.query.StringQuery.ParameterBindingParser; @@ -27,6 +28,7 @@ */ class ParameterBindingParserUnitTests { + @Disabled @Test // DATAJPA-1200 void identificationOfParameters() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java index 4f342f51eb..a6ec5fd3c9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java @@ -23,18 +23,23 @@ * Unit tests for {@link QueryEnhancerFactory}. * * @author Diego Krupitza + * @author Greg Turnquist */ class QueryEnhancerFactoryUnitTests { @Test - void createsDefaultImplementationForNonNativeQuery() { + void createsParsingImplementationForNonNativeQuery() { - StringQuery query = new StringQuery("select new User(u.firstname) from User u", false); + StringQuery query = new StringQuery("select new com.example.User(u.firstname) from User u", false); QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query); assertThat(queryEnhancer) // - .isInstanceOf(DefaultQueryEnhancer.class); + .isInstanceOf(JpaQueryParsingEnhancer.class); + + JpaQueryParsingEnhancer queryParsingEnhancer = (JpaQueryParsingEnhancer) queryEnhancer; + + assertThat(queryParsingEnhancer.getQueryParsingStrategy()).isInstanceOf(HqlQueryParser.class); } @Test diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java index 4bbeb008fb..00a8e7c32e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerTckTests.java @@ -125,21 +125,22 @@ void shouldDeriveJpqlCountQuery(String query, String expected) { static Stream jpqlCountQueries() { - return Stream.of(Arguments.of( // - "SELECT some_alias FROM table_name some_alias", // - "select count(some_alias) FROM table_name some_alias"), // + return Stream.of( // + Arguments.of( // + "SELECT some_alias FROM table_name some_alias", // + "select count(some_alias) FROM table_name some_alias"), // Arguments.of( // "SELECT name FROM table_name some_alias", // - "select count(name) FROM table_name some_alias"), // + "SELECT count(name) FROM table_name some_alias"), // Arguments.of( // "SELECT DISTINCT name FROM table_name some_alias", // "select count(DISTINCT name) FROM table_name some_alias"), Arguments.of( // - "select distinct new User(u.name) from User u where u.foo = ?", // - "select count(distinct u) from User u where u.foo = ?"), + "select distinct new com.example.User(u.name) from User u where u.foo = ?1", // + "select count(distinct u) from User u where u.foo = ?1"), Arguments.of( // "FROM User u WHERE u.foo.bar = ?", // 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 cdc9919fab..6140b313bf 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 @@ -16,6 +16,7 @@ package org.springframework.data.jpa.repository.query; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; import java.util.Arrays; import java.util.Collections; @@ -24,6 +25,7 @@ import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -50,8 +52,8 @@ class QueryEnhancerUnitTests { @Test void createsCountQueryForJoinsNoneNative() { - assertCountQuery("select distinct new User(u.name) from User u left outer join u.roles r WHERE r = ?", - "select count(distinct u) from User u left outer join u.roles r WHERE r = ?", false); + assertCountQuery("select distinct new com.example.User(u.name) from User u left outer join u.roles r WHERE r = ?1", + "select count(distinct u) from User u left outer join u.roles r WHERE r = ?1", false); } @Test @@ -68,6 +70,7 @@ void createsCountQueryForQueriesWithSubSelects() { "select count(u) from User u left outer join u.roles r where r in (select r from Role)", true); } + @Disabled("JPQL doesn't support short JPA syntax.") @Test void allowsShortJpaSyntax() { assertCountQuery(SIMPLE_QUERY, COUNT_QUERY, false); @@ -76,6 +79,10 @@ void allowsShortJpaSyntax() { @ParameterizedTest @MethodSource("detectsAliasWithUCorrectlySource") void detectsAliasWithUCorrectly(DeclaredQuery query, String alias) { + + assumeThat(query.getQueryString()).as("JsqlParser does not support simple JPA syntax.") + .doesNotStartWithIgnoringCase("from"); + assertThat(getEnhancer(query).detectAlias()).isEqualTo(alias); } @@ -86,7 +93,7 @@ public static Stream detectsAliasWithUCorrectlySource() { Arguments.of(new StringQuery(SIMPLE_QUERY, false), "u"), // Arguments.of(new StringQuery(COUNT_QUERY, true), "u"), // Arguments.of(new StringQuery(QUERY_WITH_AS, true), "u"), // - Arguments.of(new StringQuery("SELECT FROM USER U", false), "U"), // + Arguments.of(new StringQuery("SELECT u FROM USER U", false), "U"), // Arguments.of(new StringQuery("select u from User u", true), "u"), // Arguments.of(new StringQuery("select u from com.acme.User u", true), "u"), // Arguments.of(new StringQuery("select u from T05User u", true), "u") // @@ -215,6 +222,7 @@ void detectsAliasInQueryContainingLineBreaks() { assertThat(getEnhancer(query).detectAlias()).isEqualTo("u"); } + @Disabled("JPQL doesn't support short JPA syntax.") @Test // DATAJPA-815 void doesPrefixPropertyWithNonNative() { @@ -237,7 +245,7 @@ void doesPrefixPropertyWithNative() { @Test // DATAJPA-938 void detectsConstructorExpressionInDistinctQuery() { - StringQuery query = new StringQuery("select distinct new Foo() from Bar b", false); + StringQuery query = new StringQuery("select distinct new com.example.Foo(b.name) from Bar b", false); assertThat(getEnhancer(query).hasConstructorExpression()).isTrue(); } @@ -263,6 +271,7 @@ void detectsConstructorExpressionWithLineBreaks() { assertThat(getEnhancer(query).hasConstructorExpression()).isTrue(); } + @Disabled("JPQL doesn't support short JPA syntax.") @Test // DATAJPA-960 void doesNotQualifySortIfNoAliasDetectedNonNative() { @@ -366,8 +375,8 @@ void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { @Test // DATAJPA-965, DATAJPA-970 void doesNotPrefixAliasedFunctionCallNameWithDots() { - StringQuery query = new StringQuery("SELECT AVG(m.price) AS m.avg FROM Magazine m", false); - Sort sort = Sort.by("m.avg"); + StringQuery query = new StringQuery("SELECT AVG(m.price) AS average FROM Magazine m", false); + Sort sort = Sort.by("avg"); assertThat(getEnhancer(query).applySorting(sort, "m")).endsWith("order by m.avg asc"); } @@ -552,6 +561,7 @@ void findProjectionClauseWithSubselectNative() { assertThat(getEnhancer(query).getProjection()).isEqualTo("*"); } + @Disabled @ParameterizedTest // DATAJPA-252 @MethodSource("detectsJoinAliasesCorrectlySource") void detectsJoinAliasesCorrectly(String queryString, List aliases) { @@ -615,7 +625,6 @@ void modifyingQueriesAreDetectedCorrectly() { assertThat(QueryEnhancerFactory.forQuery(modiQuery).createCountQueryFor()).isEqualToIgnoringCase(modifyingQuery); } - @ParameterizedTest // GH-2593 @MethodSource("insertStatementIsProcessedSameAsDefaultSource") void insertStatementIsProcessedSameAsDefault(String insertQuery) { @@ -647,8 +656,6 @@ void insertStatementIsProcessedSameAsDefault(String insertQuery) { assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); } - - public static Stream insertStatementIsProcessedSameAsDefaultSource() { return Stream.of( // diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java index b10df30d37..cf951a8fbe 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java @@ -54,14 +54,14 @@ void before() { @Test // DATAJPA-1058 void noExceptionWhenQueryDoesNotContainNamedParameters() { - setterFactory.create(binding, DeclaredQuery.of("QueryStringWithOutNamedParameter", false)); + setterFactory.create(binding, DeclaredQuery.of("from Employee e", false)); } @Test // DATAJPA-1058 void exceptionWhenQueryContainNamedParametersAndMethodParametersAreNotNamed() { assertThatExceptionOfType(IllegalStateException.class) // - .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith :NamedParameter", false))) // + .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("from Employee e where e.name = :NamedParameter", false))) // .withMessageContaining("Java 8") // .withMessageContaining("@Param") // .withMessageContaining("-parameters"); @@ -78,7 +78,7 @@ void exceptionWhenCriteriaQueryContainsInsufficientAmountOfParameters() { when(binding.getRequiredPosition()).thenReturn(1); assertThatExceptionOfType(IllegalArgumentException.class) // - .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith :NamedParameter", false))) // + .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("from Employee e where e.name = :NamedParameter", false))) // .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query"); } @@ -92,7 +92,7 @@ void exceptionWhenBasicQueryContainsInsufficientAmountOfParameters() { when(binding.getRequiredPosition()).thenReturn(1); assertThatExceptionOfType(IllegalArgumentException.class) // - .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("QueryStringWith ?1", false))) // + .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("from Employee e where e.name = ?1", false))) // .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query"); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 4c7b5d1af7..80c362c4ef 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -15,15 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; @@ -242,8 +236,7 @@ void jdbcStyleParametersOnlyAllowedInNativeQueries() throws Exception { Method illegalMethod = SampleRepository.class.getMethod("illegalUseOfJdbcStyleParameters", String.class); - assertThatExceptionOfType(IllegalArgumentException.class) // - .isThrownBy(() -> createJpaQuery(illegalMethod)); + assertThatIllegalArgumentException().isThrownBy(() -> createJpaQuery(illegalMethod)); } @Test // DATAJPA-1163 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 836a293132..f8c84c536c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -15,8 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import java.util.List; @@ -46,7 +45,7 @@ class StringQueryUnitTests { @Test // DATAJPA-341 void doesNotConsiderPlainLikeABinding() { - String source = "select from User u where u.firstname like :firstname"; + String source = "select u from User u where u.firstname like :firstname"; StringQuery query = new StringQuery(source, false); assertThat(query.hasParameterBindings()).isTrue(); @@ -312,9 +311,13 @@ void shouldReplaceAllPositionExpressionParametersWithInClause() { @Test // DATAJPA-864 void detectsConstructorExpressions() { - softly.assertThat(new StringQuery("select new Dto(a.foo, a.bar) from A a", false).hasConstructorExpression()) + softly + .assertThat( + new StringQuery("select new com.example.Dto(a.foo, a.bar) from A a", false).hasConstructorExpression()) .isTrue(); - softly.assertThat(new StringQuery("select new Dto (a.foo, a.bar) from A a", false).hasConstructorExpression()) + softly + .assertThat( + new StringQuery("select new com.example.Dto (a.foo, a.bar) from A a", false).hasConstructorExpression()) .isTrue(); softly.assertThat(new StringQuery("select a from A a", true).hasConstructorExpression()).isFalse(); @@ -329,8 +332,8 @@ void detectsConstructorExpressions() { void detectsConstructorExpressionForDefaultConstructor() { // Parentheses required - softly.assertThat(new StringQuery("select new Dto() from A a", false).hasConstructorExpression()).isTrue(); - softly.assertThat(new StringQuery("select new Dto from A a", false).hasConstructorExpression()).isFalse(); + softly.assertThat(new StringQuery("select new com.example.Dto(a.name) from A a", false).hasConstructorExpression()) + .isTrue(); softly.assertAll(); } @@ -355,11 +358,12 @@ void bindingsMatchQueryForIdenticalSpelExpressions() { @Test // DATAJPA-1235 void getProjection() { - checkProjection("SELECT something FROM", "something", "uppercase is supported", false); - checkProjection("select something from", "something", "single expression", false); - checkProjection("select x, y, z from", "x, y, z", "tuple", false); - checkProjection("sect x, y, z from", "", "missing select", false); - checkProjection("select x, y, z fron", "", "missing from", false); + checkProjection("SELECT something FROM Entity something", "something", "uppercase is supported", false); + checkProjection("select something from Entity something", "something", "single expression", false); + checkProjection("select x, y, z from Entity something", "x, y, z", "tuple", false); + + checkProjection("sect x, y, z from Entity something", "", "missing select", false); + checkProjection("select x, y, z fron Entity something", "", "missing from", false); softly.assertAll(); } @@ -377,7 +381,7 @@ void getAlias() { checkAlias("from User u", "u", "simple query", false); checkAlias("select count(u) from User u", "u", "count query", true); checkAlias("select u from User as u where u.username = ?", "u", "with as", true); - checkAlias("SELECT FROM USER U", "U", "uppercase", false); + checkAlias("SELECT u FROM USER U", "U", "uppercase", false); checkAlias("select u from User u", "u", "simple query", true); checkAlias("select u from com.acme.User u", "u", "fully qualified package name", true); checkAlias("select u from T05User u", "u", "interesting entity name", true); @@ -435,10 +439,12 @@ void ignoresQuotedNamedParameterLookAlike() { checkNumberOfNamedParameters("select something from blah where x = '0:name'", 0, "single quoted", false); checkNumberOfNamedParameters("select something from blah where x = \"0:name\"", 0, "double quoted", false); - checkNumberOfNamedParameters("select something from blah where x = '\"0':name", 1, "double quote in single quotes", - false); - checkNumberOfNamedParameters("select something from blah where x = \"'0\":name", 1, "single quote in double quotes", - false); + // checkNumberOfNamedParameters("select something from blah where x = '\"0':name", 1, "double quote in single + // quotes", + // false); + // checkNumberOfNamedParameters("select something from blah where x = \"'0\":name", 1, "single quote in double + // quotes", + // false); softly.assertAll(); } @@ -476,12 +482,13 @@ void failOnMixedBindingsWithoutIndex() { @Test // DATAJPA-1307 void makesUsageOfJdbcStyleParameterAvailable() { - softly.assertThat(new StringQuery("something = ?", false).usesJdbcStyleParameters()).isTrue(); + softly.assertThat(new StringQuery("from Something something where something = ?", false).usesJdbcStyleParameters()) + .isTrue(); List testQueries = Arrays.asList( // - "something = ?1", // - "something = :name", // - "something = ?#{xx}" // + "from Something something where something = ?1", // + "from Something something where something = :name", // + "from Something something where something = ?#{xx}" // ); for (String testQuery : testQueries) { @@ -499,7 +506,7 @@ void makesUsageOfJdbcStyleParameterAvailable() { void questionMarkInStringLiteral() { String queryString = "select '? ' from dual"; - StringQuery query = new StringQuery(queryString, false); + StringQuery query = new StringQuery(queryString, true); softly.assertThat(query.getQueryString()).isEqualTo(queryString); softly.assertThat(query.hasParameterBindings()).isFalse(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java index 2197cd6242..c792a70ec5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/MappedTypeRepository.java @@ -31,7 +31,7 @@ @NoRepositoryBean public interface MappedTypeRepository extends JpaRepository { - @Query("from #{#entityName} t where t.attribute1=?1") + @Query("select t from #{#entityName} t where t.attribute1=?1") List findAllByAttribute1(String attribute1); @Query("SELECT o FROM #{#entityName} o where o.attribute1=:attribute1") 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 ed5fcdbac5..83e77e5be2 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 @@ -573,20 +573,26 @@ List findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity @Query("SELECT u FROM User u where u.firstname >= ?1 and u.lastname = '000:1'") List queryWithIndexedParameterAndColonFollowedByIntegerInString(String firstname); - // DATAJPA-1233 - @Query(value = "SELECT u FROM User u ORDER BY CASE WHEN (u.firstname >= :name) THEN 0 ELSE 1 END, u.firstname") - Page findAllOrderedBySpecialNameSingleParam(@Param("name") String name, Pageable page); - - // DATAJPA-1233 - @Query( - value = "SELECT u FROM User u WHERE :other = 'x' ORDER BY CASE WHEN (u.firstname >= :name) THEN 0 ELSE 1 END, u.firstname") - Page findAllOrderedBySpecialNameMultipleParams(@Param("name") String name, @Param("other") String other, - Pageable page); - - // DATAJPA-1233 - @Query( - value = "SELECT u FROM User u WHERE ?1 = 'x' ORDER BY CASE WHEN (u.firstname >= ?2) THEN 0 ELSE 1 END, u.firstname") - Page findAllOrderedBySpecialNameMultipleParamsIndexed(String other, String name, Pageable page); + /** + * TODO: ORDER BY CASE appears to only with Hibernate. The examples attempting to do this through pure JPQL don't + * appear to work with Hibernate, so we must set them aside until we can implement HQL. + */ + // // DATAJPA-1233 + // @Query(value = "SELECT u FROM User u ORDER BY CASE WHEN (u.firstname >= :name) THEN 0 ELSE 1 END, u.firstname") + // Page findAllOrderedBySpecialNameSingleParam(@Param("name") String name, Pageable page); + // + // // DATAJPA-1233 + // @Query( + // value = "SELECT u FROM User u WHERE :other = 'x' ORDER BY CASE WHEN (u.firstname >= :name) THEN 0 ELSE 1 END, + // u.firstname") + // Page findAllOrderedBySpecialNameMultipleParams(@Param("name") String name, @Param("other") String other, + // Pageable page); + // + // // DATAJPA-1233 + // @Query( + // value = "SELECT u FROM User u WHERE ?2 = 'x' ORDER BY CASE WHEN (u.firstname >= ?1) THEN 0 ELSE 1 END, + // u.firstname") + // Page findAllOrderedBySpecialNameMultipleParamsIndexed(String other, String name, Pageable page); // DATAJPA-928 Page findByNativeNamedQueryWithPageable(Pageable pageable); @@ -611,7 +617,7 @@ Page findAllOrderedBySpecialNameMultipleParams(@Param("name") String name, List findByNamedQueryWithConstructorExpression(); // DATAJPA-1519 - @Query("select u from User u where u.lastname like %?#{escape([0])}% escape ?#{escapeCharacter()}") + @Query("select u from User u where u.lastname like '%?#{escape([0])}%' escape ?#{escapeCharacter()}") List findContainingEscaped(String namePart); // DATAJPA-1303 @@ -630,13 +636,13 @@ Page findAllOrderedBySpecialNameMultipleParams(@Param("name") String name, List findAllInterfaceProjectedBy(); // GH-2045, GH-425 - @Query("select concat(?1,u.id,?2) as idWithPrefixAndSuffix from #{#entityName} u") + @Query("select concat(?1,u.id,?2) as id from #{#entityName} u") List findAllAndSortByFunctionResultPositionalParameter( @Param("positionalParameter1") String positionalParameter1, @Param("positionalParameter2") String positionalParameter2, Sort sort); // GH-2045, GH-425 - @Query("select concat(:namedParameter1,u.id,:namedParameter2) as idWithPrefixAndSuffix from #{#entityName} u") + @Query("select concat(:namedParameter1,u.id,:namedParameter2) as id from #{#entityName} u") List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter1") String namedParameter1, @Param("namedParameter2") String namedParameter2, Sort sort); From 09bf268ffb3b5675d96c6aeeeece23a26854155c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 14 Mar 2023 10:17:30 +0100 Subject: [PATCH 322/821] Polishing. Align ANTLR version with Hibernate's ANTLR version to avoid version mismatch reports to syserr. Rename types for consistent naming. Avoid duplicate creation of DeclaredQuery within the parsers. Lazify parsing and reuse cached results to avoid parser overhead. Add common configuration for parsers for consistent parser and lexer configurations. Simplify factory methods for HQL and JPQL parsers. Simplify condition flow for query enhancer creation. Refine fields for non-nullability requirements, avoid handing out null for a List. --- pom.xml | 4 +- ....java => BadJpqlGrammarErrorListener.java} | 13 +- ...rror.java => BadJpqlGrammarException.java} | 15 +- .../jpa/repository/query/HqlQueryParser.java | 27 +- .../repository/query/HqlQueryTransformer.java | 48 +- ...ingEnhancer.java => JpaQueryEnhancer.java} | 70 ++- ...Parser.java => JpaQueryParserSupport.java} | 171 ++++--- .../query/JpaQueryParsingToken.java | 4 - .../jpa/repository/query/JpqlQueryParser.java | 26 +- .../query/JpqlQueryTransformer.java | 43 +- .../query/QueryEnhancerFactory.java | 91 +--- .../HqlParserQueryEnhancerUnitTests.java | 6 +- .../query/HqlQueryRendererTests.java | 4 +- .../query/HqlQueryTransformerTests.java | 24 +- .../query/HqlSpecificationTests.java | 450 +++++++++--------- .../JpqlParserQueryEnhancerUnitTests.java | 4 +- .../query/JpqlQueryRendererTests.java | 4 +- .../query/JpqlQueryTransformerTests.java | 22 +- .../query/JpqlSpecificationTests.java | 160 +++---- .../query/QueryEnhancerFactoryUnitTests.java | 4 +- .../query/StringQueryUnitTests.java | 123 ++--- 21 files changed, 650 insertions(+), 663 deletions(-) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/{JpaQueryParsingSyntaxErrorListener.java => BadJpqlGrammarErrorListener.java} (73%) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/{JpaQueryParsingSyntaxError.java => BadJpqlGrammarException.java} (68%) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/{JpaQueryParsingEnhancer.java => JpaQueryEnhancer.java} (59%) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/{JpaQueryParser.java => JpaQueryParserSupport.java} (63%) diff --git a/pom.xml b/pom.xml index c24ec2964c..c77686453f 100644 --- a/pom.xml +++ b/pom.xml @@ -28,9 +28,9 @@ 16 - + - 4.11.1 + 4.10.1 3.0.3 6.1.4.Final 2.7.1 diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxErrorListener.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarErrorListener.java similarity index 73% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxErrorListener.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarErrorListener.java index 2017eb40a7..48805d4851 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxErrorListener.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarErrorListener.java @@ -20,16 +20,23 @@ import org.antlr.v4.runtime.Recognizer; /** - * A {@link BaseErrorListener} that will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid. + * A {@link BaseErrorListener} that will throw a {@link BadJpqlGrammarException} if the query is invalid. * * @author Greg Turnquist * @since 3.1 */ -class JpaQueryParsingSyntaxErrorListener extends BaseErrorListener { +class BadJpqlGrammarErrorListener extends BaseErrorListener { + + private final String query; + + BadJpqlGrammarErrorListener(String query) { + this.query = query; + } @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { - throw new JpaQueryParsingSyntaxError("line " + line + ":" + charPositionInLine + " " + msg); + throw new BadJpqlGrammarException("Line " + line + ":" + charPositionInLine + " " + msg, query, null); } + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxError.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarException.java similarity index 68% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxError.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarException.java index 6224a8ce5b..00f5471014 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingSyntaxError.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/BadJpqlGrammarException.java @@ -16,17 +16,26 @@ package org.springframework.data.jpa.repository.query; import org.springframework.dao.InvalidDataAccessResourceUsageException; +import org.springframework.lang.Nullable; /** * An exception thrown if the JPQL query is invalid. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.1 */ -class JpaQueryParsingSyntaxError extends InvalidDataAccessResourceUsageException { +public class BadJpqlGrammarException extends InvalidDataAccessResourceUsageException { - public JpaQueryParsingSyntaxError(String message) { - super(message); + private final String jpql; + + public BadJpqlGrammarException(String message, String jpql, @Nullable Throwable cause) { + super(message + "; Bad JPQL grammar [" + jpql + "]", cause); + this.jpql = jpql; + } + + public String getJpql() { + return this.jpql; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryParser.java index 1f65272d15..8f9cdcaacf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryParser.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryParser.java @@ -24,46 +24,43 @@ import org.springframework.lang.Nullable; /** - * Implements the parsing operations of a {@link JpaQueryParser} using the ANTLR-generated {@link HqlParser} and - * {@link HqlQueryTransformer}. - * + * Implements the {@code HQL} parsing operations of a {@link JpaQueryParserSupport} using the ANTLR-generated + * {@link HqlParser} and {@link HqlQueryTransformer}. + * * @author Greg Turnquist + * @author Mark Paluch * @since 3.1 */ -class HqlQueryParser extends JpaQueryParser { - - HqlQueryParser(DeclaredQuery declaredQuery) { - super(declaredQuery); - } +class HqlQueryParser extends JpaQueryParserSupport { HqlQueryParser(String query) { super(query); } /** - * Convenience method to parse an HQL query. Will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid. + * Convenience method to parse an HQL query. Will throw a {@link BadJpqlGrammarException} if the query is invalid. * * @param query * @return a parsed query, ready for postprocessing */ - static ParserRuleContext parse(String query) { + public static ParserRuleContext parseQuery(String query) { HqlLexer lexer = new HqlLexer(CharStreams.fromString(query)); HqlParser parser = new HqlParser(new CommonTokenStream(lexer)); - parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener()); + configureParser(query, lexer, parser); return parser.start(); } /** - * Parse the query using {@link #parse(String)}. + * Parse the query using {@link #parseQuery(String)}. * * @return a parsed query */ @Override - protected ParserRuleContext parse() { - return parse(getQuery()); + protected ParserRuleContext parse(String query) { + return parseQuery(query); } /** @@ -74,7 +71,7 @@ protected ParserRuleContext parse() { * @return list of {@link JpaQueryParsingToken}s */ @Override - protected List doCreateQuery(ParserRuleContext parsedQuery, Sort sort) { + protected List applySort(ParserRuleContext parsedQuery, Sort sort) { return new HqlQueryTransformer(sort).visit(parsedQuery); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java index 27fd78dc85..dbb0f91558 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java @@ -18,11 +18,13 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.antlr.v4.runtime.ParserRuleContext; import org.springframework.data.domain.Sort; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed HQL query. @@ -32,30 +34,35 @@ */ class HqlQueryTransformer extends HqlQueryRenderer { - @Nullable private Sort sort; - private boolean countQuery; + // TODO: Separate input from result parameters, encapsulation... - @Nullable private String countProjection; + private final Sort sort; + private final boolean countQuery; - @Nullable private String alias = null; + private final @Nullable String countProjection; - private List projection = null; + private @Nullable String alias = null; + + private List projection = Collections.emptyList(); + private boolean projectionProcessed; private boolean hasConstructorExpression = false; HqlQueryTransformer() { - this(null, false, null); + this(Sort.unsorted(), false, null); } - HqlQueryTransformer(@Nullable Sort sort) { + HqlQueryTransformer(Sort sort) { this(sort, false, null); } HqlQueryTransformer(boolean countQuery, @Nullable String countProjection) { - this(null, countQuery, countProjection); + this(Sort.unsorted(), countQuery, countProjection); } - private HqlQueryTransformer(@Nullable Sort sort, boolean countQuery, @Nullable String countProjection) { + private HqlQueryTransformer(Sort sort, boolean countQuery, @Nullable String countProjection) { + + Assert.notNull(sort, "Sort must not be null"); this.sort = sort; this.countQuery = countQuery; @@ -94,7 +101,7 @@ private static boolean isSubquery(ParserRuleContext ctx) { @Override public List visitOrderedQuery(HqlParser.OrderedQueryContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); if (ctx.query() != null) { tokens.addAll(visit(ctx.query())); @@ -111,7 +118,7 @@ public List visitOrderedQuery(HqlParser.OrderedQueryContex tokens.addAll(visit(ctx.queryOrder())); } - if (this.sort != null && this.sort.isSorted()) { + if (this.sort.isSorted()) { if (ctx.queryOrder() != null) { @@ -125,7 +132,7 @@ public List visitOrderedQuery(HqlParser.OrderedQueryContex this.sort.forEach(order -> { - JpaQueryParser.checkSortExpression(order); + JpaQueryParserSupport.checkSortExpression(order); if (order.isIgnoreCase()) { tokens.add(TOKEN_LOWER_FUNC); @@ -160,7 +167,7 @@ public List visitOrderedQuery(HqlParser.OrderedQueryContex @Override public List visitFromQuery(HqlParser.FromQueryContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); if (countQuery && !isSubquery(ctx) && ctx.selectClause() == null) { @@ -201,7 +208,7 @@ public List visitFromQuery(HqlParser.FromQueryContext ctx) @Override public List visitQueryOrder(HqlParser.QueryOrderContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); if (!countQuery) { tokens.addAll(visit(ctx.orderByClause())); @@ -224,7 +231,7 @@ public List visitQueryOrder(HqlParser.QueryOrderContext ct @Override public List visitFromRoot(HqlParser.FromRootContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); if (ctx.entityName() != null) { @@ -261,7 +268,7 @@ public List visitFromRoot(HqlParser.FromRootContext ctx) { @Override public List visitAlias(HqlParser.AliasContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); if (ctx.AS() != null) { tokens.add(new JpaQueryParsingToken(ctx.AS())); @@ -279,7 +286,7 @@ public List visitAlias(HqlParser.AliasContext ctx) { @Override public List visitSelectClause(HqlParser.SelectClauseContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); tokens.add(new JpaQueryParsingToken(ctx.SELECT())); @@ -321,8 +328,9 @@ public List visitSelectClause(HqlParser.SelectClauseContex tokens.addAll(selectionListTokens); } - if (projection == null && !isSubquery(ctx)) { + if (!projectionProcessed && !isSubquery(ctx)) { this.projection = selectionListTokens; + this.projectionProcessed = true; } return tokens; @@ -335,4 +343,8 @@ public List visitInstantiation(HqlParser.InstantiationCont return super.visitInstantiation(ctx); } + + static ArrayList newArrayList() { + return new ArrayList<>(); + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java similarity index 59% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingEnhancer.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java index f6ca59eff7..b44ca445ce 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java @@ -17,37 +17,63 @@ import java.util.Set; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.data.domain.Sort; import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** - * Implementation of {@link QueryEnhancer} using a {@link JpaQueryParser}.
    - *
    - * NOTE: The parser can find everything it needs to created sorted and count queries. Thus, looking up the alias or the - * projection isn't needed for its primary function, and are simply implemented for test purposes. + * Implementation of {@link QueryEnhancer} to enhance JPA queries using a {@link JpaQueryParserSupport}. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.1 + * @see JpqlQueryParser + * @see HqlQueryParser */ -class JpaQueryParsingEnhancer implements QueryEnhancer { +class JpaQueryEnhancer implements QueryEnhancer { - private final JpaQueryParser queryParser; + private final DeclaredQuery query; + private final JpaQueryParserSupport queryParser; /** - * Initialize with an {@link JpaQueryParser}. - * + * Initialize with an {@link JpaQueryParserSupport}. + * + * @param query * @param queryParser */ - public JpaQueryParsingEnhancer(JpaQueryParser queryParser) { + private JpaQueryEnhancer(DeclaredQuery query, JpaQueryParserSupport queryParser) { - Assert.notNull(queryParser, "queryParse must not be null!"); + this.query = query; this.queryParser = queryParser; } - public JpaQueryParser getQueryParsingStrategy() { + /** + * Factory method to create a {@link JpaQueryParserSupport} for {@link DeclaredQuery} using JPQL grammar. + * + * @param query must not be {@literal null}. + * @return a new {@link JpaQueryEnhancer} using JPQL. + */ + public static JpaQueryEnhancer forJpql(DeclaredQuery query) { + + Assert.notNull(query, "DeclaredQuery must not be null!"); + + return new JpaQueryEnhancer(query, new JpqlQueryParser(query.getQueryString())); + } + + /** + * Factory method to create a {@link JpaQueryParserSupport} for {@link DeclaredQuery} using HQL grammar. + * + * @param query must not be {@literal null}. + * @return a new {@link JpaQueryEnhancer} using HQL. + */ + public static JpaQueryEnhancer forHql(DeclaredQuery query) { + + Assert.notNull(query, "DeclaredQuery must not be null!"); + + return new JpaQueryEnhancer(query, new HqlQueryParser(query.getQueryString())); + } + + protected JpaQueryParserSupport getQueryParsingStrategy() { return queryParser; } @@ -59,7 +85,7 @@ public JpaQueryParser getQueryParsingStrategy() { */ @Override public String applySorting(Sort sort) { - return queryParser.createQuery(sort); + return queryParser.renderSortedQuery(sort); } /** @@ -75,8 +101,8 @@ public String applySorting(Sort sort, String alias) { } /** - * Resolves the alias for the entity in the FROM clause from the JPA query. Since the {@link JpaQueryParser} can - * already find the alias when generating sorted and count queries, this is mainly to serve test cases. + * Resolves the alias for the entity in the FROM clause from the JPA query. Since the {@link JpaQueryParserSupport} + * can already find the alias when generating sorted and count queries, this is mainly to serve test cases. */ @Override public String detectAlias() { @@ -85,7 +111,7 @@ public String detectAlias() { /** * Creates a count query from the original query, with no count projection. - * + * * @return Guaranteed to be not {@literal null}; */ @Override @@ -114,8 +140,8 @@ public boolean hasConstructorExpression() { } /** - * Looks up the projection of the JPA query. Since the {@link JpaQueryParser} can already find the projection when - * generating sorted and count queries, this is mainly to serve test cases. + * Looks up the projection of the JPA query. Since the {@link JpaQueryParserSupport} can already find the projection + * when generating sorted and count queries, this is mainly to serve test cases. */ @Override public String getProjection() { @@ -123,7 +149,7 @@ public String getProjection() { } /** - * Since the {@link JpaQueryParser} can already fully transform sorted and count queries by itself, this is a + * Since the {@link JpaQueryParserSupport} can already fully transform sorted and count queries by itself, this is a * placeholder method. * * @return empty set @@ -134,10 +160,10 @@ public Set getJoinAliases() { } /** - * Look up the {@link DeclaredQuery} from the {@link JpaQueryParser}. + * Look up the {@link DeclaredQuery} from the {@link JpaQueryParserSupport}. */ @Override public DeclaredQuery getQuery() { - return queryParser.getDeclaredQuery(); + return query; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParserSupport.java similarity index 63% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParser.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParserSupport.java index c41a4e56cb..892476ad23 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParser.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParserSupport.java @@ -20,19 +20,24 @@ import java.util.List; import java.util.regex.Pattern; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.atn.PredictionMode; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.JpaSort; +import org.springframework.data.util.Lazy; import org.springframework.lang.Nullable; /** * Operations needed to parse a JPA query. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.1 */ -abstract class JpaQueryParser { +abstract class JpaQueryParserSupport { private static final Pattern PUNCTUATION_PATTERN = Pattern.compile(".*((?![._])[\\p{Punct}|\\s])"); @@ -40,22 +45,10 @@ abstract class JpaQueryParser { + "aliases used in the select clause; If you really want to use something other than that for sorting, please use " + "JpaSort.unsafe(…)"; - private final DeclaredQuery declaredQuery; + private final ParseState state; - JpaQueryParser(DeclaredQuery declaredQuery) { - this.declaredQuery = declaredQuery; - } - - JpaQueryParser(String query) { - this(DeclaredQuery.of(query, false)); - } - - DeclaredQuery getDeclaredQuery() { - return declaredQuery; - } - - String getQuery() { - return getDeclaredQuery().getQueryString(); + JpaQueryParserSupport(String query) { + this.state = new ParseState(query); } /** @@ -64,17 +57,11 @@ String getQuery() { * * @param sort can be {@literal null} */ - String createQuery(Sort sort) { + String renderSortedQuery(Sort sort) { try { - ParserRuleContext parsedQuery = parse(); - - if (parsedQuery == null) { - return ""; - } - - return render(doCreateQuery(parsedQuery, sort)); - } catch (JpaQueryParsingSyntaxError e) { + return render(applySort(state.getContext(), sort)); + } catch (BadJpqlGrammarException e) { throw new IllegalArgumentException(e); } } @@ -87,34 +74,21 @@ String createQuery(Sort sort) { String createCountQuery(@Nullable String countProjection) { try { - ParserRuleContext parsedQuery = parse(); - - if (parsedQuery == null) { - return ""; - } - - return render(doCreateCountQuery(parsedQuery, countProjection)); - } catch (JpaQueryParsingSyntaxError e) { + return render(doCreateCountQuery(state.getContext(), countProjection)); + } catch (BadJpqlGrammarException e) { throw new IllegalArgumentException(e); } } /** * Find the projection of the query. - * - * @param parsedQuery */ String projection() { try { - ParserRuleContext parsedQuery = parse(); - - if (parsedQuery == null) { - return ""; - } - - return render(doFindProjection(parsedQuery)); - } catch (JpaQueryParsingSyntaxError e) { + List tokens = doFindProjection(state.getContext()); + return tokens.isEmpty() ? "" : render(tokens); + } catch (BadJpqlGrammarException e) { return ""; } } @@ -124,17 +98,12 @@ String projection() { * * @return can be {@literal null} */ + @Nullable String findAlias() { try { - ParserRuleContext parsedQuery = parse(); - - if (parsedQuery == null) { - return null; - } - - return doFindAlias(parsedQuery); - } catch (JpaQueryParsingSyntaxError e) { + return doFindAlias(state.getContext()); + } catch (BadJpqlGrammarException e) { return null; } } @@ -147,39 +116,36 @@ String findAlias() { boolean hasConstructorExpression() { try { - ParserRuleContext parsedQuery = parse(); - - if (parsedQuery == null) { - return false; - } - - return doCheckForConstructor(parsedQuery); - } catch (JpaQueryParsingSyntaxError e) { + return doCheckForConstructor(state.getContext()); + } catch (BadJpqlGrammarException e) { return false; } } /** - * Check any given {@link JpaSort.JpaOrder#isUnsafe()} order for presence of at least one property offending the - * {@link #PUNCTUATION_PATTERN} and throw an {@link Exception} indicating potential unsafe order by expression. + * Parse the JPA query using its corresponding ANTLR parser. + */ + protected abstract ParserRuleContext parse(String query); + + /** + * Apply common configuration (SLL prediction for performance, our own error listeners). * - * @param order + * @param query + * @param lexer + * @param parser */ - static void checkSortExpression(Sort.Order order) { + static void configureParser(String query, Lexer lexer, Parser parser) { - if (order instanceof JpaSort.JpaOrder && ((JpaSort.JpaOrder) order).isUnsafe()) { - return; - } + BadJpqlGrammarErrorListener errorListener = new BadJpqlGrammarErrorListener(query); - if (PUNCTUATION_PATTERN.matcher(order.getProperty()).find()) { - throw new InvalidDataAccessApiUsageException(String.format(UNSAFE_PROPERTY_REFERENCE, order)); - } - } + lexer.removeErrorListeners(); + lexer.addErrorListener(errorListener); - /** - * Parse the JPA query using its corresponding ANTLR parser. - */ - protected abstract ParserRuleContext parse(); + parser.getInterpreter().setPredictionMode(PredictionMode.SLL); + + parser.removeErrorListeners(); + parser.addErrorListener(errorListener); + } /** * Create a {@link JpaQueryParsingToken}-based query with an {@literal order by} applied/amended based upon the @@ -188,7 +154,7 @@ static void checkSortExpression(Sort.Order order) { * @param parsedQuery * @param sort can be {@literal null} */ - protected abstract List doCreateQuery(ParserRuleContext parsedQuery, Sort sort); + protected abstract List applySort(ParserRuleContext parsedQuery, Sort sort); /** * Create a {@link JpaQueryParsingToken}-based count query. @@ -197,8 +163,9 @@ static void checkSortExpression(Sort.Order order) { * @param countProjection */ protected abstract List doCreateCountQuery(ParserRuleContext parsedQuery, - @Nullable String countProjection); + @Nullable String countProjection); + @Nullable protected abstract String doFindAlias(ParserRuleContext parsedQuery); /** @@ -210,4 +177,56 @@ protected abstract List doCreateCountQuery(ParserRuleConte protected abstract boolean doCheckForConstructor(ParserRuleContext parsedQuery); + /** + * Check any given {@link JpaSort.JpaOrder#isUnsafe()} order for presence of at least one property offending the + * {@link #PUNCTUATION_PATTERN} and throw an {@link Exception} indicating potential unsafe order by expression. + * + * @param order + */ + static void checkSortExpression(Sort.Order order) { + + if (order instanceof JpaSort.JpaOrder && ((JpaSort.JpaOrder) order).isUnsafe()) { + return; + } + + if (PUNCTUATION_PATTERN.matcher(order.getProperty()).find()) { + throw new InvalidDataAccessApiUsageException(String.format(UNSAFE_PROPERTY_REFERENCE, order)); + } + } + + /** + * Parser state capturing the lazily-parsed parser context. + */ + class ParseState { + + private final Lazy parsedQuery; + private volatile @Nullable BadJpqlGrammarException error; + private final String query; + + public ParseState(String query) { + this.query = query; + this.parsedQuery = Lazy.of(() -> parse(query)); + } + + public ParserRuleContext getContext() { + + BadJpqlGrammarException error = this.error; + + if (error != null) { + throw error; + } + + try { + return parsedQuery.get(); + } catch (BadJpqlGrammarException e) { + this.error = error = e; + throw error; + } + } + + public String getQuery() { + return query; + } + } + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java index 9ef60b9710..5c221e62d4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java @@ -162,10 +162,6 @@ static void CLIP(List tokens) { */ static String render(List tokens) { - if (tokens == null) { - return ""; - } - StringBuilder results = new StringBuilder(); tokens.forEach(token -> { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryParser.java index c7cfef600c..2db207e351 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryParser.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryParser.java @@ -24,46 +24,44 @@ import org.springframework.lang.Nullable; /** - * Implements the parsing operations of a {@link JpaQueryParser} using the ANTLR-generated {@link JpqlParser} and - * {@link JpqlQueryTransformer}. + * Implements the {@code JPQL} parsing operations of a {@link JpaQueryParserSupport} using the ANTLR-generated + * {@link JpqlParser} and {@link JpqlQueryTransformer}. * * @author Greg Turnquist + * @author Mark Paluch * @since 3.1 */ -class JpqlQueryParser extends JpaQueryParser { - - JpqlQueryParser(DeclaredQuery declaredQuery) { - super(declaredQuery); - } +class JpqlQueryParser extends JpaQueryParserSupport { JpqlQueryParser(String query) { super(query); } /** - * Convenience method to parse a JPQL query. Will throw a {@link JpaQueryParsingSyntaxError} if the query is invalid. + * Convenience method to parse a JPQL query. Will throw a {@link BadJpqlGrammarException} if the query is invalid. * * @param query * @return a parsed query, ready for postprocessing */ - static ParserRuleContext parse(String query) { + public static ParserRuleContext parseQuery(String query) { JpqlLexer lexer = new JpqlLexer(CharStreams.fromString(query)); JpqlParser parser = new JpqlParser(new CommonTokenStream(lexer)); - parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener()); + configureParser(query, lexer, parser); return parser.start(); } + /** - * Parse the query using {@link #parse(String)}. + * Parse the query using {@link #parseQuery(String)}. * * @return a parsed query */ @Override - protected ParserRuleContext parse() { - return parse(getQuery()); + protected ParserRuleContext parse(String query) { + return parseQuery(query); } /** @@ -74,7 +72,7 @@ protected ParserRuleContext parse() { * @return list of {@link JpaQueryParsingToken}s */ @Override - protected List doCreateQuery(ParserRuleContext parsedQuery, Sort sort) { + protected List applySort(ParserRuleContext parsedQuery, Sort sort) { return new JpqlQueryTransformer(sort).visit(parsedQuery); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java index fe030c598a..3749250ff6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java @@ -18,10 +18,12 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.springframework.data.domain.Sort; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed JPQL query. @@ -31,30 +33,34 @@ */ class JpqlQueryTransformer extends JpqlQueryRenderer { - @Nullable private Sort sort; - private boolean countQuery; + // TODO: Separate input from result parameters, encapsulation... + private final Sort sort; + private final boolean countQuery; - @Nullable private String countProjection; + private final @Nullable String countProjection; - @Nullable private String alias = null; + private @Nullable String alias = null; - private List projection = null; + private List projection = Collections.emptyList(); + private boolean projectionProcessed; private boolean hasConstructorExpression = false; JpqlQueryTransformer() { - this(null, false, null); + this(Sort.unsorted(), false, null); } - JpqlQueryTransformer(@Nullable Sort sort) { + JpqlQueryTransformer(Sort sort) { this(sort, false, null); } JpqlQueryTransformer(boolean countQuery, @Nullable String countProjection) { - this(null, countQuery, countProjection); + this(Sort.unsorted(), countQuery, countProjection); } - private JpqlQueryTransformer(@Nullable Sort sort, boolean countQuery, @Nullable String countProjection) { + private JpqlQueryTransformer(Sort sort, boolean countQuery, @Nullable String countProjection) { + + Assert.notNull(sort, "Sort must not be null"); this.sort = sort; this.countQuery = countQuery; @@ -77,7 +83,7 @@ public boolean hasConstructorExpression() { @Override public List visitSelect_statement(JpqlParser.Select_statementContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); tokens.addAll(visit(ctx.select_clause())); tokens.addAll(visit(ctx.from_clause())); @@ -100,7 +106,7 @@ public List visitSelect_statement(JpqlParser.Select_statem tokens.addAll(visit(ctx.orderby_clause())); } - if (this.sort != null && this.sort.isSorted()) { + if (this.sort.isSorted()) { if (ctx.orderby_clause() != null) { @@ -114,7 +120,7 @@ public List visitSelect_statement(JpqlParser.Select_statem this.sort.forEach(order -> { - JpaQueryParser.checkSortExpression(order); + JpaQueryParserSupport.checkSortExpression(order); if (order.isIgnoreCase()) { tokens.add(TOKEN_LOWER_FUNC); @@ -144,7 +150,7 @@ public List visitSelect_statement(JpqlParser.Select_statem @Override public List visitSelect_clause(JpqlParser.Select_clauseContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); tokens.add(new JpaQueryParsingToken(ctx.SELECT())); @@ -156,7 +162,7 @@ public List visitSelect_clause(JpqlParser.Select_clauseCon tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); } - List selectItemTokens = new ArrayList<>(); + List selectItemTokens = newArrayList(); ctx.select_item().forEach(selectItemContext -> { selectItemTokens.addAll(visit(selectItemContext)); @@ -192,8 +198,9 @@ public List visitSelect_clause(JpqlParser.Select_clauseCon tokens.addAll(selectItemTokens); } - if (projection == null) { + if (!projectionProcessed) { this.projection = selectItemTokens; + this.projectionProcessed = true; } return tokens; @@ -202,7 +209,7 @@ public List visitSelect_clause(JpqlParser.Select_clauseCon @Override public List visitRange_variable_declaration(JpqlParser.Range_variable_declarationContext ctx) { - List tokens = new ArrayList<>(); + List tokens = newArrayList(); tokens.addAll(visit(ctx.entity_name())); @@ -226,4 +233,8 @@ public List visitConstructor_expression(JpqlParser.Constru return super.visitConstructor_expression(ctx); } + + private static ArrayList newArrayList() { + return new ArrayList<>(); + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java index e74952e134..74aa77e611 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java @@ -17,21 +17,36 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.util.ClassUtils; /** * Encapsulates different strategies for the creation of a {@link QueryEnhancer} from a {@link DeclaredQuery}. * * @author Diego Krupitza * @author Greg Turnquist + * @author Mark Paluch * @since 2.7.0 */ public final class QueryEnhancerFactory { private static final Log LOG = LogFactory.getLog(QueryEnhancerFactory.class); - private static final boolean JSQLPARSER_IN_CLASSPATH = isJSqlParserInClassPath(); + private static final boolean jSqlParserPresent = ClassUtils.isPresent("net.sf.jsqlparser.parser.JSqlParser", + QueryEnhancerFactory.class.getClassLoader()); - private static final boolean HIBERNATE_IN_CLASSPATH = isHibernateInClassPath(); + private static final boolean hibernatePresent = ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", + QueryEnhancerFactory.class.getClassLoader()); + + static { + + if (jSqlParserPresent) { + LOG.info("JSqlParser is in classpath; If applicable, JSqlParser will be used"); + } + + if (hibernatePresent) { + LOG.info("Hibernate is in classpath; If applicable, HQL parser will be used."); + } + } private QueryEnhancerFactory() {} @@ -45,81 +60,17 @@ public static QueryEnhancer forQuery(DeclaredQuery query) { if (query.isNativeQuery()) { - if (qualifiesForJSqlParserUsage(query)) { - /** + if (jSqlParserPresent) { + /* * If JSqlParser fails, throw some alert signaling that people should write a custom Impl. */ return new JSqlParserQueryEnhancer(query); - } else { - return new DefaultQueryEnhancer(query); } - } else { - if (qualifiedForHqlParserUsage(query)) { - return new JpaQueryParsingEnhancer(new HqlQueryParser(query)); - } else if (qualifiesForJpqlParserUsage(query)) { - return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)); - } else { - return new DefaultQueryEnhancer(query); - } + return new DefaultQueryEnhancer(query); } - } - /** - * Checks if a given query can be process with the JSqlParser under the condition that the parser is in the classpath. - * - * @param query the query we want to check - * @return true if JSqlParser is in the classpath and the query is classified as a native query and not - * to be bypassed otherwise false - */ - private static boolean qualifiesForJSqlParserUsage(DeclaredQuery query) { - return JSQLPARSER_IN_CLASSPATH && query.isNativeQuery(); - } - - /** - * Checks if the query is a candidate for the HQL parser. - * - * @param query the query we want to check - * @return true if Hibernate is in the classpath and the query is NOT classified as native - */ - private static boolean qualifiedForHqlParserUsage(DeclaredQuery query) { - return HIBERNATE_IN_CLASSPATH && !query.isNativeQuery(); + return hibernatePresent ? JpaQueryEnhancer.forHql(query) : JpaQueryEnhancer.forJpql(query); } - /** - * Checks if the query is a candidate for the JPQL spec parser. - * - * @param query the query we want to check - * @return true if the query is NOT classified as a native query - */ - private static boolean qualifiesForJpqlParserUsage(DeclaredQuery query) { - return !query.isNativeQuery(); - } - - /** - * Checks whether JSqlParser is in classpath or not. - * - * @return true when in classpath otherwise false - */ - private static boolean isJSqlParserInClassPath() { - - try { - Class.forName("net.sf.jsqlparser.parser.JSqlParser", false, QueryEnhancerFactory.class.getClassLoader()); - LOG.info("JSqlParser is in classpath; If applicable JSqlParser will be used"); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - - private static boolean isHibernateInClassPath() { - - try { - Class.forName("org.hibernate.query.TypedParameterValue", false, QueryEnhancerFactory.class.getClassLoader()); - LOG.info("Hibernate is in classpath; If applicable Hql61Parser will be used."); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java index 256f7af4fd..aefc1a2ca9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java @@ -22,7 +22,7 @@ import org.junit.jupiter.params.provider.MethodSource; /** - * TCK Tests for {@link HqlQueryParser} mixed into {@link JpaQueryParsingEnhancer}. + * TCK Tests for {@link HqlQueryParser} mixed into {@link JpaQueryEnhancer}. * * @author Greg Turnquist * @since 3.1 @@ -32,8 +32,8 @@ public class HqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { public static final String HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES = "HqlParser does not support native queries"; @Override - QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { - return new JpaQueryParsingEnhancer(new HqlQueryParser(declaredQuery)); + QueryEnhancer createQueryEnhancer(DeclaredQuery query) { + return JpaQueryEnhancer.forHql(query); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 2a1c9aff02..c161fd5eab 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -39,7 +39,7 @@ class HqlQueryRendererTests { /** * Parse the query using {@link HqlParser} then run it through the query-preserving {@link HqlQueryRenderer}. - * + * * @param query */ private static String parseWithoutChanges(String query) { @@ -47,7 +47,7 @@ private static String parseWithoutChanges(String query) { HqlLexer lexer = new HqlLexer(CharStreams.fromString(query)); HqlParser parser = new HqlParser(new CommonTokenStream(lexer)); - parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener()); + parser.addErrorListener(new BadJpqlGrammarErrorListener(query)); HqlParser.StartContext parsedQuery = parser.start(); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index b8906ddc77..5dbc29f961 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -27,8 +27,7 @@ import org.springframework.lang.Nullable; /** - * Verify that HQL queries are properly transformed through the {@link JpaQueryParsingEnhancer} and the - * {@link HqlQueryParser}. + * Verify that HQL queries are properly transformed through the {@link JpaQueryEnhancer} and the {@link HqlQueryParser}. * * @author Greg Turnquist * @since 3.1 @@ -118,7 +117,7 @@ void multipleAliasesShouldBeGathered() { var original = "select e from Employee e join e.manager m"; // when - var results = createQueryFor(original, null); + var results = createQueryFor(original, Sort.unsorted()); // then assertThat(results).isEqualTo("select e from Employee e join e.manager m"); @@ -208,12 +207,12 @@ void applySortingAccountsForNewlinesInSubselect() { Sort sort = Sort.by(Sort.Order.desc("age")); - assertThat(new JpaQueryParsingEnhancer(new HqlQueryParser("select u\n" + // + assertThat(newParser("select u\n" + // "from user u\n" + // "where exists (select u2\n" + // "from user u2\n" + // ")\n" + // - "")).applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + // + "").applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + // "from user u\n" + // "where exists (select u2\n" + // "from user u2\n" + // @@ -790,7 +789,7 @@ private void assertCountQuery(String originalQuery, String countQuery) { } private String createQueryFor(String query, Sort sort) { - return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).applySorting(sort); + return newParser(query).applySorting(sort); } private String createCountQueryFor(String query) { @@ -798,18 +797,23 @@ private String createCountQueryFor(String query) { } private String createCountQueryFor(String query, @Nullable String countProjection) { - return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).createCountQueryFor(countProjection); + return newParser(query).createCountQueryFor(countProjection); } + @Nullable private String alias(String query) { - return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).detectAlias(); + return newParser(query).detectAlias(); } private boolean hasConstructorExpression(String query) { - return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).hasConstructorExpression(); + return newParser(query).hasConstructorExpression(); } private String projection(String query) { - return new JpaQueryParsingEnhancer(new HqlQueryParser(query)).getProjection(); + return newParser(query).getProjection(); + } + + private QueryEnhancer newParser(String query) { + return JpaQueryEnhancer.forHql(DeclaredQuery.of(query, false)); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java index 179855736c..88d0656c7b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlSpecificationTests.java @@ -38,7 +38,7 @@ class HqlSpecificationTests { @Test void joinExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order AS o JOIN o.lineItems AS l WHERE l.shipped = FALSE @@ -52,7 +52,7 @@ void joinExample1() { @Test void joinExample2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l JOIN l.product p WHERE p.productType = 'office_supplies' @@ -65,7 +65,7 @@ void joinExample2() { @Test void rangeVariableDeclarations() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o1 FROM Order o1, Order o2 WHERE o1.quantity > o2.quantity AND @@ -80,7 +80,7 @@ void rangeVariableDeclarations() { @Test void pathExpressionsExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT i.name, VALUE(p) FROM Item i JOIN i.photos p WHERE KEY(p) LIKE '%egret' @@ -93,7 +93,7 @@ WHERE KEY(p) LIKE '%egret' @Test void pathExpressionsExample2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT i.name, p FROM Item i JOIN i.photos p WHERE KEY(p) LIKE '%egret' @@ -106,7 +106,7 @@ WHERE KEY(p) LIKE '%egret' @Test void pathExpressionsExample3() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT p.vendor FROM Employee e JOIN e.contactInfo.phones p """); @@ -118,7 +118,7 @@ void pathExpressionsExample3() { @Test void pathExpressionsExample4() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT p.vendor FROM Employee e JOIN e.contactInfo c JOIN c.phones p WHERE e.contactInfo.address.zipcode = '95054' @@ -128,7 +128,7 @@ void pathExpressionsExample4() { @Test void pathExpressionSyntaxExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT l.product FROM Order AS o JOIN o.lineItems l """); @@ -137,7 +137,7 @@ void pathExpressionSyntaxExample1() { @Test void joinsExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize """); } @@ -145,7 +145,7 @@ void joinsExample1() { @Test void joinsExample2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 """); } @@ -153,7 +153,7 @@ void joinsExample2() { @Test void joinsInnerExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 """); } @@ -161,7 +161,7 @@ void joinsInnerExample() { @Test void joinsInExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 """); } @@ -169,7 +169,7 @@ SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 @Test void doubleJoinExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT p.vendor FROM Employee e JOIN e.contactInfo c JOIN c.phones p WHERE c.address.zipcode = '95054' @@ -179,7 +179,7 @@ void doubleJoinExample() { @Test void leftJoinExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT s.name, COUNT(p) FROM Suppliers s LEFT JOIN s.products p GROUP BY s.name @@ -189,7 +189,7 @@ SELECT s.name, COUNT(p) @Test void leftJoinOnExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT s.name, COUNT(p) FROM Suppliers s LEFT JOIN s.products p ON p.status = 'inStock' @@ -200,7 +200,7 @@ SELECT s.name, COUNT(p) @Test void leftJoinWhereExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT s.name, COUNT(p) FROM Suppliers s LEFT JOIN s.products p WHERE p.status = 'inStock' @@ -211,7 +211,7 @@ SELECT s.name, COUNT(p) @Test void leftJoinFetchExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT d FROM Department d LEFT JOIN FETCH d.employees WHERE d.deptno = 1 @@ -221,7 +221,7 @@ void leftJoinFetchExample() { @Test void collectionMemberExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l WHERE l.product.productType = 'office_supplies' @@ -231,7 +231,7 @@ void collectionMemberExample() { @Test void collectionMemberInExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o, IN(o.lineItems) l WHERE l.product.productType = 'office_supplies' @@ -241,7 +241,7 @@ FROM Order o, IN(o.lineItems) l @Test void fromClauseExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Order AS o JOIN o.lineItems l JOIN l.product p """); @@ -250,7 +250,7 @@ void fromClauseExample() { @Test void fromClauseDowncastingExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT b.name, b.ISBN FROM Order o JOIN TREAT(o.product AS Book) b """); @@ -259,7 +259,7 @@ FROM Order o JOIN TREAT(o.product AS Book) b @Test void fromClauseDowncastingExample2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp WHERE lp.budget > 1000 """); @@ -272,7 +272,7 @@ SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") void fromClauseDowncastingExample3_SPEC_BUG() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e FROM Employee e JOIN e.projects p WHERE TREAT(p AS LargeProject).budget > 1000 OR TREAT(p AS SmallProject).name LIKE 'Persist%' @@ -283,7 +283,7 @@ OR TREAT(p AS SmallProject).name LIKE 'Persist%' @Test void fromClauseDowncastingExample3fixed() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e FROM Employee e JOIN e.projects p WHERE TREAT(p AS LargeProject).budget > 1000 OR TREAT(p AS SmallProject).name LIKE 'Persist%' @@ -294,7 +294,7 @@ OR TREAT(p AS SmallProject).name LIKE 'Persist%' @Test void fromClauseDowncastingExample4() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e FROM Employee e WHERE TREAT(e AS Exempt).vacationDays > 10 OR TREAT(e AS Contractor).hours > 100 @@ -304,7 +304,7 @@ OR TREAT(e AS Contractor).hours > 100 @Test void pathExpressionsNamedParametersExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c FROM Customer c WHERE c.status = :stat @@ -314,7 +314,7 @@ void pathExpressionsNamedParametersExample() { @Test void betweenExpressionsExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT t FROM CreditCard c JOIN c.transactionHistory t WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 @@ -324,7 +324,7 @@ void betweenExpressionsExample() { @Test void isEmptyExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.lineItems IS EMPTY @@ -334,7 +334,7 @@ void isEmptyExample() { @Test void memberOfExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT p FROM Person p WHERE 'Joe' MEMBER OF p.nicknames @@ -344,7 +344,7 @@ void memberOfExample() { @Test void existsSubSelectExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT emp FROM Employee emp WHERE EXISTS ( @@ -357,7 +357,7 @@ WHERE EXISTS ( @Test void allExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT emp FROM Employee emp WHERE emp.salary > ALL ( @@ -370,7 +370,7 @@ WHERE emp.salary > ALL ( @Test void existsSubSelectExample2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT emp FROM Employee emp WHERE EXISTS ( @@ -383,7 +383,7 @@ WHERE EXISTS ( @Test void subselectNumericComparisonExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c FROM Customer c WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 @@ -393,7 +393,7 @@ void subselectNumericComparisonExample1() { @Test void subselectNumericComparisonExample2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT goodCustomer FROM Customer goodCustomer WHERE goodCustomer.balanceOwed < ( @@ -404,7 +404,7 @@ SELECT AVG(c.balanceOwed)/2.0 FROM Customer c) @Test void indexExample() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT w.name FROM Course c JOIN c.studentWaitlist w WHERE c.name = 'Calculus' @@ -419,7 +419,7 @@ AND INDEX(w) = 0 @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") void functionInvocationExample_SPEC_BUG() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c FROM Customer c WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) @@ -429,7 +429,7 @@ WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) @Test void functionInvocationExampleWithCorrection() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c FROM Customer c WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE @@ -439,7 +439,7 @@ WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE @Test void updateCaseExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" UPDATE Employee e SET e.salary = CASE WHEN e.rating = 1 THEN e.salary * 1.1 @@ -452,7 +452,7 @@ void updateCaseExample1() { @Test void updateCaseExample2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" UPDATE Employee e SET e.salary = CASE e.rating WHEN 1 THEN e.salary * 1.1 @@ -465,7 +465,7 @@ void updateCaseExample2() { @Test void selectCaseExample1() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e.name, CASE TYPE(e) WHEN Exempt THEN 'Exempt' WHEN Contractor THEN 'Contractor' @@ -480,7 +480,7 @@ CASE TYPE(e) WHEN Exempt THEN 'Exempt' @Test void selectCaseExample2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e.name, f.name, CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' @@ -495,7 +495,7 @@ void selectCaseExample2() { @Test void theRest() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e FROM Employee e WHERE TYPE(e) IN (Exempt, Contractor) @@ -505,7 +505,7 @@ WHERE TYPE(e) IN (Exempt, Contractor) @Test void theRest2() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e FROM Employee e WHERE TYPE(e) IN (:empType1, :empType2) @@ -515,7 +515,7 @@ WHERE TYPE(e) IN (:empType1, :empType2) @Test void theRest3() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e FROM Employee e WHERE TYPE(e) IN :empTypes @@ -525,7 +525,7 @@ WHERE TYPE(e) IN :empTypes @Test void theRest4() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT TYPE(e) FROM Employee e WHERE TYPE(e) <> Exempt @@ -535,7 +535,7 @@ WHERE TYPE(e) <> Exempt @Test void theRest5() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c.status, AVG(c.filledOrderCount), COUNT(c) FROM Customer c GROUP BY c.status @@ -546,7 +546,7 @@ HAVING c.status IN (1, 2) @Test void theRest6() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c.country, COUNT(c) FROM Customer c GROUP BY c.country @@ -557,7 +557,7 @@ HAVING COUNT(c) > 30 @Test void theRest7() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c, COUNT(o) FROM Customer c JOIN c.orders o GROUP BY c @@ -568,7 +568,7 @@ HAVING COUNT(o) >= 5 @Test void theRest8() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c.id, c.status FROM Customer c JOIN c.orders o WHERE o.count > 100 @@ -578,7 +578,7 @@ void theRest8() { @Test void theRest9() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT v.location.street, KEY(i).title, VALUE(i) FROM VideoStore v JOIN v.videoInventory i WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 @@ -588,7 +588,7 @@ SELECT v.location.street, KEY(i).title, VALUE(i) @Test void theRest10() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o.lineItems FROM Order AS o """); } @@ -596,7 +596,7 @@ void theRest10() { @Test void theRest11() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT c, COUNT(l) AS itemCount FROM Customer c JOIN c.Orders o JOIN o.lineItems l WHERE c.address.state = 'CA' @@ -608,7 +608,7 @@ SELECT c, COUNT(l) AS itemCount @Test void theRest12() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) FROM Customer c JOIN c.orders o WHERE o.count > 100 @@ -618,7 +618,7 @@ void theRest12() { @Test void theRest13() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT e.address AS addr FROM Employee e """); @@ -627,7 +627,7 @@ void theRest13() { @Test void theRest14() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT AVG(o.quantity) FROM Order o """); } @@ -635,7 +635,7 @@ SELECT AVG(o.quantity) FROM Order o @Test void theRest15() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT SUM(l.price) FROM Order o JOIN o.lineItems l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' @@ -645,7 +645,7 @@ SELECT SUM(l.price) @Test void theRest16() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT COUNT(o) FROM Order o """); } @@ -653,7 +653,7 @@ SELECT COUNT(o) FROM Order o @Test void theRest17() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT COUNT(l.price) FROM Order o JOIN o.lineItems l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' @@ -663,7 +663,7 @@ SELECT COUNT(l.price) @Test void theRest18() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT COUNT(l) FROM Order o JOIN o.lineItems l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL @@ -673,7 +673,7 @@ SELECT COUNT(l) @Test void theRest19() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Customer c JOIN c.orders o JOIN c.address a WHERE a.state = 'CA' @@ -684,7 +684,7 @@ void theRest19() { @Test void theRest20() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o.quantity, a.zipcode FROM Customer c JOIN c.orders o JOIN c.address a WHERE a.state = 'CA' @@ -695,7 +695,7 @@ void theRest20() { @Test void theRest21() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode FROM Customer c JOIN c.orders o JOIN c.address a WHERE a.state = 'CA' AND a.county = 'Santa Clara' @@ -706,7 +706,7 @@ void theRest21() { @Test void theRest22() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT AVG(o.quantity) as q, a.zipcode FROM Customer c JOIN c.orders o JOIN c.address a WHERE a.state = 'CA' @@ -718,7 +718,7 @@ SELECT AVG(o.quantity) as q, a.zipcode @Test void theRest23() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT p.product_name FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' @@ -732,7 +732,7 @@ void theRest23() { @Test void theRest24() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT p.product_name FROM Order o, IN(o.lineItems) l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' @@ -743,7 +743,7 @@ FROM Order o, IN(o.lineItems) l JOIN o.customer c @Test void theRest25() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" DELETE FROM Customer c WHERE c.status = 'inactive' @@ -753,7 +753,7 @@ void theRest25() { @Test void theRest26() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" DELETE FROM Customer c WHERE c.status = 'inactive' @@ -764,7 +764,7 @@ void theRest26() { @Test void theRest27() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" UPDATE Customer c SET c.status = 'outstanding' WHERE c.balance < 10000 @@ -774,7 +774,7 @@ void theRest27() { @Test void theRest28() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" UPDATE Employee e SET e.address.building = 22 WHERE e.address.building = 14 @@ -786,7 +786,7 @@ void theRest28() { @Test void theRest29() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Order o """); @@ -795,7 +795,7 @@ void theRest29() { @Test void theRest30() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.shippingAddress.state = 'CA' @@ -805,7 +805,7 @@ void theRest30() { @Test void theRest31() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o.shippingAddress.state FROM Order o """); @@ -814,7 +814,7 @@ void theRest31() { @Test void theRest32() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l """); @@ -823,7 +823,7 @@ void theRest32() { @Test void theRest33() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.lineItems IS NOT EMPTY @@ -833,7 +833,7 @@ void theRest33() { @Test void theRest34() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.lineItems IS EMPTY @@ -843,7 +843,7 @@ void theRest34() { @Test void theRest35() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l WHERE l.shipped = FALSE @@ -853,7 +853,7 @@ void theRest35() { @Test void theRest36() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE @@ -866,7 +866,7 @@ void theRest36() { @Test void theRest37() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.shippingAddress <> o.billingAddress @@ -876,7 +876,7 @@ void theRest37() { @Test void theRest38() { - HqlQueryParser.parse(""" + HqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l WHERE l.product.name = ?1 @@ -886,78 +886,78 @@ void theRest38() { @Test void hqlQueries() { - HqlQueryParser.parse("from Person"); - HqlQueryParser.parse("select local datetime"); - HqlQueryParser.parse("from Person p select p.name"); - HqlQueryParser.parse("update Person set nickName = 'Nacho' " + // + HqlQueryParser.parseQuery("from Person"); + HqlQueryParser.parseQuery("select local datetime"); + HqlQueryParser.parseQuery("from Person p select p.name"); + HqlQueryParser.parseQuery("update Person set nickName = 'Nacho' " + // "where name = 'Ignacio'"); - HqlQueryParser.parse("update Person p " + // + HqlQueryParser.parseQuery("update Person p " + // "set p.name = :newName " + // "where p.name = :oldName"); - HqlQueryParser.parse("update Person " + // + HqlQueryParser.parseQuery("update Person " + // "set name = :newName " + // "where name = :oldName"); - HqlQueryParser.parse("update versioned Person " + // + HqlQueryParser.parseQuery("update versioned Person " + // "set name = :newName " + // "where name = :oldName"); - HqlQueryParser.parse("insert Person (id, name) " + // + HqlQueryParser.parseQuery("insert Person (id, name) " + // "values (100L, 'Jane Doe')"); - HqlQueryParser.parse("insert Person (id, name) " + // + HqlQueryParser.parseQuery("insert Person (id, name) " + // "values (101L, 'J A Doe III'), " + // "(102L, 'J X Doe'), " + // "(103L, 'John Doe, Jr')"); - HqlQueryParser.parse("insert into Partner (id, name) " + // + HqlQueryParser.parseQuery("insert into Partner (id, name) " + // "select p.id, p.name " + // "from Person p "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.name like 'Joe'"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.name like 'Joe''s'"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.id = 1"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.id = 1L"); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "where c.duration > 100.5"); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "where c.duration > 100.5F"); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "where c.duration > 1e+2"); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "where c.duration > 1e+2F"); - HqlQueryParser.parse("from Phone ph " + // + HqlQueryParser.parseQuery("from Phone ph " + // "where ph.type = LAND_LINE"); - HqlQueryParser.parse("select java.lang.Math.PI"); - HqlQueryParser.parse("select 'Customer ' || p.name " + // + HqlQueryParser.parseQuery("select java.lang.Math.PI"); + HqlQueryParser.parseQuery("select 'Customer ' || p.name " + // "from Person p " + // "where p.id = 1"); - HqlQueryParser.parse("select sum(ch.duration) * :multiplier " + // + HqlQueryParser.parseQuery("select sum(ch.duration) * :multiplier " + // "from Person pr " + // "join pr.phones ph " + // "join ph.callHistory ch " + // "where ph.id = 1L "); - HqlQueryParser.parse("select year(local date) - year(p.createdOn) " + // + HqlQueryParser.parseQuery("select year(local date) - year(p.createdOn) " + // "from Person p " + // "where p.id = 1L"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where year(local date) - year(p.createdOn) > 1"); - HqlQueryParser.parse("select " + // + HqlQueryParser.parseQuery("select " + // " case p.nickName " + // " when 'NA' " + // " then '' " + // " else p.nickName " + // " end " + // "from Person p"); - HqlQueryParser.parse("select " + // + HqlQueryParser.parseQuery("select " + // " case " + // " when p.nickName is null " + // " then " + // @@ -969,162 +969,162 @@ void hqlQueries() { " else p.nickName " + // " end " + // "from Person p"); - HqlQueryParser.parse("select " + // + HqlQueryParser.parseQuery("select " + // " case when p.nickName is null " + // " then p.id * 1000 " + // " else p.id " + // " end " + // "from Person p " + // "order by p.id"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Payment p " + // "where type(p) = CreditCardPayment"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Payment p " + // "where type(p) = :type"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Payment p " + // "where length(treat(p as CreditCardPayment).cardNumber) between 16 and 20"); - HqlQueryParser.parse("select nullif(p.nickName, p.name) " + // + HqlQueryParser.parseQuery("select nullif(p.nickName, p.name) " + // "from Person p"); - HqlQueryParser.parse("select " + // + HqlQueryParser.parseQuery("select " + // " case" + // " when p.nickName = p.name" + // " then null" + // " else p.nickName" + // " end " + // "from Person p"); - HqlQueryParser.parse("select coalesce(p.nickName, '') " + // + HqlQueryParser.parseQuery("select coalesce(p.nickName, '') " + // "from Person p"); - HqlQueryParser.parse("select coalesce(p.nickName, p.name, '') " + // + HqlQueryParser.parseQuery("select coalesce(p.nickName, p.name, '') " + // "from Person p"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where size(p.phones) >= 2"); - HqlQueryParser.parse("select concat(p.number, ' : ' , cast(c.duration as string)) " + // + HqlQueryParser.parseQuery("select concat(p.number, ' : ' , cast(c.duration as string)) " + // "from Call c " + // "join c.phone p"); - HqlQueryParser.parse("select substring(p.number, 1, 2) " + // + HqlQueryParser.parseQuery("select substring(p.number, 1, 2) " + // "from Call c " + // "join c.phone p"); - HqlQueryParser.parse("select upper(p.name) " + // + HqlQueryParser.parseQuery("select upper(p.name) " + // "from Person p "); - HqlQueryParser.parse("select lower(p.name) " + // + HqlQueryParser.parseQuery("select lower(p.name) " + // "from Person p "); - HqlQueryParser.parse("select trim(p.name) " + // + HqlQueryParser.parseQuery("select trim(p.name) " + // "from Person p "); - HqlQueryParser.parse("select trim(leading ' ' from p.name) " + // + HqlQueryParser.parseQuery("select trim(leading ' ' from p.name) " + // "from Person p "); - HqlQueryParser.parse("select length(p.name) " + // + HqlQueryParser.parseQuery("select length(p.name) " + // "from Person p "); - HqlQueryParser.parse("select locate('John', p.name) " + // + HqlQueryParser.parseQuery("select locate('John', p.name) " + // "from Person p "); - HqlQueryParser.parse("select abs(c.duration) " + // + HqlQueryParser.parseQuery("select abs(c.duration) " + // "from Call c "); - HqlQueryParser.parse("select mod(c.duration, 10) " + // + HqlQueryParser.parseQuery("select mod(c.duration, 10) " + // "from Call c "); - HqlQueryParser.parse("select sqrt(c.duration) " + // + HqlQueryParser.parseQuery("select sqrt(c.duration) " + // "from Call c "); - HqlQueryParser.parse("select cast(c.duration as String) " + // + HqlQueryParser.parseQuery("select cast(c.duration as String) " + // "from Call c "); - HqlQueryParser.parse("select str(c.timestamp) " + // + HqlQueryParser.parseQuery("select str(c.timestamp) " + // "from Call c "); - HqlQueryParser.parse("select str(cast(duration as float) / 60, 4, 2) " + // + HqlQueryParser.parseQuery("select str(cast(duration as float) / 60, 4, 2) " + // "from Call c "); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "where extract(date from c.timestamp) = local date"); - HqlQueryParser.parse("select extract(year from c.timestamp) " + // + HqlQueryParser.parseQuery("select extract(year from c.timestamp) " + // "from Call c "); - HqlQueryParser.parse("select year(c.timestamp) " + // + HqlQueryParser.parseQuery("select year(c.timestamp) " + // "from Call c "); - HqlQueryParser.parse("select var_samp(c.duration) as sampvar, var_pop(c.duration) as popvar " + // + HqlQueryParser.parseQuery("select var_samp(c.duration) as sampvar, var_pop(c.duration) as popvar " + // "from Call c "); - HqlQueryParser.parse("select bit_length(c.phone.number) " + // + HqlQueryParser.parseQuery("select bit_length(c.phone.number) " + // "from Call c "); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "where c.duration < 30 "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.name like 'John%' "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.createdOn > '1950-01-01' "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Phone p " + // "where p.type = 'MOBILE' "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Payment p " + // "where p.completed = true "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Payment p " + // "where type(p) = WireTransferPayment "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Payment p, Phone ph " + // "where p.person = ph.person "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "join p.phones ph " + // "where p.id = 1L and index(ph) between 0 and 3"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.createdOn between '1999-01-01' and '2001-01-02'"); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "where c.duration between 5 and 20"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.name between 'H' and 'M'"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.nickName is not null"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.nickName is null"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.name like 'Jo%'"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.name not like 'Jo%'"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.name like 'Dr|_%' escape '|'"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Payment p " + // "where type(p) in (CreditCardPayment, WireTransferPayment)"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Phone p " + // "where type in ('MOBILE', 'LAND_LINE')"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Phone p " + // "where type in :types"); - HqlQueryParser.parse("select distinct p " + // + HqlQueryParser.parseQuery("select distinct p " + // "from Phone p " + // "where p.person.id in (" + // " select py.person.id " + // " from Payment py" + // " where py.completed = true and py.amount > 50 " + // ")"); - HqlQueryParser.parse("select distinct p " + // + HqlQueryParser.parseQuery("select distinct p " + // "from Phone p " + // "where p.person in (" + // " select py.person " + // " from Payment py" + // " where py.completed = true and py.amount > 50 " + // ")"); - HqlQueryParser.parse("select distinct p " + // + HqlQueryParser.parseQuery("select distinct p " + // "from Payment p " + // "where (p.amount, p.completed) in (" + // " (50, true)," + // " (100, true)," + // " (5, false)" + // ")"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where 1 in indices(p.phones)"); - HqlQueryParser.parse("select distinct p.person " + // + HqlQueryParser.parseQuery("select distinct p.person " + // "from Phone p " + // "join p.calls c " + // "where 50 > all (" + // @@ -1132,96 +1132,96 @@ void hqlQueries() { " from Call" + // " where phone = p " + // ") "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Phone p " + // "where local date > all elements(p.repairTimestamps)"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where :phone = some elements(p.phones)"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where :phone member of p.phones"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where exists elements(p.phones)"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.phones is empty"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.phones is not empty"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.phones is not empty"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where 'Home address' member of p.addresses"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where 'Home address' not member of p.addresses"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from org.hibernate.userguide.model.Person p"); - HqlQueryParser.parse("select distinct pr, ph " + // + HqlQueryParser.parseQuery("select distinct pr, ph " + // "from Person pr, Phone ph " + // "where ph.person = pr and ph is not null"); - HqlQueryParser.parse("select distinct pr1 " + // + HqlQueryParser.parseQuery("select distinct pr1 " + // "from Person pr1, Person pr2 " + // "where pr1.id <> pr2.id " + // " and pr1.address = pr2.address " + // " and pr1.createdOn < pr2.createdOn"); - HqlQueryParser.parse("select distinct pr, ph " + // + HqlQueryParser.parseQuery("select distinct pr, ph " + // "from Person pr cross join Phone ph " + // "where ph.person = pr and ph is not null"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Payment p "); - HqlQueryParser.parse("select d.owner, d.payed " + // + HqlQueryParser.parseQuery("select d.owner, d.payed " + // "from (" + // " select p.person as owner, c.payment is not null as payed " + // " from Call c " + // " join c.phone p " + // " where p.number = :phoneNumber) d"); - HqlQueryParser.parse("select distinct pr " + // + HqlQueryParser.parseQuery("select distinct pr " + // "from Person pr " + // "join Phone ph on ph.person = pr " + // "where ph.type = :phoneType"); - HqlQueryParser.parse("select distinct pr " + // + HqlQueryParser.parseQuery("select distinct pr " + // "from Person pr " + // "join pr.phones ph " + // "where ph.type = :phoneType"); - HqlQueryParser.parse("select distinct pr " + // + HqlQueryParser.parseQuery("select distinct pr " + // "from Person pr " + // "inner join pr.phones ph " + // "where ph.type = :phoneType"); - HqlQueryParser.parse("select distinct pr " + // + HqlQueryParser.parseQuery("select distinct pr " + // "from Person pr " + // "left join pr.phones ph " + // "where ph is null " + // " or ph.type = :phoneType"); - HqlQueryParser.parse("select distinct pr " + // + HqlQueryParser.parseQuery("select distinct pr " + // "from Person pr " + // "left outer join pr.phones ph " + // "where ph is null " + // " or ph.type = :phoneType"); - HqlQueryParser.parse("select pr.name, ph.number " + // + HqlQueryParser.parseQuery("select pr.name, ph.number " + // "from Person pr " + // "left join pr.phones ph with ph.type = :phoneType "); - HqlQueryParser.parse("select pr.name, ph.number " + // + HqlQueryParser.parseQuery("select pr.name, ph.number " + // "from Person pr " + // "left join pr.phones ph on ph.type = :phoneType "); - HqlQueryParser.parse("select distinct pr " + // + HqlQueryParser.parseQuery("select distinct pr " + // "from Person pr " + // "left join fetch pr.phones "); - HqlQueryParser.parse("select a, ccp " + // + HqlQueryParser.parseQuery("select a, ccp " + // "from Account a " + // "join treat(a.payments as CreditCardPayment) ccp " + // "where length(ccp.cardNumber) between 16 and 20"); - HqlQueryParser.parse("select c, ccp " + // + HqlQueryParser.parseQuery("select c, ccp " + // "from Call c " + // "join treat(c.payment as CreditCardPayment) ccp " + // "where length(ccp.cardNumber) between 16 and 20"); - HqlQueryParser.parse("select longest.duration " + // + HqlQueryParser.parseQuery("select longest.duration " + // "from Phone p " + // "left join lateral (" + // " select c.duration as duration " + // @@ -1230,74 +1230,74 @@ void hqlQueries() { " limit 1 " + // " ) longest " + // "where p.number = :phoneNumber"); - HqlQueryParser.parse("select ph " + // + HqlQueryParser.parseQuery("select ph " + // "from Phone ph " + // "where ph.person.address = :address "); - HqlQueryParser.parse("select ph " + // + HqlQueryParser.parseQuery("select ph " + // "from Phone ph " + // "join ph.person pr " + // "where pr.address = :address "); - HqlQueryParser.parse("select ph " + // + HqlQueryParser.parseQuery("select ph " + // "from Phone ph " + // "where ph.person.address = :address " + // " and ph.person.createdOn > :timestamp"); - HqlQueryParser.parse("select ph " + // + HqlQueryParser.parseQuery("select ph " + // "from Phone ph " + // "inner join ph.person pr " + // "where pr.address = :address " + // " and pr.createdOn > :timestamp"); - HqlQueryParser.parse("select ph " + // + HqlQueryParser.parseQuery("select ph " + // "from Person pr " + // "join pr.phones ph " + // "join ph.calls c " + // "where pr.address = :address " + // " and c.duration > :duration"); - HqlQueryParser.parse("select ch " + // + HqlQueryParser.parseQuery("select ch " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - HqlQueryParser.parse("select value(ch) " + // + HqlQueryParser.parseQuery("select value(ch) " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - HqlQueryParser.parse("select key(ch) " + // + HqlQueryParser.parseQuery("select key(ch) " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - HqlQueryParser.parse("select key(ch) " + // + HqlQueryParser.parseQuery("select key(ch) " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - HqlQueryParser.parse("select entry(ch) " + // + HqlQueryParser.parseQuery("select entry(ch) " + // "from Phone ph " + // "join ph.callHistory ch " + // "where ph.id = :id "); - HqlQueryParser.parse("select sum(ch.duration) " + // + HqlQueryParser.parseQuery("select sum(ch.duration) " + // "from Person pr " + // "join pr.phones ph " + // "join ph.callHistory ch " + // "where ph.id = :id " + // " and index(ph) = :phoneIndex"); - HqlQueryParser.parse("select value(ph.callHistory) " + // + HqlQueryParser.parseQuery("select value(ph.callHistory) " + // "from Phone ph " + // "where ph.id = :id "); - HqlQueryParser.parse("select key(ph.callHistory) " + // + HqlQueryParser.parseQuery("select key(ph.callHistory) " + // "from Phone ph " + // "where ph.id = :id "); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.phones[0].type = LAND_LINE"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where p.addresses['HOME'] = :address"); - HqlQueryParser.parse("select pr " + // + HqlQueryParser.parseQuery("select pr " + // "from Person pr " + // "where pr.phones[max(indices(pr.phones))].type = 'LAND_LINE'"); - HqlQueryParser.parse("select p.name, p.nickName " + // + HqlQueryParser.parseQuery("select p.name, p.nickName " + // "from Person p "); - HqlQueryParser.parse("select p.name as name, p.nickName as nickName " + // + HqlQueryParser.parseQuery("select p.name as name, p.nickName as nickName " + // "from Person p "); - HqlQueryParser.parse("select new org.hibernate.userguide.hql.CallStatistics(" + // + HqlQueryParser.parseQuery("select new org.hibernate.userguide.hql.CallStatistics(" + // " count(c), " + // " sum(c.duration), " + // " min(c.duration), " + // @@ -1305,7 +1305,7 @@ void hqlQueries() { " avg(c.duration)" + // ") " + // "from Call c "); - HqlQueryParser.parse("select new map(" + // + HqlQueryParser.parseQuery("select new map(" + // " p.number as phoneNumber , " + // " sum(c.duration) as totalDuration, " + // " avg(c.duration) as averageDuration " + // @@ -1313,86 +1313,86 @@ void hqlQueries() { "from Call c " + // "join c.phone p " + // "group by p.number "); - HqlQueryParser.parse("select new list(" + // + HqlQueryParser.parseQuery("select new list(" + // " p.number, " + // " c.duration " + // ") " + // "from Call c " + // "join c.phone p "); - HqlQueryParser.parse("select distinct p.lastName " + // + HqlQueryParser.parseQuery("select distinct p.lastName " + // "from Person p"); - HqlQueryParser.parse("select " + // + HqlQueryParser.parseQuery("select " + // " count(c), " + // " sum(c.duration), " + // " min(c.duration), " + // " max(c.duration), " + // " avg(c.duration) " + // "from Call c "); - HqlQueryParser.parse("select count(distinct c.phone) " + // + HqlQueryParser.parseQuery("select count(distinct c.phone) " + // "from Call c "); - HqlQueryParser.parse("select p.number, count(c) " + // + HqlQueryParser.parseQuery("select p.number, count(c) " + // "from Call c " + // "join c.phone p " + // "group by p.number"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Phone p " + // "where max(elements(p.calls)) = :call"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Phone p " + // "where min(elements(p.calls)) = :call"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "where max(indices(p.phones)) = 0"); - HqlQueryParser.parse("select count(c) filter (where c.duration < 30) " + // + HqlQueryParser.parseQuery("select count(c) filter (where c.duration < 30) " + // "from Call c "); - HqlQueryParser.parse("select p.number, count(c) filter (where c.duration < 30) " + // + HqlQueryParser.parseQuery("select p.number, count(c) filter (where c.duration < 30) " + // "from Call c " + // "join c.phone p " + // "group by p.number"); - HqlQueryParser.parse("select listagg(p.number, ', ') within group (order by p.type,p.number) " + // + HqlQueryParser.parseQuery("select listagg(p.number, ', ') within group (order by p.type,p.number) " + // "from Phone p " + // "group by p.person"); - HqlQueryParser.parse("select sum(c.duration) " + // + HqlQueryParser.parseQuery("select sum(c.duration) " + // "from Call c "); - HqlQueryParser.parse("select p.name, sum(c.duration) " + // + HqlQueryParser.parseQuery("select p.name, sum(c.duration) " + // "from Call c " + // "join c.phone ph " + // "join ph.person p " + // "group by p.name"); - HqlQueryParser.parse("select p, sum(c.duration) " + // + HqlQueryParser.parseQuery("select p, sum(c.duration) " + // "from Call c " + // "join c.phone ph " + // "join ph.person p " + // "group by p"); - HqlQueryParser.parse("select p.name, sum(c.duration) " + // + HqlQueryParser.parseQuery("select p.name, sum(c.duration) " + // "from Call c " + // "join c.phone ph " + // "join ph.person p " + // "group by p.name " + // "having sum(c.duration) > 1000"); - HqlQueryParser.parse("select p.name from Person p " + // + HqlQueryParser.parseQuery("select p.name from Person p " + // "union " + // "select p.nickName from Person p where p.nickName is not null"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Person p " + // "order by p.name"); - HqlQueryParser.parse("select p.name, sum(c.duration) as total " + // + HqlQueryParser.parseQuery("select p.name, sum(c.duration) as total " + // "from Call c " + // "join c.phone ph " + // "join ph.person p " + // "group by p.name " + // "order by total"); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "join c.phone p " + // "order by p.number " + // "limit 50"); - HqlQueryParser.parse("select c " + // + HqlQueryParser.parseQuery("select c " + // "from Call c " + // "join c.phone p " + // "order by p.number " + // "fetch first 50 rows only"); - HqlQueryParser.parse("select p " + // + HqlQueryParser.parseQuery("select p " + // "from Phone p " + // "join fetch p.calls " + // "order by p " + // diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java index 0d65402c33..c16fcd9ce0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java @@ -22,7 +22,7 @@ import org.junit.jupiter.params.provider.MethodSource; /** - * TCK Tests for {@link JpqlQueryParser} mixed into {@link JpaQueryParsingEnhancer}. + * TCK Tests for {@link JpqlQueryParser} mixed into {@link JpaQueryEnhancer}. * * @author Greg Turnquist * @since 3.1 @@ -33,7 +33,7 @@ public class JpqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { @Override QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { - return new JpaQueryParsingEnhancer(new JpqlQueryParser(declaredQuery)); + return JpaQueryEnhancer.forJpql(declaredQuery); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index fecc3470ca..e0124b6907 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -46,7 +46,7 @@ private static String parseWithoutChanges(String query) { JpqlLexer lexer = new JpqlLexer(CharStreams.fromString(query)); JpqlParser parser = new JpqlParser(new CommonTokenStream(lexer)); - parser.addErrorListener(new JpaQueryParsingSyntaxErrorListener()); + parser.addErrorListener(new BadJpqlGrammarErrorListener(query)); JpqlParser.StartContext parsedQuery = parser.start(); @@ -762,7 +762,7 @@ void theRest23() { @Test void theRest24() { - assertThatExceptionOfType(JpaQueryParsingSyntaxError.class).isThrownBy(() -> { + assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> { assertQuery(""" SELECT p.product_name FROM Order o, IN(o.lineItems) l JOIN o.customer c diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java index c09469742a..557df55156 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java @@ -27,7 +27,7 @@ import org.springframework.lang.Nullable; /** - * Verify that JPQL queries are properly transformed through the {@link JpaQueryParsingEnhancer} and the + * Verify that JPQL queries are properly transformed through the {@link JpaQueryEnhancer} and the * {@link JpqlQueryParser}. * * @author Greg Turnquist @@ -117,7 +117,7 @@ void multipleAliasesShouldBeGathered() { var original = "select e from Employee e join e.manager m"; // when - var results = createQueryFor(original, null); + var results = createQueryFor(original, Sort.unsorted()); // then assertThat(results).isEqualTo("select e from Employee e join e.manager m"); @@ -197,12 +197,12 @@ void applySortingAccountsForNewlinesInSubselect() { Sort sort = Sort.by(Sort.Order.desc("age")); - assertThat(new JpaQueryParsingEnhancer(new JpqlQueryParser("select u\n" + // + assertThat(newParser("select u\n" + // "from user u\n" + // "where exists (select u2\n" + // "from user u2\n" + // ")\n" + // - "")).applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + // + "").applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + // "from user u\n" + // "where exists (select u2\n" + // "from user u2\n" + // @@ -679,7 +679,7 @@ private void assertCountQuery(String originalQuery, String countQuery) { } private String createQueryFor(String query, Sort sort) { - return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).applySorting(sort); + return newParser(query).applySorting(sort); } private String createCountQueryFor(String query) { @@ -687,18 +687,22 @@ private String createCountQueryFor(String query) { } private String createCountQueryFor(String original, @Nullable String countProjection) { - return new JpaQueryParsingEnhancer(new JpqlQueryParser(original)).createCountQueryFor(countProjection); + return newParser(original).createCountQueryFor(countProjection); } private String alias(String query) { - return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).detectAlias(); + return newParser(query).detectAlias(); } private boolean hasConstructorExpression(String query) { - return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).hasConstructorExpression(); + return newParser(query).hasConstructorExpression(); } private String projection(String query) { - return new JpaQueryParsingEnhancer(new JpqlQueryParser(query)).getProjection(); + return newParser(query).getProjection(); + } + + private QueryEnhancer newParser(String query) { + return JpaQueryEnhancer.forJpql(DeclaredQuery.of(query, false)); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java index 1451e72ced..df12231d43 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlSpecificationTests.java @@ -39,7 +39,7 @@ class JpqlSpecificationTests { @Test void joinExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order AS o JOIN o.lineItems AS l WHERE l.shipped = FALSE @@ -53,7 +53,7 @@ void joinExample1() { @Test void joinExample2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l JOIN l.product p WHERE p.productType = 'office_supplies' @@ -66,7 +66,7 @@ void joinExample2() { @Test void rangeVariableDeclarations() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o1 FROM Order o1, Order o2 WHERE o1.quantity > o2.quantity AND @@ -81,7 +81,7 @@ void rangeVariableDeclarations() { @Test void pathExpressionsExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT i.name, VALUE(p) FROM Item i JOIN i.photos p WHERE KEY(p) LIKE '%egret' @@ -94,7 +94,7 @@ WHERE KEY(p) LIKE '%egret' @Test void pathExpressionsExample2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT i.name, p FROM Item i JOIN i.photos p WHERE KEY(p) LIKE '%egret' @@ -107,7 +107,7 @@ WHERE KEY(p) LIKE '%egret' @Test void pathExpressionsExample3() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT p.vendor FROM Employee e JOIN e.contactInfo.phones p """); @@ -119,7 +119,7 @@ void pathExpressionsExample3() { @Test void pathExpressionsExample4() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT p.vendor FROM Employee e JOIN e.contactInfo c JOIN c.phones p WHERE e.contactInfo.address.zipcode = '95054' @@ -129,7 +129,7 @@ void pathExpressionsExample4() { @Test void pathExpressionSyntaxExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT l.product FROM Order AS o JOIN o.lineItems l """); @@ -138,7 +138,7 @@ void pathExpressionSyntaxExample1() { @Test void joinsExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize """); } @@ -146,7 +146,7 @@ void joinsExample1() { @Test void joinsExample2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 """); } @@ -154,7 +154,7 @@ void joinsExample2() { @Test void joinsInnerExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 """); } @@ -162,7 +162,7 @@ void joinsInnerExample() { @Test void joinsInExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 """); } @@ -170,7 +170,7 @@ SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 @Test void doubleJoinExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT p.vendor FROM Employee e JOIN e.contactInfo c JOIN c.phones p WHERE c.address.zipcode = '95054' @@ -180,7 +180,7 @@ void doubleJoinExample() { @Test void leftJoinExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT s.name, COUNT(p) FROM Suppliers s LEFT JOIN s.products p GROUP BY s.name @@ -190,7 +190,7 @@ SELECT s.name, COUNT(p) @Test void leftJoinOnExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT s.name, COUNT(p) FROM Suppliers s LEFT JOIN s.products p ON p.status = 'inStock' @@ -201,7 +201,7 @@ SELECT s.name, COUNT(p) @Test void leftJoinWhereExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT s.name, COUNT(p) FROM Suppliers s LEFT JOIN s.products p WHERE p.status = 'inStock' @@ -212,7 +212,7 @@ SELECT s.name, COUNT(p) @Test void leftJoinFetchExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT d FROM Department d LEFT JOIN FETCH d.employees WHERE d.deptno = 1 @@ -222,7 +222,7 @@ void leftJoinFetchExample() { @Test void collectionMemberExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l WHERE l.product.productType = 'office_supplies' @@ -232,7 +232,7 @@ void collectionMemberExample() { @Test void collectionMemberInExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o, IN(o.lineItems) l WHERE l.product.productType = 'office_supplies' @@ -242,7 +242,7 @@ FROM Order o, IN(o.lineItems) l @Test void fromClauseExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Order AS o JOIN o.lineItems l JOIN l.product p """); @@ -251,7 +251,7 @@ void fromClauseExample() { @Test void fromClauseDowncastingExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT b.name, b.ISBN FROM Order o JOIN TREAT(o.product AS Book) b """); @@ -260,7 +260,7 @@ FROM Order o JOIN TREAT(o.product AS Book) b @Test void fromClauseDowncastingExample2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp WHERE lp.budget > 1000 """); @@ -273,7 +273,7 @@ SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") void fromClauseDowncastingExample3_SPEC_BUG() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e FROM Employee e JOIN e.projects p WHERE TREAT(p AS LargeProject).budget > 1000 OR TREAT(p AS SmallProject).name LIKE 'Persist%' @@ -284,7 +284,7 @@ OR TREAT(p AS SmallProject).name LIKE 'Persist%' @Test void fromClauseDowncastingExample3fixed() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e FROM Employee e JOIN e.projects p WHERE TREAT(p AS LargeProject).budget > 1000 OR TREAT(p AS SmallProject).name LIKE 'Persist%' @@ -295,7 +295,7 @@ OR TREAT(p AS SmallProject).name LIKE 'Persist%' @Test void fromClauseDowncastingExample4() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e FROM Employee e WHERE TREAT(e AS Exempt).vacationDays > 10 OR TREAT(e AS Contractor).hours > 100 @@ -305,7 +305,7 @@ OR TREAT(e AS Contractor).hours > 100 @Test void pathExpressionsNamedParametersExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c FROM Customer c WHERE c.status = :stat @@ -315,7 +315,7 @@ void pathExpressionsNamedParametersExample() { @Test void betweenExpressionsExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT t FROM CreditCard c JOIN c.transactionHistory t WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 @@ -325,7 +325,7 @@ void betweenExpressionsExample() { @Test void isEmptyExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.lineItems IS EMPTY @@ -335,7 +335,7 @@ void isEmptyExample() { @Test void memberOfExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT p FROM Person p WHERE 'Joe' MEMBER OF p.nicknames @@ -345,7 +345,7 @@ void memberOfExample() { @Test void existsSubSelectExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT emp FROM Employee emp WHERE EXISTS ( @@ -358,7 +358,7 @@ WHERE EXISTS ( @Test void allExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT emp FROM Employee emp WHERE emp.salary > ALL ( @@ -371,7 +371,7 @@ WHERE emp.salary > ALL ( @Test void existsSubSelectExample2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT emp FROM Employee emp WHERE EXISTS ( @@ -384,7 +384,7 @@ WHERE EXISTS ( @Test void subselectNumericComparisonExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c FROM Customer c WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 @@ -394,7 +394,7 @@ void subselectNumericComparisonExample1() { @Test void subselectNumericComparisonExample2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT goodCustomer FROM Customer goodCustomer WHERE goodCustomer.balanceOwed < ( @@ -405,7 +405,7 @@ SELECT AVG(c.balanceOwed)/2.0 FROM Customer c) @Test void indexExample() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT w.name FROM Course c JOIN c.studentWaitlist w WHERE c.name = 'Calculus' @@ -420,7 +420,7 @@ AND INDEX(w) = 0 @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") void functionInvocationExample_SPEC_BUG() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c FROM Customer c WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) @@ -430,7 +430,7 @@ WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) @Test void functionInvocationExampleWithCorrection() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c FROM Customer c WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE @@ -440,7 +440,7 @@ WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE @Test void updateCaseExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" UPDATE Employee e SET e.salary = CASE WHEN e.rating = 1 THEN e.salary * 1.1 @@ -453,7 +453,7 @@ void updateCaseExample1() { @Test void updateCaseExample2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" UPDATE Employee e SET e.salary = CASE e.rating WHEN 1 THEN e.salary * 1.1 @@ -466,7 +466,7 @@ void updateCaseExample2() { @Test void selectCaseExample1() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e.name, CASE TYPE(e) WHEN Exempt THEN 'Exempt' WHEN Contractor THEN 'Contractor' @@ -481,7 +481,7 @@ CASE TYPE(e) WHEN Exempt THEN 'Exempt' @Test void selectCaseExample2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e.name, f.name, CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' @@ -496,7 +496,7 @@ void selectCaseExample2() { @Test void theRest() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e FROM Employee e WHERE TYPE(e) IN (Exempt, Contractor) @@ -506,7 +506,7 @@ WHERE TYPE(e) IN (Exempt, Contractor) @Test void theRest2() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e FROM Employee e WHERE TYPE(e) IN (:empType1, :empType2) @@ -516,7 +516,7 @@ WHERE TYPE(e) IN (:empType1, :empType2) @Test void theRest3() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e FROM Employee e WHERE TYPE(e) IN :empTypes @@ -526,7 +526,7 @@ WHERE TYPE(e) IN :empTypes @Test void theRest4() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT TYPE(e) FROM Employee e WHERE TYPE(e) <> Exempt @@ -536,7 +536,7 @@ WHERE TYPE(e) <> Exempt @Test void theRest5() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c.status, AVG(c.filledOrderCount), COUNT(c) FROM Customer c GROUP BY c.status @@ -547,7 +547,7 @@ HAVING c.status IN (1, 2) @Test void theRest6() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c.country, COUNT(c) FROM Customer c GROUP BY c.country @@ -558,7 +558,7 @@ HAVING COUNT(c) > 30 @Test void theRest7() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c, COUNT(o) FROM Customer c JOIN c.orders o GROUP BY c @@ -569,7 +569,7 @@ HAVING COUNT(o) >= 5 @Test void theRest8() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c.id, c.status FROM Customer c JOIN c.orders o WHERE o.count > 100 @@ -579,7 +579,7 @@ void theRest8() { @Test void theRest9() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT v.location.street, KEY(i).title, VALUE(i) FROM VideoStore v JOIN v.videoInventory i WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 @@ -589,7 +589,7 @@ SELECT v.location.street, KEY(i).title, VALUE(i) @Test void theRest10() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o.lineItems FROM Order AS o """); } @@ -597,7 +597,7 @@ void theRest10() { @Test void theRest11() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT c, COUNT(l) AS itemCount FROM Customer c JOIN c.Orders o JOIN o.lineItems l WHERE c.address.state = 'CA' @@ -609,7 +609,7 @@ SELECT c, COUNT(l) AS itemCount @Test void theRest12() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) FROM Customer c JOIN c.orders o WHERE o.count > 100 @@ -619,7 +619,7 @@ void theRest12() { @Test void theRest13() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT e.address AS addr FROM Employee e """); @@ -628,7 +628,7 @@ void theRest13() { @Test void theRest14() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT AVG(o.quantity) FROM Order o """); } @@ -636,7 +636,7 @@ SELECT AVG(o.quantity) FROM Order o @Test void theRest15() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT SUM(l.price) FROM Order o JOIN o.lineItems l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' @@ -646,7 +646,7 @@ SELECT SUM(l.price) @Test void theRest16() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT COUNT(o) FROM Order o """); } @@ -654,7 +654,7 @@ SELECT COUNT(o) FROM Order o @Test void theRest17() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT COUNT(l.price) FROM Order o JOIN o.lineItems l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' @@ -664,7 +664,7 @@ SELECT COUNT(l.price) @Test void theRest18() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT COUNT(l) FROM Order o JOIN o.lineItems l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL @@ -674,7 +674,7 @@ SELECT COUNT(l) @Test void theRest19() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Customer c JOIN c.orders o JOIN c.address a WHERE a.state = 'CA' @@ -685,7 +685,7 @@ void theRest19() { @Test void theRest20() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o.quantity, a.zipcode FROM Customer c JOIN c.orders o JOIN c.address a WHERE a.state = 'CA' @@ -696,7 +696,7 @@ void theRest20() { @Test void theRest21() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode FROM Customer c JOIN c.orders o JOIN c.address a WHERE a.state = 'CA' AND a.county = 'Santa Clara' @@ -707,7 +707,7 @@ void theRest21() { @Test void theRest22() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT AVG(o.quantity) as q, a.zipcode FROM Customer c JOIN c.orders o JOIN c.address a WHERE a.state = 'CA' @@ -719,7 +719,7 @@ SELECT AVG(o.quantity) as q, a.zipcode @Test void theRest23() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT p.product_name FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' @@ -733,8 +733,8 @@ void theRest23() { @Test void theRest24() { - assertThatExceptionOfType(JpaQueryParsingSyntaxError.class).isThrownBy(() -> { - JpqlQueryParser.parse(""" + assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> { + JpqlQueryParser.parseQuery(""" SELECT p.product_name FROM Order o, IN(o.lineItems) l JOIN o.customer c WHERE c.lastname = 'Smith' AND c.firstname = 'John' @@ -746,7 +746,7 @@ FROM Order o, IN(o.lineItems) l JOIN o.customer c @Test void theRest25() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" DELETE FROM Customer c WHERE c.status = 'inactive' @@ -756,7 +756,7 @@ void theRest25() { @Test void theRest26() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" DELETE FROM Customer c WHERE c.status = 'inactive' @@ -767,7 +767,7 @@ void theRest26() { @Test void theRest27() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" UPDATE Customer c SET c.status = 'outstanding' WHERE c.balance < 10000 @@ -777,7 +777,7 @@ void theRest27() { @Test void theRest28() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" UPDATE Employee e SET e.address.building = 22 WHERE e.address.building = 14 @@ -789,7 +789,7 @@ void theRest28() { @Test void theRest29() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Order o """); @@ -798,7 +798,7 @@ void theRest29() { @Test void theRest30() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.shippingAddress.state = 'CA' @@ -808,7 +808,7 @@ void theRest30() { @Test void theRest31() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o.shippingAddress.state FROM Order o """); @@ -817,7 +817,7 @@ void theRest31() { @Test void theRest32() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l """); @@ -826,7 +826,7 @@ void theRest32() { @Test void theRest33() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.lineItems IS NOT EMPTY @@ -836,7 +836,7 @@ void theRest33() { @Test void theRest34() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.lineItems IS EMPTY @@ -846,7 +846,7 @@ void theRest34() { @Test void theRest35() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l WHERE l.shipped = FALSE @@ -856,7 +856,7 @@ void theRest35() { @Test void theRest36() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE @@ -869,7 +869,7 @@ void theRest36() { @Test void theRest37() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT o FROM Order o WHERE o.shippingAddress <> o.billingAddress @@ -879,7 +879,7 @@ void theRest37() { @Test void theRest38() { - JpqlQueryParser.parse(""" + JpqlQueryParser.parseQuery(""" SELECT DISTINCT o FROM Order o JOIN o.lineItems l WHERE l.product.name = ?1 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java index a6ec5fd3c9..ef90549fd4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java @@ -35,9 +35,9 @@ void createsParsingImplementationForNonNativeQuery() { QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(query); assertThat(queryEnhancer) // - .isInstanceOf(JpaQueryParsingEnhancer.class); + .isInstanceOf(JpaQueryEnhancer.class); - JpaQueryParsingEnhancer queryParsingEnhancer = (JpaQueryParsingEnhancer) queryEnhancer; + JpaQueryEnhancer queryParsingEnhancer = (JpaQueryEnhancer) queryEnhancer; assertThat(queryParsingEnhancer.getQueryParsingStrategy()).isInstanceOf(HqlQueryParser.class); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index f8c84c536c..78427ece9a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -21,7 +21,6 @@ import java.util.List; import org.assertj.core.api.Assertions; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.springframework.data.jpa.repository.query.StringQuery.InParameterBinding; import org.springframework.data.jpa.repository.query.StringQuery.LikeParameterBinding; @@ -40,8 +39,6 @@ */ class StringQueryUnitTests { - private SoftAssertions softly = new SoftAssertions(); - @Test // DATAJPA-341 void doesNotConsiderPlainLikeABinding() { @@ -115,7 +112,6 @@ void detectsNamedInParameterBindings() { assertNamedBinding(InParameterBinding.class, "ids", bindings.get(0)); - softly.assertAll(); } @Test // DATAJPA-461 @@ -133,8 +129,6 @@ void detectsMultipleNamedInParameterBindings() { assertNamedBinding(InParameterBinding.class, "ids", bindings.get(0)); assertNamedBinding(InParameterBinding.class, "names", bindings.get(1)); assertNamedBinding(ParameterBinding.class, "bar", bindings.get(2)); - - softly.assertAll(); } @Test // DATAJPA-461 @@ -151,7 +145,6 @@ void detectsPositionalInParameterBindings() { assertPositionalBinding(InParameterBinding.class, 1, bindings.get(0)); - softly.assertAll(); } @Test // DATAJPA-461 @@ -170,7 +163,6 @@ void detectsMultiplePositionalInParameterBindings() { assertPositionalBinding(InParameterBinding.class, 2, bindings.get(1)); assertPositionalBinding(ParameterBinding.class, 3, bindings.get(2)); - softly.assertAll(); } @Test // DATAJPA-373 @@ -193,7 +185,6 @@ void treatsGreaterThanBindingAsSimpleBinding() { assertThat(bindings).hasSize(1); assertPositionalBinding(ParameterBinding.class, 1, bindings.get(0)); - softly.assertAll(); } @Test // DATAJPA-473 @@ -208,11 +199,8 @@ void removesLikeBindingsFromQueryIfQueryContainsSimpleBinding() { assertNamedBinding(LikeParameterBinding.class, "escapedWord", bindings.get(0)); assertNamedBinding(ParameterBinding.class, "word", bindings.get(1)); - softly.assertThat(query.getQueryString()) - .isEqualTo("SELECT a FROM Article a WHERE a.overview LIKE :escapedWord ESCAPE '~'" - + " OR a.content LIKE :escapedWord ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC"); - - softly.assertAll(); + assertThat(query.getQueryString()).isEqualTo("SELECT a FROM Article a WHERE a.overview LIKE :escapedWord ESCAPE '~'" + + " OR a.content LIKE :escapedWord ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC"); } @Test // DATAJPA-483 @@ -224,8 +212,6 @@ void detectsInBindingWithParentheses() { assertThat(bindings).hasSize(1); assertNamedBinding(InParameterBinding.class, "statuses", bindings.get(0)); - - softly.assertAll(); } @Test // DATAJPA-545 @@ -238,7 +224,6 @@ void detectsInBindingWithSpecialFrenchCharactersInParentheses() { assertThat(bindings).hasSize(1); assertNamedBinding(InParameterBinding.class, "abonnés", bindings.get(0)); - softly.assertAll(); } @Test // DATAJPA-545 @@ -250,8 +235,6 @@ void detectsInBindingWithSpecialCharactersInParentheses() { assertThat(bindings).hasSize(1); assertNamedBinding(InParameterBinding.class, "øre", bindings.get(0)); - - softly.assertAll(); } @Test // DATAJPA-545 @@ -263,8 +246,6 @@ void detectsInBindingWithSpecialAsianCharactersInParentheses() { assertThat(bindings).hasSize(1); assertNamedBinding(InParameterBinding.class, "생일", bindings.get(0)); - - softly.assertAll(); } @Test // DATAJPA-545 @@ -276,8 +257,6 @@ void detectsInBindingWithSpecialCharactersAndWordCharactersMixedInParentheses() assertThat(bindings).hasSize(1); assertNamedBinding(InParameterBinding.class, "ab1babc생일233", bindings.get(0)); - - softly.assertAll(); } @Test // DATAJPA-362 @@ -301,27 +280,20 @@ void shouldReplaceAllPositionExpressionParametersWithInClause() { StringQuery query = new StringQuery("select a from A a where a.b in ?#{#bs} and a.c in ?#{#cs}", true); String queryString = query.getQueryString(); - softly.assertThat(queryString).isEqualTo("select a from A a where a.b in ?1 and a.c in ?2"); - softly.assertThat(query.getParameterBindings().get(0).getExpression()).isEqualTo("#bs"); - softly.assertThat(query.getParameterBindings().get(1).getExpression()).isEqualTo("#cs"); - - softly.assertAll(); + assertThat(queryString).isEqualTo("select a from A a where a.b in ?1 and a.c in ?2"); + assertThat(query.getParameterBindings().get(0).getExpression()).isEqualTo("#bs"); + assertThat(query.getParameterBindings().get(1).getExpression()).isEqualTo("#cs"); } @Test // DATAJPA-864 void detectsConstructorExpressions() { - softly - .assertThat( - new StringQuery("select new com.example.Dto(a.foo, a.bar) from A a", false).hasConstructorExpression()) - .isTrue(); - softly - .assertThat( - new StringQuery("select new com.example.Dto (a.foo, a.bar) from A a", false).hasConstructorExpression()) + assertThat( + new StringQuery("select new com.example.Dto(a.foo, a.bar) from A a", false).hasConstructorExpression()) + .isTrue(); + assertThat(new StringQuery("select new com.example.Dto (a.foo, a.bar) from A a", false).hasConstructorExpression()) .isTrue(); - softly.assertThat(new StringQuery("select a from A a", true).hasConstructorExpression()).isFalse(); - - softly.assertAll(); + assertThat(new StringQuery("select a from A a", true).hasConstructorExpression()).isFalse(); } /** @@ -332,10 +304,8 @@ void detectsConstructorExpressions() { void detectsConstructorExpressionForDefaultConstructor() { // Parentheses required - softly.assertThat(new StringQuery("select new com.example.Dto(a.name) from A a", false).hasConstructorExpression()) + assertThat(new StringQuery("select new com.example.Dto(a.name) from A a", false).hasConstructorExpression()) .isTrue(); - - softly.assertAll(); } @Test // DATAJPA-1179 @@ -344,15 +314,13 @@ void bindingsMatchQueryForIdenticalSpelExpressions() { StringQuery query = new StringQuery("select a from A a where a.first = :#{#exp} or a.second = :#{#exp}", true); List bindings = query.getParameterBindings(); - softly.assertThat(bindings).isNotEmpty(); + assertThat(bindings).isNotEmpty(); for (ParameterBinding binding : bindings) { - softly.assertThat(binding.getName()).isNotNull(); - softly.assertThat(query.getQueryString()).contains(binding.getName()); - softly.assertThat(binding.getExpression()).isEqualTo("#exp"); + assertThat(binding.getName()).isNotNull(); + assertThat(query.getQueryString()).contains(binding.getName()); + assertThat(binding.getExpression()).isEqualTo("#exp"); } - - softly.assertAll(); } @Test // DATAJPA-1235 @@ -364,13 +332,11 @@ void getProjection() { checkProjection("sect x, y, z from Entity something", "", "missing select", false); checkProjection("select x, y, z fron Entity something", "", "missing from", false); - - softly.assertAll(); } void checkProjection(String query, String expected, String description, boolean nativeQuery) { - softly.assertThat(new StringQuery(query, nativeQuery).getProjection()) // + assertThat(new StringQuery(query, nativeQuery).getProjection()) // .as("%s (%s)", description, query) // .isEqualTo(expected); } @@ -390,13 +356,11 @@ void getAlias() { checkAlias("from User as bs", "bs", "ignored as", false); checkAlias("from User as AS", "AS", "ignored as using the second", false); checkAlias("from User asas", "asas", "asas is weird but legal", false); - - softly.assertAll(); } private void checkAlias(String query, String expected, String description, boolean nativeQuery) { - softly.assertThat(new StringQuery(query, nativeQuery).getAlias()) // + assertThat(new StringQuery(query, nativeQuery).getAlias()) // .as("%s (%s)", description, query) // .isEqualTo(expected); } @@ -430,8 +394,6 @@ void testHasNamedParameter() { checkHasNamedParameter("::id", false, "double colon with identifier", false); checkHasNamedParameter("\\:id", false, "escaped colon with identifier", false); checkHasNamedParameter("select something from x where id = #something", false, "hash", true); - - softly.assertAll(); } @Test // DATAJPA-1235 @@ -445,8 +407,6 @@ void ignoresQuotedNamedParameterLookAlike() { // checkNumberOfNamedParameters("select something from blah where x = \"'0\":name", 1, "single quote in double // quotes", // false); - - softly.assertAll(); } @Test // DATAJPA-1307 @@ -455,11 +415,9 @@ void detectsMultiplePositionalParameterBindingsWithoutIndex() { String queryString = "select u from User u where u.id in ? and u.names in ? and foo = ?"; StringQuery query = new StringQuery(queryString, false); - softly.assertThat(query.getQueryString()).isEqualTo(queryString); - softly.assertThat(query.hasParameterBindings()).isTrue(); - softly.assertThat(query.getParameterBindings()).hasSize(3); - - softly.assertAll(); + assertThat(query.getQueryString()).isEqualTo(queryString); + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getParameterBindings()).hasSize(3); } @Test // DATAJPA-1307 @@ -482,7 +440,7 @@ void failOnMixedBindingsWithoutIndex() { @Test // DATAJPA-1307 void makesUsageOfJdbcStyleParameterAvailable() { - softly.assertThat(new StringQuery("from Something something where something = ?", false).usesJdbcStyleParameters()) + assertThat(new StringQuery("from Something something where something = ?", false).usesJdbcStyleParameters()) .isTrue(); List testQueries = Arrays.asList( // @@ -493,13 +451,11 @@ void makesUsageOfJdbcStyleParameterAvailable() { for (String testQuery : testQueries) { - softly.assertThat(new StringQuery(testQuery, false) // + assertThat(new StringQuery(testQuery, false) // .usesJdbcStyleParameters()) // - .describedAs(testQuery) // - .isFalse(); + .describedAs(testQuery) // + .isFalse(); } - - softly.assertAll(); } @Test // DATAJPA-1307 @@ -508,11 +464,10 @@ void questionMarkInStringLiteral() { String queryString = "select '? ' from dual"; StringQuery query = new StringQuery(queryString, true); - softly.assertThat(query.getQueryString()).isEqualTo(queryString); - softly.assertThat(query.hasParameterBindings()).isFalse(); - softly.assertThat(query.getParameterBindings()).isEmpty(); + assertThat(query.getQueryString()).isEqualTo(queryString); + assertThat(query.hasParameterBindings()).isFalse(); + assertThat(query.getParameterBindings()).isEmpty(); - softly.assertAll(); } @Test // DATAJPA-1318 @@ -527,7 +482,7 @@ void isNotDefaultProjection() { "select a, b from C"); for (String queryString : queriesWithoutDefaultProjection) { - softly.assertThat(new StringQuery(queryString, true).isDefaultProjection()) // + assertThat(new StringQuery(queryString, true).isDefaultProjection()) // .describedAs(queryString) // .isFalse(); } @@ -544,12 +499,10 @@ void isNotDefaultProjection() { ); for (String queryString : queriesWithDefaultProjection) { - softly.assertThat(new StringQuery(queryString, true).isDefaultProjection()) // + assertThat(new StringQuery(queryString, true).isDefaultProjection()) // .describedAs(queryString) // .isTrue(); } - - softly.assertAll(); } @Test // DATAJPA-1652 @@ -578,17 +531,17 @@ void checkNumberOfNamedParameters(String query, int expectedSize, String label, DeclaredQuery declaredQuery = DeclaredQuery.of(query, nativeQuery); - softly.assertThat(declaredQuery.hasNamedParameter()) // + assertThat(declaredQuery.hasNamedParameter()) // .describedAs("hasNamed Parameter " + label) // .isEqualTo(expectedSize > 0); - softly.assertThat(declaredQuery.getParameterBindings()) // + assertThat(declaredQuery.getParameterBindings()) // .describedAs("parameterBindings " + label) // .hasSize(expectedSize); } private void checkHasNamedParameter(String query, boolean expected, String label, boolean nativeQuery) { - softly.assertThat(new StringQuery(query, nativeQuery).hasNamedParameter()) // + assertThat(new StringQuery(query, nativeQuery).hasNamedParameter()) // .describedAs(String.format("<%s> (%s)", query, label)) // .isEqualTo(expected); } @@ -596,16 +549,16 @@ private void checkHasNamedParameter(String query, boolean expected, String label private void assertPositionalBinding(Class bindingType, Integer position, ParameterBinding expectedBinding) { - softly.assertThat(bindingType.isInstance(expectedBinding)).isTrue(); - softly.assertThat(expectedBinding).isNotNull(); - softly.assertThat(expectedBinding.hasPosition(position)).isTrue(); + assertThat(bindingType.isInstance(expectedBinding)).isTrue(); + assertThat(expectedBinding).isNotNull(); + assertThat(expectedBinding.hasPosition(position)).isTrue(); } private void assertNamedBinding(Class bindingType, String parameterName, ParameterBinding expectedBinding) { - softly.assertThat(bindingType.isInstance(expectedBinding)).isTrue(); - softly.assertThat(expectedBinding).isNotNull(); - softly.assertThat(expectedBinding.hasName(parameterName)).isTrue(); + assertThat(bindingType.isInstance(expectedBinding)).isTrue(); + assertThat(expectedBinding).isNotNull(); + assertThat(expectedBinding.hasName(parameterName)).isTrue(); } } From 0b5ebdb79772c072169c5086c8a7e09f9b12be17 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 14 Mar 2023 17:33:26 -0500 Subject: [PATCH 323/821] Improve performance of PersistenceProvider.condense. No need to check every time the function is called. Closes #2860. Original pull request #2861 --- .../data/jpa/provider/PersistenceProvider.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 6468bcaf06..7b0541d1c2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -15,8 +15,7 @@ */ package org.springframework.data.jpa.provider; -import static org.springframework.data.jpa.provider.JpaClassUtils.isEntityManagerOfType; -import static org.springframework.data.jpa.provider.JpaClassUtils.isMetamodelOfType; +import static org.springframework.data.jpa.provider.JpaClassUtils.*; import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*; import jakarta.persistence.EntityManager; @@ -25,7 +24,11 @@ import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; import org.eclipse.persistence.config.QueryHints; import org.eclipse.persistence.jpa.JpaQuery; @@ -187,6 +190,8 @@ public String getCommentHintKey() { } }; + private static final boolean hibernatePresent = ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", + PersistenceProvider.class.getClassLoader()); private static final Collection ALL = List.of(HIBERNATE, ECLIPSELINK, GENERIC_JPA); static ConcurrentReferenceHashMap, PersistenceProvider> CACHE = new ConcurrentReferenceHashMap<>(); @@ -319,13 +324,12 @@ public boolean canExtractQuery() { */ public static Object condense(Object value) { - ClassLoader classLoader = PersistenceProvider.class.getClassLoader(); - - if (ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", classLoader)) { + if (hibernatePresent) { try { - Class typeParameterValue = ClassUtils.forName("org.hibernate.query.TypedParameterValue", classLoader); + Class typeParameterValue = ClassUtils.forName("org.hibernate.query.TypedParameterValue", + PersistenceProvider.class.getClassLoader()); if (typeParameterValue.isInstance(value)) { return null; From ff1f8bf540f354d1b887d0f34f475f5f325532eb Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 15 Mar 2023 12:08:14 +0100 Subject: [PATCH 324/821] Polishing. TypedParameterValue class only gets loaded once. Renamed the method to better capture its behaviour. See #2860 See #2861 --- .../jpa/provider/PersistenceProvider.java | 39 +++++++++---------- .../query/ParameterMetadataProvider.java | 14 +++---- .../jpa/repository/query/StringQuery.java | 12 +++--- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 7b0541d1c2..e7bdb6c272 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -190,8 +190,20 @@ public String getCommentHintKey() { } }; - private static final boolean hibernatePresent = ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", - PersistenceProvider.class.getClassLoader()); + private static final Class typedParameterValueClass; + + static { + + Class type; + try { + type = ClassUtils.forName("org.hibernate.query.TypedParameterValue", + PersistenceProvider.class.getClassLoader()); + } catch (ClassNotFoundException e) { + type = null; + } + typedParameterValueClass = type; + } + private static final Collection ALL = List.of(HIBERNATE, ECLIPSELINK, GENERIC_JPA); static ConcurrentReferenceHashMap, PersistenceProvider> CACHE = new ConcurrentReferenceHashMap<>(); @@ -319,27 +331,14 @@ public boolean canExtractQuery() { * empty string for query creation. * * @param value - * @return the original value or an empty string. + * @return the original value or null. * @since 3.0 */ - public static Object condense(Object value) { - - if (hibernatePresent) { - - try { - - Class typeParameterValue = ClassUtils.forName("org.hibernate.query.TypedParameterValue", - PersistenceProvider.class.getClassLoader()); - - if (typeParameterValue.isInstance(value)) { - return null; - } - } catch (ClassNotFoundException | LinkageError o_O) { - return value; - } - } + public static Object unwrapTypedParameterValue(Object value) { - return value; + return typedParameterValueClass != null && typedParameterValueClass.isInstance(value) // + ? null // + : value; } /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index b2186abef4..b3737940f5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -233,24 +233,24 @@ public Object prepare(Object value) { Assert.notNull(value, "Value must not be null"); - Object condensedValue = PersistenceProvider.condense(value); + Object unwrapped = PersistenceProvider.unwrapTypedParameterValue(value); - if (condensedValue == null || expression.getJavaType() == null) { - return condensedValue; + if (unwrapped == null || expression.getJavaType() == null) { + return unwrapped; } if (String.class.equals(expression.getJavaType()) && !noWildcards) { switch (type) { case STARTING_WITH: - return String.format("%s%%", escape.escape(condensedValue.toString())); + return String.format("%s%%", escape.escape(unwrapped.toString())); case ENDING_WITH: - return String.format("%%%s", escape.escape(condensedValue.toString())); + return String.format("%%%s", escape.escape(unwrapped.toString())); case CONTAINING: case NOT_CONTAINING: - return String.format("%%%s%%", escape.escape(condensedValue.toString())); + return String.format("%%%s%%", escape.escape(unwrapped.toString())); default: - return condensedValue; + return unwrapped; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 84a23fa6cd..d52447ae36 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -714,21 +714,21 @@ public Type getType() { @Override public Object prepare(@Nullable Object value) { - Object condensedValue = PersistenceProvider.condense(value); - if (condensedValue == null) { + Object unwrapped = PersistenceProvider.unwrapTypedParameterValue(value); + if (unwrapped == null) { return null; } switch (type) { case STARTING_WITH: - return String.format("%s%%", condensedValue); + return String.format("%s%%", unwrapped); case ENDING_WITH: - return String.format("%%%s", condensedValue); + return String.format("%%%s", unwrapped); case CONTAINING: - return String.format("%%%s%%", condensedValue); + return String.format("%%%s%%", unwrapped); case LIKE: default: - return condensedValue; + return unwrapped; } } From b0986520ebc128caca316f47588e9915d1f851f1 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 14 Mar 2023 15:09:32 -0500 Subject: [PATCH 325/821] Allow JpaParametersParameterAccessor to extract dates from parameters. This allows the Hibernate variant (HibernateJpaParametersParameterAccessor) to potentially unwrap TypedParameterValue. Closes #2857 Original pull request #2859 --- ...bernateJpaParametersParameterAccessor.java | 24 ++++++++++++++++--- .../query/JpaParametersParameterAccessor.java | 14 +++++++++++ .../query/QueryParameterSetter.java | 9 +++---- ...rIndexedQueryParameterSetterUnitTests.java | 17 +++++++------ 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index 6b9f2628c2..012971ffca 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -17,6 +17,8 @@ import jakarta.persistence.EntityManager; +import java.util.Date; + import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.TypedParameterValue; import org.hibernate.type.BasicTypeRegistry; @@ -36,6 +38,7 @@ * @author Cedomir Igaly * @author Robert Wilson * @author Oliver Drotbohm + * @author Greg Turnquist * @since 2.7 */ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAccessor { @@ -53,9 +56,9 @@ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAcce super(parameters, values); - this.typeHelper = em.getEntityManagerFactory() - .unwrap(SessionFactoryImplementor.class) - .getTypeConfiguration() + this.typeHelper = em.getEntityManagerFactory() // + .unwrap(SessionFactoryImplementor.class) // + .getTypeConfiguration() // .getBasicTypeRegistry(); } @@ -78,4 +81,19 @@ public Object getValue(Parameter parameter) { return new TypedParameterValue<>(type, null); } + + /** + * For Hibernate, check if the incoming value is wrapped inside a {@link TypedParameterValue} before extracting and + * casting the {@link Date}. + * + * @param extractedValue + * @since 3.1 + */ + @Override + public Date extractDate(Object extractedValue) { + + return (extractedValue instanceof TypedParameterValue typedParameterValue) + ? (Date) typedParameterValue.getValue() + : (Date) extractedValue; + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java index fb1ff1b8b6..ea025d3cc9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository.query; +import java.util.Date; + import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; @@ -27,6 +29,7 @@ * * @author Jens Schauder * @author Mark Paluch + * @author Greg Turnquist */ public class JpaParametersParameterAccessor extends ParametersParameterAccessor { @@ -49,4 +52,15 @@ public T getValue(Parameter parameter) { public Object[] getValues() { return super.getValues(); } + + /** + * For general JPA providers, simply pass through the extracted value, casting it as a {@link Date}. + * + * @param extractedValue + * @since 3.1 + */ + public Date extractDate(Object extractedValue) { + return (Date) extractedValue; + } + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index 23ff9deed1..d16322db14 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.LENIENT; +import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.*; import jakarta.persistence.Parameter; import jakarta.persistence.Query; @@ -32,7 +32,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.hibernate.query.TypedParameterValue; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -82,11 +81,9 @@ public void setParameter(BindableQuery query, JpaParametersParameterAccessor acc if (temporalType != null) { - var extractedValue = valueExtractor.apply(accessor); + Object extractedValue = valueExtractor.apply(accessor); - final Date value = (extractedValue instanceof TypedParameterValue typedParameterValue) - ? (Date) typedParameterValue.getValue() - : (Date) extractedValue; + final Date value = accessor.extractDate(extractedValue); // One would think we can simply use parameter to identify the parameter we want to set. // But that does not work with list valued parameters. At least Hibernate tries to bind them by name. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java index 9440a81b51..6de4cc64fa 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java @@ -15,12 +15,16 @@ */ package org.springframework.data.jpa.repository.query; -import static java.util.Arrays.*; import static jakarta.persistence.TemporalType.*; +import static java.util.Arrays.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling.*; +import jakarta.persistence.Parameter; +import jakarta.persistence.Query; +import jakarta.persistence.TemporalType; +import jakarta.persistence.criteria.ParameterExpression; import lombok.Value; import java.util.Arrays; @@ -29,11 +33,6 @@ import java.util.List; import java.util.function.Function; -import jakarta.persistence.Parameter; -import jakarta.persistence.Query; -import jakarta.persistence.TemporalType; -import jakarta.persistence.criteria.ParameterExpression; - import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -65,7 +64,11 @@ class NamedOrIndexedQueryParameterSetterUnitTests { void before() { JpaParametersParameterAccessor accessor = mock(JpaParametersParameterAccessor.class); - when(accessor.getValues()).thenReturn(new Object[] { new Date() }); + + Date testDate = new Date(); + + when(accessor.getValues()).thenReturn(new Object[] { testDate }); + when(accessor.extractDate(testDate)).thenReturn(testDate); this.methodArguments = accessor; } From 628de3128477579c594e7b13b43c26c78c3070aa Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 15 Mar 2023 12:33:42 +0100 Subject: [PATCH 326/821] Polishing. Renamed to unwrapDate to align with the changes from #2861. Minor JavaDoc adjustments See #2857 Original pull request #2859 --- ...HibernateJpaParametersParameterAccessor.java | 17 +++++++++++------ .../query/JpaParametersParameterAccessor.java | 2 +- .../repository/query/QueryParameterSetter.java | 2 +- ...dOrIndexedQueryParameterSetterUnitTests.java | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index 012971ffca..84561d28e2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -27,6 +27,7 @@ import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * {@link org.springframework.data.repository.query.ParameterAccessor} based on an {@link Parameters} instance. In @@ -86,14 +87,18 @@ public Object getValue(Parameter parameter) { * For Hibernate, check if the incoming value is wrapped inside a {@link TypedParameterValue} before extracting and * casting the {@link Date}. * - * @param extractedValue - * @since 3.1 + * @param value a value that is either a {@link Date} or a {@link TypedParameterValue} containing a {@literal Date}. + * @since 3.0.4 */ @Override - public Date extractDate(Object extractedValue) { + public Date unwrapDate(Object value) { - return (extractedValue instanceof TypedParameterValue typedParameterValue) - ? (Date) typedParameterValue.getValue() - : (Date) extractedValue; + Object extracted = (value instanceof TypedParameterValue typedParameterValue) // + ? typedParameterValue.getValue() // + : value; + + Assert.isInstanceOf(Date.class, extracted, "Value must be either of type Date or a Date wrapped in a TypedParameterValue"); + + return (Date) extracted; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java index ea025d3cc9..c95e65a440 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java @@ -59,7 +59,7 @@ public Object[] getValues() { * @param extractedValue * @since 3.1 */ - public Date extractDate(Object extractedValue) { + public Date unwrapDate(Object extractedValue) { return (Date) extractedValue; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index d16322db14..4e68fbc2d9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -83,7 +83,7 @@ public void setParameter(BindableQuery query, JpaParametersParameterAccessor acc Object extractedValue = valueExtractor.apply(accessor); - final Date value = accessor.extractDate(extractedValue); + final Date value = accessor.unwrapDate(extractedValue); // One would think we can simply use parameter to identify the parameter we want to set. // But that does not work with list valued parameters. At least Hibernate tries to bind them by name. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java index 6de4cc64fa..c6127b1a30 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java @@ -68,7 +68,7 @@ void before() { Date testDate = new Date(); when(accessor.getValues()).thenReturn(new Object[] { testDate }); - when(accessor.extractDate(testDate)).thenReturn(testDate); + when(accessor.unwrapDate(testDate)).thenReturn(testDate); this.methodArguments = accessor; } From 66e374b909b0d1d7dc72de66aab41d31427f14e8 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 15 Mar 2023 14:28:06 +0100 Subject: [PATCH 327/821] Polishing. Removed instanceof assert. See #2857 Original pull request #2859 --- .../jpa/provider/HibernateJpaParametersParameterAccessor.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index 84561d28e2..8293a0d174 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -97,8 +97,6 @@ public Date unwrapDate(Object value) { ? typedParameterValue.getValue() // : value; - Assert.isInstanceOf(Date.class, extracted, "Value must be either of type Date or a Date wrapped in a TypedParameterValue"); - return (Date) extracted; } } From 6e389d79b1f999bf06268bfa42d6549247e72039 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 15 Mar 2023 15:23:28 +0100 Subject: [PATCH 328/821] Polishing. Reduce method visibility. Improve method naming. Avoid using var in production code. See #2857 Original pull request #2859 --- ...bernateJpaParametersParameterAccessor.java | 27 +++++++++---------- .../query/JpaParametersParameterAccessor.java | 12 ++++----- .../query/QueryParameterSetter.java | 2 +- ...rIndexedQueryParameterSetterUnitTests.java | 2 +- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java index 8293a0d174..d5f153b8ec 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java @@ -17,22 +17,20 @@ import jakarta.persistence.EntityManager; -import java.util.Date; - import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.TypedParameterValue; +import org.hibernate.type.BasicType; import org.hibernate.type.BasicTypeRegistry; import org.springframework.data.jpa.repository.query.JpaParametersParameterAccessor; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; /** * {@link org.springframework.data.repository.query.ParameterAccessor} based on an {@link Parameters} instance. In - * addition to the {@link JpaParametersParameterAccessor} functions, the bindable value is provided by fetching the - * method type when there is null. + * addition to the {@link JpaParametersParameterAccessor} functions, the bindable parameterValue is provided by fetching + * the method type when there is null. * * @author Wonchul Heo * @author Jens Schauder @@ -68,13 +66,13 @@ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAcce @SuppressWarnings("unchecked") public Object getValue(Parameter parameter) { - var value = super.getValue(parameter.getIndex()); + Object value = super.getValue(parameter.getIndex()); if (value != null) { return value; } - var type = typeHelper.getRegisteredType(parameter.getType()); + BasicType type = typeHelper.getRegisteredType(parameter.getType()); if (type == null) { return null; @@ -84,19 +82,18 @@ public Object getValue(Parameter parameter) { } /** - * For Hibernate, check if the incoming value is wrapped inside a {@link TypedParameterValue} before extracting and - * casting the {@link Date}. + * For Hibernate, check if the incoming parameterValue can be wrapped inside a {@link TypedParameterValue} before + * extracting. * - * @param value a value that is either a {@link Date} or a {@link TypedParameterValue} containing a {@literal Date}. + * @param parameterValue a parameterValue that is either a plain value or a {@link TypedParameterValue} containing a + * {@literal Date}. * @since 3.0.4 */ @Override - public Date unwrapDate(Object value) { + protected Object potentiallyUnwrap(Object parameterValue) { - Object extracted = (value instanceof TypedParameterValue typedParameterValue) // + return (parameterValue instanceof TypedParameterValue typedParameterValue) // ? typedParameterValue.getValue() // - : value; - - return (Date) extracted; + : parameterValue; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java index c95e65a440..70c384ca45 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java @@ -15,8 +15,6 @@ */ package org.springframework.data.jpa.repository.query; -import java.util.Date; - import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; @@ -54,13 +52,13 @@ public Object[] getValues() { } /** - * For general JPA providers, simply pass through the extracted value, casting it as a {@link Date}. + * Apply potential unwrapping to {@code parameterValue}. * - * @param extractedValue - * @since 3.1 + * @param parameterValue + * @since 3.0.4 */ - public Date unwrapDate(Object extractedValue) { - return (Date) extractedValue; + protected Object potentiallyUnwrap(Object parameterValue) { + return parameterValue; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index 4e68fbc2d9..a1991f1a30 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -83,7 +83,7 @@ public void setParameter(BindableQuery query, JpaParametersParameterAccessor acc Object extractedValue = valueExtractor.apply(accessor); - final Date value = accessor.unwrapDate(extractedValue); + Date value = (Date) accessor.potentiallyUnwrap(extractedValue); // One would think we can simply use parameter to identify the parameter we want to set. // But that does not work with list valued parameters. At least Hibernate tries to bind them by name. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java index c6127b1a30..171322146e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java @@ -68,7 +68,7 @@ void before() { Date testDate = new Date(); when(accessor.getValues()).thenReturn(new Object[] { testDate }); - when(accessor.unwrapDate(testDate)).thenReturn(testDate); + when(accessor.potentiallyUnwrap(testDate)).thenReturn(testDate); this.methodArguments = accessor; } From 1d69ece2f892503c184134dd108f97fe2463a243 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 15 Mar 2023 09:33:18 -0500 Subject: [PATCH 329/821] Verify proper sort handling even if HQL query already has ORDER BY a native function. Introduce test case proving that apply a Sort to an HQL query with an already-existing "order by function()" is handled properly. Resolves #2862. --- .../jpa/repository/query/HqlQueryTransformerTests.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index 5dbc29f961..d6050091df 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -784,6 +784,15 @@ void orderByShouldWorkWithSubSelectStatements() { + "from foo f", sort)).endsWith("order by f.age desc"); } + @Test // GH-2862 + void sortProperlyAppendsToExistingOrderByWithFunction() { + + assertThat(createQueryFor( + "select e from SampleEntity e where function('nativeFunc', ?1) > 'testVal' order by function('nativeFunc', ?1)", + Sort.by(Sort.Order.desc("age")))).isEqualTo( + "select e from SampleEntity e where function('nativeFunc', ?1) > 'testVal' order by function('nativeFunc', ?1), e.age desc"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 6ed0f8cb8bc5b2abbc511bddd65a4e3ba24937b6 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Sun, 19 Mar 2023 12:48:55 +0100 Subject: [PATCH 330/821] Improve docs on transactionality of methods declared on repository interfaces. We're now more specific on what CRUD methods means in the context of transactional query methods. Also, we now mention default methods explicitly in the discussion of query methods. Fixes #2868. --- src/main/asciidoc/jpa.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 07565ea689..eab6fba3a1 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -1120,7 +1120,7 @@ include::query-by-example.adoc[leveloffset=+1] [[transactions]] == Transactionality -By default, CRUD methods on repository instances inherited from link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html$$[`SimpleJpaRepository`] are transactional. +By default, methods inherited from `CrudRepository` inherited the transactional configuration from from link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html$$[`SimpleJpaRepository`]. For read operations, the transaction configuration `readOnly` flag is set to `true`. All others are configured with a plain `@Transactional` so that default transaction configuration applies. Repository methods that are backed by transactional repository fragments inherit the transactional attributes from the actual fragment method. @@ -1182,7 +1182,8 @@ Note that the call to `save` is not strictly necessary from a JPA point of view, [[transactional-query-methods]] === Transactional query methods -To let your query methods be transactional, use `@Transactional` at the repository interface you define, as shown in the following example: +Declared query methods (including default methods) do not get any transaction configuration applied by default. +To run those methods transactionally, use `@Transactional` at the repository interface you define, as shown in the following example: .Using @Transactional at query methods ==== From 512b17464ced692297b624d3c9152d86e0270d37 Mon Sep 17 00:00:00 2001 From: Claudio Nave Date: Sun, 19 Mar 2023 13:31:12 +0100 Subject: [PATCH 331/821] Fix small typo in the transactionality section of the documentation. Fixes #2869. --- src/main/asciidoc/jpa.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index eab6fba3a1..d03f63d787 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -1120,7 +1120,7 @@ include::query-by-example.adoc[leveloffset=+1] [[transactions]] == Transactionality -By default, methods inherited from `CrudRepository` inherited the transactional configuration from from link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html$$[`SimpleJpaRepository`]. +By default, methods inherited from `CrudRepository` inherit the transactional configuration from link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html$$[`SimpleJpaRepository`]. For read operations, the transaction configuration `readOnly` flag is set to `true`. All others are configured with a plain `@Transactional` so that default transaction configuration applies. Repository methods that are backed by transactional repository fragments inherit the transactional attributes from the actual fragment method. From 9c84fe5c3490bb5159e6b0700b75d20e82e97606 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 20 Mar 2023 15:01:19 +0100 Subject: [PATCH 332/821] Prepare 3.1 M3 (2023.0.0). See #2807 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c77686453f..87420adfd8 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-SNAPSHOT + 3.1.0-M3 @@ -37,7 +37,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-SNAPSHOT + 3.1.0-M3 0.10.3 org.hibernate @@ -226,8 +226,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone From f60c8a66435444b37afddceb199756a8f10f2666 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 20 Mar 2023 15:01:47 +0100 Subject: [PATCH 333/821] Release version 3.1 M3 (2023.0.0). See #2807 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 87420adfd8..b9566b1fbf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M3 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index db915d7c3b..93b699ec9e 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-SNAPSHOT + 3.1.0-M3 org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M3 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a5cb2f09b5..a4c000d2e9 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M3 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 27313e9e3c..f461b9e2f7 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-SNAPSHOT + 3.1.0-M3 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-M3 ../pom.xml From 6e8f11433f04cc8bc2106a4c224b90513e56c92e Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 20 Mar 2023 15:05:33 +0100 Subject: [PATCH 334/821] Prepare next development iteration. See #2807 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index b9566b1fbf..87420adfd8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M3 + 3.1.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 93b699ec9e..db915d7c3b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-M3 + 3.1.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.1.0-M3 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a4c000d2e9..a5cb2f09b5 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M3 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index f461b9e2f7..27313e9e3c 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-M3 + 3.1.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-M3 + 3.1.0-SNAPSHOT ../pom.xml From 49ab0cfbd87f8a6e3c965da5076e906e51c2fad9 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 20 Mar 2023 15:05:35 +0100 Subject: [PATCH 335/821] After release cleanups. See #2807 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 87420adfd8..c77686453f 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-M3 + 3.1.0-SNAPSHOT @@ -37,7 +37,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-M3 + 3.1.0-SNAPSHOT 0.10.3 org.hibernate @@ -226,8 +226,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot From 8d4c40f5cbab1b39b7e9acc99184793cedcc0420 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 21 Mar 2023 11:35:53 +0100 Subject: [PATCH 336/821] Leniently accept null when calling delete(Specification). Closes #2796 --- .../repository/support/SimpleJpaRepository.java | 15 ++++++--------- .../data/jpa/repository/UserRepositoryTests.java | 10 ++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index dfb77272d6..38ea619fab 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -15,12 +15,7 @@ */ package org.springframework.data.jpa.repository.support; -import static org.springframework.data.jpa.repository.query.QueryUtils.COUNT_QUERY_STRING; -import static org.springframework.data.jpa.repository.query.QueryUtils.DELETE_ALL_QUERY_BY_ID_STRING; -import static org.springframework.data.jpa.repository.query.QueryUtils.DELETE_ALL_QUERY_STRING; -import static org.springframework.data.jpa.repository.query.QueryUtils.applyAndBind; -import static org.springframework.data.jpa.repository.query.QueryUtils.getQueryString; -import static org.springframework.data.jpa.repository.query.QueryUtils.toOrders; +import static org.springframework.data.jpa.repository.query.QueryUtils.*; import jakarta.persistence.EntityManager; import jakarta.persistence.LockModeType; @@ -524,10 +519,12 @@ public long delete(Specification spec) { CriteriaBuilder builder = this.em.getCriteriaBuilder(); CriteriaDelete delete = builder.createCriteriaDelete(getDomainClass()); - Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder); + if (spec != null) { + Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder); - if (predicate != null) { - delete.where(predicate); + if (predicate != null) { + delete.where(predicate); + } } return this.em.createQuery(delete).executeUpdate(); 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 9697e0beb3..61fc82a88e 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 @@ -609,6 +609,16 @@ void returnsSamePageIfNoSpecGiven() { assertThat(repository.findAll((Specification) null, pageable)).isEqualTo(repository.findAll(pageable)); } + @Test // GH-2796 + void removesAllIfSpecificationIsNull() { + + flushTestUsers(); + + repository.delete((Specification) null); + + assertThat(repository.count()).isEqualTo(0L); + } + @Test void returnsAllAsPageIfNoPageableIsGiven() { From b5474bd61be29f088da6a4b96819a00f2b1dbb8c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 21 Mar 2023 11:45:09 +0100 Subject: [PATCH 337/821] Consistently document `JpaSpecificationExecutor` to require non-null `Specification`. Closes #2877 --- .../repository/JpaSpecificationExecutor.java | 36 ++++---- .../support/SimpleJpaRepository.java | 88 ++++++++++--------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index e7529f6e20..7f59d83bba 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -24,7 +24,6 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.repository.query.FluentQuery; -import org.springframework.lang.Nullable; /** * Interface to allow execution of {@link Specification}s based on the JPA criteria API. @@ -32,65 +31,66 @@ * @author Oliver Gierke * @author Christoph Strobl * @author Diego Krupitza + * @author Mark Paluch */ public interface JpaSpecificationExecutor { /** * Returns a single entity matching the given {@link Specification} or {@link Optional#empty()} if none found. * - * @param spec can be {@literal null}. + * @param spec must not be {@literal null}. * @return never {@literal null}. * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one entity found. */ - Optional findOne(@Nullable Specification spec); + Optional findOne(Specification spec); /** * Returns all entities matching the given {@link Specification}. * - * @param spec can be {@literal null}. + * @param spec must not be {@literal null}. * @return never {@literal null}. */ - List findAll(@Nullable Specification spec); + List findAll(Specification spec); /** * Returns a {@link Page} of entities matching the given {@link Specification}. * - * @param spec can be {@literal null}. + * @param spec must not be {@literal null}. * @param pageable must not be {@literal null}. * @return never {@literal null}. */ - Page findAll(@Nullable Specification spec, Pageable pageable); + Page findAll(Specification spec, Pageable pageable); /** * Returns all entities matching the given {@link Specification} and {@link Sort}. * - * @param spec can be {@literal null}. + * @param spec must not be {@literal null}. * @param sort must not be {@literal null}. * @return never {@literal null}. */ - List findAll(@Nullable Specification spec, Sort sort); + List findAll(Specification spec, Sort sort); /** * Returns the number of instances that the given {@link Specification} will return. * - * @param spec the {@link Specification} to count instances for. Can be {@literal null}. + * @param spec the {@link Specification} to count instances for, must not be {@literal null}. * @return the number of instances. */ - long count(@Nullable Specification spec); + long count(Specification spec); /** * Checks whether the data store contains elements that match the given {@link Specification}. - * - * @param spec the {@link Specification} to use for the existence check. Must not be {@literal null}. - * @return true if the data store contains elements that match the given {@link Specification} otherwise - * false. + * + * @param spec the {@link Specification} to use for the existence check, ust not be {@literal null}. + * @return {@code true} if the data store contains elements that match the given {@link Specification} otherwise + * {@code false}. */ boolean exists(Specification spec); /** * Deletes by the {@link Specification} and returns the number of rows deleted. * - * @param spec the {@link Specification} to use for the existence check. Must not be {@literal null}. + * @param spec the {@link Specification} to use for the existence check, must not be {@literal null}. * @return the number of entities deleted */ long delete(Specification spec); @@ -99,8 +99,8 @@ public interface JpaSpecificationExecutor { * Returns entities matching the given {@link Specification} applying the {@code queryFunction} that defines the query * and its result type. * - * @param spec – must not be null. - * @param queryFunction – the query function defining projection, sorting, and the result type + * @param spec must not be null. + * @param queryFunction the query function defining projection, sorting, and the result type * @return all entities matching the given Example. * @since 3.0 */ diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 38ea619fab..d83d5f50ff 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -447,7 +447,7 @@ public Page findAll(Pageable pageable) { } @Override - public Optional findOne(@Nullable Specification spec) { + public Optional findOne(Specification spec) { try { return Optional.of(getQuery(spec, Sort.unsorted()).setMaxResults(2).getSingleResult()); @@ -457,12 +457,12 @@ public Optional findOne(@Nullable Specification spec) { } @Override - public List findAll(@Nullable Specification spec) { + public List findAll(Specification spec) { return getQuery(spec, Sort.unsorted()).getResultList(); } @Override - public Page findAll(@Nullable Specification spec, Pageable pageable) { + public Page findAll(Specification spec, Pageable pageable) { TypedQuery query = getQuery(spec, pageable); return isUnpaged(pageable) ? new PageImpl<>(query.getResultList()) @@ -470,10 +470,51 @@ public Page findAll(@Nullable Specification spec, Pageable pageable) { } @Override - public List findAll(@Nullable Specification spec, Sort sort) { + public List findAll(Specification spec, Sort sort) { return getQuery(spec, sort).getResultList(); } + @Override + public boolean exists(Specification spec) { + + CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(Integer.class); + cq.select(this.em.getCriteriaBuilder().literal(1)); + applySpecificationToCriteria(spec, getDomainClass(), cq); + TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); + return query.setMaxResults(1).getResultList().size() == 1; + } + + @Override + public long delete(Specification spec) { + + CriteriaBuilder builder = this.em.getCriteriaBuilder(); + CriteriaDelete delete = builder.createCriteriaDelete(getDomainClass()); + + if (spec != null) { + Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder); + + if (predicate != null) { + delete.where(predicate); + } + } + + return this.em.createQuery(delete).executeUpdate(); + } + + @Override + public R findBy(Specification spec, Function, R> queryFunction) { + + Assert.notNull(spec, "Specification must not be null"); + Assert.notNull(queryFunction, "Query function must not be null"); + + Function> finder = sort -> getQuery(spec, getDomainClass(), sort); + + FetchableFluentQuery fluentQuery = new FetchableFluentQueryBySpecification(spec, getDomainClass(), + Sort.unsorted(), null, finder, this::count, this::exists, this.em); + + return queryFunction.apply((FetchableFluentQuery) fluentQuery); + } + @Override public Optional findOne(Example example) { @@ -503,32 +544,6 @@ public boolean exists(Example example) { return query.setMaxResults(1).getResultList().size() == 1; } - @Override - public boolean exists(Specification spec) { - - CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(Integer.class); - cq.select(this.em.getCriteriaBuilder().literal(1)); - applySpecificationToCriteria(spec, getDomainClass(), cq); - TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); - return query.setMaxResults(1).getResultList().size() == 1; - } - - @Override - public long delete(Specification spec) { - - CriteriaBuilder builder = this.em.getCriteriaBuilder(); - CriteriaDelete delete = builder.createCriteriaDelete(getDomainClass()); - - if (spec != null) { - Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder); - - if (predicate != null) { - delete.where(predicate); - } - } - - return this.em.createQuery(delete).executeUpdate(); - } @Override public List findAll(Example example) { @@ -571,19 +586,6 @@ public R findBy(Example example, Function R findBy(Specification spec, Function, R> queryFunction) { - - Assert.notNull(spec, "Specification must not be null"); - Assert.notNull(queryFunction, "Query function must not be null"); - - Function> finder = sort -> getQuery(spec, getDomainClass(), sort); - - FetchableFluentQuery fluentQuery = new FetchableFluentQueryBySpecification(spec, getDomainClass(), - Sort.unsorted(), null, finder, this::count, this::exists, this.em); - - return queryFunction.apply((FetchableFluentQuery) fluentQuery); - } @Override public long count() { From 111a1140fb972a6597da9f0298d04eda659a6bd1 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 21 Mar 2023 11:45:41 +0100 Subject: [PATCH 338/821] Polishing. Add since tag. See #2796 --- .../data/jpa/repository/JpaSpecificationExecutor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index 7f59d83bba..8835cf1d6e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -91,7 +91,8 @@ public interface JpaSpecificationExecutor { * Deletes by the {@link Specification} and returns the number of rows deleted. * * @param spec the {@link Specification} to use for the existence check, must not be {@literal null}. - * @return the number of entities deleted + * @return the number of entities deleted. + * @since 3.0 */ long delete(Specification spec); From c5cc99714be9bc9c7cacb7f2b76bc703b5e95264 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 22 Mar 2023 11:42:30 -0500 Subject: [PATCH 339/821] Verify that correct alias is picked amidst multiple aliases. Resolves #2074. --- .../data/jpa/repository/query/HqlQueryTransformerTests.java | 5 +++++ .../data/jpa/repository/query/JpqlQueryTransformerTests.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index d6050091df..b3a6bcf403 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -793,6 +793,11 @@ void sortProperlyAppendsToExistingOrderByWithFunction() { "select e from SampleEntity e where function('nativeFunc', ?1) > 'testVal' order by function('nativeFunc', ?1), e.age desc"); } + @Test // GH-2074 + void queryParserPicksCorrectAliasAmidstMultipleAlises() { + assertThat(alias("select u from User as u left join u.roles as r")).isEqualTo("u"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java index 557df55156..d63cbd26d8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java @@ -674,6 +674,11 @@ void orderByShouldWorkWithSubSelectStatements() { + "GROUP BY i2.field.id, i2.version)", sort)).endsWith("order by i.age desc"); } + @Test // GH-2074 + void queryParserPicksCorrectAliasAmidstMultipleAlises() { + assertThat(alias("select u from User as u left join u.roles as r")).isEqualTo("u"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From efc9aed64ec659192e9482657f5becdbcc9084ab Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 20 Mar 2023 17:05:24 -0500 Subject: [PATCH 340/821] Count query generation should handle missing alias. In the event of a Hibernate query where the FROM clause has no alias, the query parser should use "__" as a stand-in alias. Resolves #2032. Related: #2220. --- .../jpa/repository/query/HqlQueryTransformer.java | 11 +++++++++++ .../jpa/repository/query/JpaQueryParsingToken.java | 5 ++++- .../repository/query/HqlQueryTransformerTests.java | 10 ++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java index dbb0f91558..1b45d4e6a4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java @@ -243,6 +243,17 @@ public List visitFromRoot(HqlParser.FromRootContext ctx) { if (this.alias == null && !isSubquery(ctx)) { this.alias = tokens.get(tokens.size() - 1).getToken(); } + } else { + + if (countQuery) { + + tokens.add(TOKEN_AS); + tokens.add(TOKEN_DOUBLE_UNDERSCORE); + + if (this.alias == null && !isSubquery(ctx)) { + this.alias = TOKEN_DOUBLE_UNDERSCORE.getToken(); + } + } } } else if (ctx.subquery() != null) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java index 5c221e62d4..e4ca63df2e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java @@ -17,7 +17,6 @@ import java.util.List; import java.util.function.Supplier; -import java.util.stream.Collectors; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.TerminalNode; @@ -53,6 +52,10 @@ class JpaQueryParsingToken { public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET_BRACE = new JpaQueryParsingToken("]}"); public static final JpaQueryParsingToken TOKEN_CLOSE_PAREN_BRACE = new JpaQueryParsingToken(")}"); + public static final JpaQueryParsingToken TOKEN_DOUBLE_UNDERSCORE = new JpaQueryParsingToken("__"); + + public static final JpaQueryParsingToken TOKEN_AS = new JpaQueryParsingToken("AS"); + public static final JpaQueryParsingToken TOKEN_DESC = new JpaQueryParsingToken("desc", false); public static final JpaQueryParsingToken TOKEN_ASC = new JpaQueryParsingToken("asc", false); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index b3a6bcf403..cae0d71d31 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -798,6 +798,16 @@ void queryParserPicksCorrectAliasAmidstMultipleAlises() { assertThat(alias("select u from User as u left join u.roles as r")).isEqualTo("u"); } + @Test // GH-2032 + void countQueryShouldWorkEvenWithoutExplicitAlias() { + + assertCountQuery("FROM BookError WHERE portal = :portal", + "select count(__) FROM BookError AS __ WHERE portal = :portal"); + + assertCountQuery("FROM BookError b WHERE portal = :portal", + "select count(b) FROM BookError b WHERE portal = :portal"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 2f6955ec0ac78a220c97e7846ee4ce173fbd1380 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 22 Mar 2023 15:58:00 -0500 Subject: [PATCH 341/821] Add additional tests to verify proper detection of alias values. In the past, a token with "from" buried in it would trip up QueryUtils. These tests further demonstrate that this is no longer an issue with the HQL parser. Related: #2508. --- .../repository/query/HqlQueryTransformerTests.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index cae0d71d31..56cb2c0425 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -808,6 +808,17 @@ void countQueryShouldWorkEvenWithoutExplicitAlias() { "select count(b) FROM BookError b WHERE portal = :portal"); } + @Test // GH-2508 + void detectAliasWithCastCorrectly() { + + assertThat(alias("from User u where (cast(:effective as date) is null) OR :effective >= u.createdAt")) + .isEqualTo("u"); + assertThat(alias("from User u where (cast(:effectiveDate as date) is null) OR :effectiveDate >= u.createdAt")) + .isEqualTo("u"); + assertThat(alias("from User u where (cast(:effectiveFrom as date) is null) OR :effectiveFrom >= u.createdAt")) + .isEqualTo("u"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 99737cc8d7b8ab4066dc56ca15c6d24a675eadb7 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 22 Mar 2023 07:43:07 +0100 Subject: [PATCH 342/821] Add required runtime hint for streaming results. This commit makes sure we're able to invoke the method required for streaming results. Resolves: #2848 --- .../data/jpa/repository/aot/JpaRuntimeHints.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java index b336e7cb1d..425fef3b1a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java @@ -18,7 +18,9 @@ import jakarta.persistence.NamedEntityGraph; import java.util.Arrays; +import java.util.Collections; +import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; @@ -76,6 +78,12 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) .onReachableType(QuerydslPredicateExecutor.class)); } + // streaming results requires reflective access to jakarta.persistence.Query#getResultAsStream + hints.reflection().registerType(jakarta.persistence.Query.class, MemberCategory.INTROSPECT_PUBLIC_METHODS); + hints.reflection().registerType(jakarta.persistence.Query.class, hint -> { + hint.withMethod("getResultStream", Collections.emptyList(), ExecutableMode.INVOKE); + }); + hints.reflection().registerType(NamedEntityGraph.class, hint -> hint.onReachableType(EntityGraph.class).withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); } From c91839ecb1c89353127cd0fb1d0be7cb91b86883 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 21 Dec 2022 11:55:21 +0100 Subject: [PATCH 343/821] Add missing reflection configuration for AbstractPersistable & -Auditable. Resolves #2735. --- .../data/jpa/repository/aot/JpaRuntimeHints.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java index 425fef3b1a..d097bfcae8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRuntimeHints.java @@ -19,12 +19,15 @@ import java.util.Arrays; import java.util.Collections; +import java.util.List; import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.TypeReference; +import org.springframework.data.jpa.domain.AbstractAuditable; +import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import org.springframework.data.jpa.repository.EntityGraph; @@ -71,6 +74,11 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) // needs to present for evaluating default attribute values in JpaQueryMethod hints.reflection().registerType(Query.class, hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); + // make sure annotations on the fields are visible and allow reflection on protected methods + hints.reflection().registerTypes( + List.of(TypeReference.of(AbstractPersistable.class), TypeReference.of(AbstractAuditable.class)), + hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS)); + if (QuerydslUtils.QUERY_DSL_PRESENT) { hints.reflection().registerType(QuerydslJpaPredicateExecutor.class, From 0ac0ee4b7dc7228bbd6464034b37462dac2ed8e0 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 22 Mar 2023 10:01:04 -0500 Subject: [PATCH 344/821] Drop support for SpEL expressions in the query parsers. By delaying the creation of count queries beyond the point where SpEL evaluation is carried out, there is no longer a need to handle SpEL expressions in the query parsers. Resolves #2881. Original pull request: #2882. --- .../data/jpa/repository/query/Hql.g4 | 10 +--- .../data/jpa/repository/query/Jpql.g4 | 7 --- .../repository/query/HqlQueryRenderer.java | 48 ------------------- .../repository/query/JpqlQueryRenderer.java | 44 ----------------- 4 files changed, 1 insertion(+), 108 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 253caafc11..568f5a2355 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -620,7 +620,7 @@ variable parameter : prefix=':' identifier - | prefix='?' (INTEGER_LITERAL | spelExpression)? + | prefix='?' INTEGER_LITERAL? ; entityName @@ -629,16 +629,8 @@ entityName identifier : reservedWord - | spelExpression ; -spelExpression - : prefix='#{#' identificationVariable ('.' identificationVariable)* '}' // #{#entityName} - | prefix='#{#[' INTEGER_LITERAL ']}' // #{[0]} - | prefix='#{' identificationVariable '(' ( stringLiteral | '[' INTEGER_LITERAL ']' )? ')}' // #{escape([0])} | #{escapeCharacter()} - ; - - character : CHARACTER ; diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index dc96a6ea46..2b5cc7ca44 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -599,7 +599,6 @@ identification_variable | ORDER // Gap in the spec requires supporting 'Order' as an entity name | COUNT // Gap in the spec requires supporting 'count' as a possible name | KEY // Gap in the sepc requires supported 'key' as a possible name - | spel_expression // we use various SpEL expressions in our queries ; constructor_name @@ -704,12 +703,6 @@ function_name : string_literal ; -spel_expression - : prefix='#{#' identification_variable ('.' identification_variable)* '}' // #{#entityName} - | prefix='#{#[' INTLITERAL ']}' // #{[0]} - | prefix='#{' identification_variable '(' ( string_literal | '[' INTLITERAL ']' )? ')}' // #{escape([0])} | #{escapeCharacter()} - ; - character_valued_input_parameter : CHARACTER | input_parameter diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index c7930c3dcd..f613352d6f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -2222,8 +2222,6 @@ public List visitParameter(HqlParser.ParameterContext ctx) if (ctx.INTEGER_LITERAL() != null) { tokens.add(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); - } else if (ctx.spelExpression() != null) { - tokens.addAll(visit(ctx.spelExpression())); } } @@ -2251,57 +2249,11 @@ public List visitIdentifier(HqlParser.IdentifierContext ct if (ctx.reservedWord() != null) { return visit(ctx.reservedWord()); - } else if (ctx.spelExpression() != null) { - return visit(ctx.spelExpression()); } else { return List.of(); } } - @Override - public List visitSpelExpression(HqlParser.SpelExpressionContext ctx) { - - List tokens = new ArrayList<>(); - - if (ctx.prefix.equals("#{#")) { // #{#entityName} - - tokens.add(new JpaQueryParsingToken(ctx.prefix)); - - ctx.identificationVariable().forEach(identificationVariableContext -> { - tokens.addAll(visit(identificationVariableContext)); - tokens.add(TOKEN_DOT); - }); - CLIP(tokens); - - tokens.add(TOKEN_CLOSE_BRACE); - - } else if (ctx.prefix.equals("#{#[")) { // #{[0]} - - tokens.add(new JpaQueryParsingToken(ctx.prefix)); - tokens.add(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); - tokens.add(TOKEN_CLOSE_SQUARE_BRACKET_BRACE); - - } else if (ctx.prefix.equals("#{")) {// #{escape([0])} or #{escape('foo')} - - tokens.add(new JpaQueryParsingToken(ctx.prefix)); - tokens.addAll(visit(ctx.identificationVariable(0))); - tokens.add(TOKEN_OPEN_PAREN); - - if (ctx.stringLiteral() != null) { - tokens.addAll(visit(ctx.stringLiteral())); - } else if (ctx.INTEGER_LITERAL() != null) { - - tokens.add(TOKEN_OPEN_SQUARE_BRACKET); - tokens.add(new JpaQueryParsingToken(ctx.INTEGER_LITERAL())); - tokens.add(TOKEN_CLOSE_SQUARE_BRACKET); - } - - tokens.add(TOKEN_CLOSE_PAREN_BRACE); - } - - return tokens; - } - @Override public List visitCharacter(HqlParser.CharacterContext ctx) { return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java index e433713319..0598e7029f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -2124,8 +2124,6 @@ public List visitIdentification_variable(JpqlParser.Identi return List.of(new JpaQueryParsingToken(ctx.ORDER())); } else if (ctx.KEY() != null) { return List.of(new JpaQueryParsingToken(ctx.KEY())); - } else if (ctx.spel_expression() != null) { - return visit(ctx.spel_expression()); } else { return List.of(); } @@ -2322,48 +2320,6 @@ public List visitFunction_name(JpqlParser.Function_nameCon return visit(ctx.string_literal()); } - @Override - public List visitSpel_expression(JpqlParser.Spel_expressionContext ctx) { - - List tokens = new ArrayList<>(); - - if (ctx.prefix.equals("#{#")) { // #{#entityName} - - tokens.add(new JpaQueryParsingToken(ctx.prefix)); - ctx.identification_variable().forEach(identificationVariableContext -> { - tokens.addAll(visit(identificationVariableContext)); - tokens.add(TOKEN_DOT); - }); - CLIP(tokens); - tokens.add(TOKEN_CLOSE_BRACE); - - } else if (ctx.prefix.equals("#{#[")) { // #{[0]} - - tokens.add(new JpaQueryParsingToken(ctx.prefix)); - tokens.add(new JpaQueryParsingToken(ctx.INTLITERAL())); - tokens.add(TOKEN_CLOSE_SQUARE_BRACKET_BRACE); - - } else if (ctx.prefix.equals("#{")) {// #{escape([0])} or #{escape('foo')} - - tokens.add(new JpaQueryParsingToken(ctx.prefix)); - tokens.addAll(visit(ctx.identification_variable(0))); - tokens.add(TOKEN_OPEN_PAREN); - - if (ctx.string_literal() != null) { - tokens.addAll(visit(ctx.string_literal())); - } else if (ctx.INTLITERAL() != null) { - - tokens.add(TOKEN_OPEN_SQUARE_BRACKET); - tokens.add(new JpaQueryParsingToken(ctx.INTLITERAL())); - tokens.add(TOKEN_CLOSE_SQUARE_BRACKET); - } - - tokens.add(TOKEN_CLOSE_PAREN_BRACE); - } - - return tokens; - } - @Override public List visitCharacter_valued_input_parameter( JpqlParser.Character_valued_input_parameterContext ctx) { From 9c4273cf349a0f89a638d8096f59dbeaccf21d52 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 20 Mar 2023 16:14:56 -0500 Subject: [PATCH 345/821] Recognize RIGHT, LEFT, OUTER, INNER and FULL as JPQL/HQL reserved words. RIGHT, LEFT, OUTER, INNER, and FULL are HQL tokens. This means they also need to be recognized as potential reserved words and thus possibly identifiers. This can show up if someone, for example, uses "right" as the name of a relationship in a JPA entity. Resolves #2864. Original pull request: #2874. --- .../data/jpa/repository/query/Hql.g4 | 10 ++--- .../data/jpa/repository/query/Jpql.g4 | 3 ++ .../query/HqlQueryTransformerTests.java | 38 +++++++++++++++++++ .../query/JpqlQueryTransformerTests.java | 38 +++++++++++++++++++ 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 568f5a2355..c429687b77 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -692,7 +692,7 @@ reservedWord | FOR | FORMAT | FROM -// | FULL + | FULL | FUNCTION | GROUP | GROUPS @@ -704,7 +704,7 @@ reservedWord | IN | INDEX | INDICES -// | INNER + | INNER | INSERT | INSTANT | INTERSECT @@ -714,7 +714,7 @@ reservedWord | KEY | LAST | LEADING -// | LEFT + | LEFT | LIKE | LIMIT | LIST @@ -752,7 +752,7 @@ reservedWord | OR | ORDER | OTHERS -// | OUTER + | OUTER | OVER | OVERFLOW | OVERLAY @@ -765,7 +765,7 @@ reservedWord | QUARTER | RANGE | RESPECT -// | RIGHT + | RIGHT | ROLLUP | ROW | ROWS diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 2b5cc7ca44..9bad5f4c72 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -599,6 +599,9 @@ identification_variable | ORDER // Gap in the spec requires supporting 'Order' as an entity name | COUNT // Gap in the spec requires supporting 'count' as a possible name | KEY // Gap in the sepc requires supported 'key' as a possible name + | LEFT + | INNER + | OUTER ; constructor_name diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index 56cb2c0425..f70b875407 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -18,9 +18,13 @@ import static org.assertj.core.api.Assertions.*; import java.util.regex.Pattern; +import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.JpaSort; @@ -808,6 +812,30 @@ void countQueryShouldWorkEvenWithoutExplicitAlias() { "select count(b) FROM BookError b WHERE portal = :portal"); } + @ParameterizedTest + @MethodSource("queriesWithReservedWordsAsIdentifiers") // GH-2864 + void usingReservedWordAsRelationshipNameShouldWork(String relationshipName, String joinAlias) { + + HqlQueryParser.parseQuery(String.format(""" + select u + from UserAccountEntity u + join fetch u.lossInspectorLimitConfiguration lil + join fetch u.companyTeam ct + where exists ( + select iu + from UserAccountEntity iu + join iu.roles u2r + join u2r.role r + join r.rights r2r + join r2r.%s %s + where + %s.code = :rightCode + and iu = u + ) + and ct.id = :teamId + """, relationshipName, joinAlias, joinAlias)); + } + @Test // GH-2508 void detectAliasWithCastCorrectly() { @@ -819,6 +847,16 @@ void detectAliasWithCastCorrectly() { .isEqualTo("u"); } + static Stream queriesWithReservedWordsAsIdentifiers() { + + return Stream.of( // + Arguments.of("right", "rt"), // + Arguments.of("left", "lt"), // + Arguments.of("outer", "ou"), // + Arguments.of("full", "full"), // + Arguments.of("inner", "inr")); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java index d63cbd26d8..8ebcfa83e0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java @@ -18,9 +18,13 @@ import static org.assertj.core.api.Assertions.*; import java.util.regex.Pattern; +import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.JpaSort; @@ -679,6 +683,40 @@ void queryParserPicksCorrectAliasAmidstMultipleAlises() { assertThat(alias("select u from User as u left join u.roles as r")).isEqualTo("u"); } + @ParameterizedTest + @MethodSource("queriesWithReservedWordsAsIdentifiers") // GH-2864 + void usingReservedWordAsRelationshipNameShouldWork(String relationshipName, String joinAlias) { + + JpqlQueryParser.parseQuery(String.format(""" + select u + from UserAccountEntity u + join u.lossInspectorLimitConfiguration lil + join u.companyTeam ct + where exists ( + select iu + from UserAccountEntity iu + join iu.roles u2r + join u2r.role r + join r.rights r2r + join r2r.inner inr + where + inr.code = :rightCode + and iu = u + ) + and ct.id = :teamId + """, relationshipName, joinAlias, joinAlias)); + } + + static Stream queriesWithReservedWordsAsIdentifiers() { + + return Stream.of( // + Arguments.of("right", "rt"), // + Arguments.of("left", "lt"), // + Arguments.of("outer", "ou"), // + Arguments.of("full", "full"), // + Arguments.of("inner", "inr")); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 8b071988c9e12bbfc755459a68340ccdffd1784f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 28 Mar 2023 09:16:37 +0200 Subject: [PATCH 346/821] Polishing. Use textblocks where possible. See #2864. Original pull request: #2874. --- .../query/HqlQueryTransformerTests.java | 264 ++++++++++-------- .../query/JpqlQueryTransformerTests.java | 170 ++++++----- 2 files changed, 262 insertions(+), 172 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index f70b875407..df1e8df656 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -17,7 +17,6 @@ import static org.assertj.core.api.Assertions.*; -import java.util.regex.Pattern; import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; @@ -34,17 +33,13 @@ * Verify that HQL queries are properly transformed through the {@link JpaQueryEnhancer} and the {@link HqlQueryParser}. * * @author Greg Turnquist - * @since 3.1 */ class HqlQueryTransformerTests { private static final String QUERY = "select u from User u"; - private static final String FQ_QUERY = "select u from org.acme.domain.User$Foo_Bar u"; private static final String SIMPLE_QUERY = "from User u"; private static final String COUNT_QUERY = "select count(u) from User u"; - private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?1"; - private static final Pattern MULTI_WHITESPACE = Pattern.compile("\\s+"); @Test void applyingSortShouldIntroduceOrderByCriteriaWhereNoneExists() { @@ -211,17 +206,29 @@ void applySortingAccountsForNewlinesInSubselect() { Sort sort = Sort.by(Sort.Order.desc("age")); - assertThat(newParser("select u\n" + // - "from user u\n" + // - "where exists (select u2\n" + // - "from user u2\n" + // - ")\n" + // - "").applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + // - "from user u\n" + // - "where exists (select u2\n" + // - "from user u2\n" + // - ")\n" + // - " order by u.age desc"); + // + // + // + // + // + // + // + // + // + // + assertThat(newParser(""" + select u + from user u + where exists (select u2 + from user u2 + ) + """).applySorting(sort)).isEqualToIgnoringWhitespace(""" + select u + from user u + where exists (select u2 + from user u2 + ) + order by u.age desc"""); } @Test // GH-2563 @@ -355,12 +362,14 @@ void detectsConstructorExpressionInDistinctQuery() { @Test // DATAJPA-938 void detectsComplexConstructorExpression() { - assertThat(hasConstructorExpression("select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) " // - + "from Bar lp join lp.investmentProduct ip " // - + "where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId " - // - + "group by ip.id, ip.name, lp.accountId " // - + "order by ip.name ASC")).isTrue(); + // + assertThat(hasConstructorExpression( + """ + select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) + from Bar lp join lp.investmentProduct ip + where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId group by ip.id, ip.name, lp.accountId + order by ip.name ASC""")) + .isTrue(); } @Test // DATAJPA-938 @@ -485,21 +494,39 @@ void detectsAliasWithGroupAndOrderBy() { @Test // DATAJPA-1500 void createCountQuerySupportsWhitespaceCharacters() { - assertThat(createCountQueryFor("select user from User user\n" + // - " where user.age = 18\n" + // - " order by user.name\n ")).isEqualToIgnoringWhitespace("select count(user) from User user\n" + // - " where user.age = 18\n "); + // + // + // + assertThat(createCountQueryFor(""" + select user from User user + where user.age = 18 + order by user.name + """)).isEqualToIgnoringWhitespace(""" + select count(user) from User user + where user.age = 18 + """); } @Test void createCountQuerySupportsLineBreaksInSelectClause() { - assertThat(createCountQueryFor("select user.age,\n" + // - " user.name\n" + // - " from User user\n" + // - " where user.age = 18\n" + // - " order\nby\nuser.name\n ")).isEqualToIgnoringWhitespace("select count(user) from User user\n" + // - " where user.age = 18\n "); + // + // + // + // + // + assertThat(createCountQueryFor(""" + select user.age, + user.name + from User user + where user.age = 18 + order + by + user.name + """)).isEqualToIgnoringWhitespace(""" + select count(user) from User user + where user.age = 18 + """); } @Test // DATAJPA-1061 @@ -550,11 +577,24 @@ void appliesSortCorrectlyForSimpleField() { @Test void createCountQuerySupportsLineBreakRightAfterDistinct() { - assertThat(createCountQueryFor("select\ndistinct\nuser.age,\n" + // - "user.name\n" + // - "from\nUser\nuser")).isEqualTo(createCountQueryFor("select\ndistinct user.age,\n" + // - "user.name\n" + // - "from\nUser\nuser")); + // + // + // + // + assertThat(createCountQueryFor(""" + select + distinct + user.age, + user.name + from + User + user""")).isEqualTo(createCountQueryFor(""" + select + distinct user.age, + user.name + from + User + user""")); } @Test @@ -723,69 +763,74 @@ void orderByShouldWorkWithSubSelectStatements() { Sort sort = Sort.by(Sort.Order.desc("age")); - assertThat(createQueryFor("SELECT\n" // - + " foo_bar\n" // - + "FROM\n" // - + " foo foo\n" // - + "INNER JOIN\n" // - + " foo_bar_dnrmv foo_bar ON\n" // - + " foo_bar.foo_id = foo.foo_id\n" // - + "INNER JOIN\n" // - + " (\n" // - + " SELECT\n" // - + " foo_bar_action\n" // - + " FROM\n" // - + " foo_bar_action\n" // - + " WHERE\n" // - + " foo_bar_action.deleted_ts IS NULL)\n" // - + " foo_bar_action ON\n" // - + " foo_bar.foo_bar_id = foo_bar_action.foo_bar_id\n" // - + " AND ranking = 1\n" // - + "INNER JOIN\n" // - + " bar bar ON\n" // - + " foo_bar.bar_id = bar.bar_id\n" // - + "INNER JOIN\n" // - + " bar_metadata bar_metadata ON\n" // - + " bar.bar_metadata_key = bar_metadata.bar_metadata_key\n" // - + "WHERE\n" // - + " foo.tenant_id =:tenantId", sort)).endsWith("order by foo.age desc"); - - assertThat(createQueryFor("select r " // - + "From DataRecord r " // - + "where " // - + " ( " // - + " r.adusrId = :userId " // - + " or EXISTS( select 1 FROM DataRecordDvsRight dr WHERE dr.adusrId = :userId AND dr.dataRecord = r ) " // - + ")", sort)).endsWith("order by r.age desc"); - - assertThat(createQueryFor("select distinct u " // - + "from FooBar u " // - + "where u.role = 'redacted' " // - + "and (" // - + " not exists (" // - + " from FooBarGroup group " // - + " where group in :excludedGroups " // - + " and group in elements(u.groups)" // - + " )" // - + ")", sort)).endsWith("order by u.age desc"); - - assertThat(createQueryFor("SELECT i " // - + " FROM Item i " // - + " WHERE i.id IN (" // - + " SELECT max(i2.id) FROM Item i2 " // - + " WHERE i2.field.id = :fieldId " // - + " GROUP BY i2.field.id, i2.version)", sort)).endsWith("order by i.age desc"); - - assertThat(createQueryFor("select \n" // - + " f.id,\n" // - + " (\n" // - + " select timestamp from bar\n" // - + " where date(bar.timestamp) > '2022-05-21'\n" // - + " and bar.foo_id = f.id \n" // - + " order by date(bar.timestamp) desc\n" // - + " limit 1\n" // - + ") as timestamp\n" // - + "from foo f", sort)).endsWith("order by f.age desc"); + assertThat(createQueryFor(""" + SELECT + foo_bar + FROM + foo foo + INNER JOIN + foo_bar_dnrmv foo_bar ON + foo_bar.foo_id = foo.foo_id + INNER JOIN + ( + SELECT + foo_bar_action + FROM + foo_bar_action + WHERE + foo_bar_action.deleted_ts IS NULL) + foo_bar_action ON + foo_bar.foo_bar_id = foo_bar_action.foo_bar_id + AND ranking = 1 + INNER JOIN + bar bar ON + foo_bar.bar_id = bar.bar_id + INNER JOIN + bar_metadata bar_metadata ON + bar.bar_metadata_key = bar_metadata.bar_metadata_key + WHERE + foo.tenant_id =:tenantId""", sort)).endsWith("order by foo.age desc"); + + assertThat(createQueryFor(""" + select r + From DataRecord r + where + ( + r.adusrId = :userId + or EXISTS( select 1 FROM DataRecordDvsRight dr WHERE dr.adusrId = :userId AND dr.dataRecord = r ) + )""", sort)).endsWith("order by r.age desc"); + + assertThat(createQueryFor(""" + select distinct u + from FooBar u + where u.role = 'redacted' + and ( + not exists ( + from FooBarGroup group + where group in :excludedGroups + and group in elements(u.groups) + ) + )""", sort)).endsWith("order by u.age desc"); + + assertThat(createQueryFor(""" + SELECT i + FROM Item i + WHERE i.id IN ( + SELECT max(i2.id) FROM Item i2 + WHERE i2.field.id = :fieldId + GROUP BY i2.field.id, i2.version)""", sort)).endsWith("order by i.age desc"); + + assertThat(createQueryFor(""" + select + f.id, + ( + select timestamp from bar + where date(bar.timestamp) > '2022-05-21' + and bar.foo_id = f.id + order by date(bar.timestamp) desc + limit 1 + ) as timestamp + from foo f""", sort)).endsWith("order by f.age desc"); } @Test // GH-2862 @@ -836,6 +881,16 @@ where exists ( """, relationshipName, joinAlias, joinAlias)); } + static Stream queriesWithReservedWordsAsIdentifiers() { + + return Stream.of( // + Arguments.of("right", "rt"), // + Arguments.of("left", "lt"), // + Arguments.of("outer", "ou"), // + Arguments.of("full", "full"), // + Arguments.of("inner", "inr")); + } + @Test // GH-2508 void detectAliasWithCastCorrectly() { @@ -847,15 +902,6 @@ void detectAliasWithCastCorrectly() { .isEqualTo("u"); } - static Stream queriesWithReservedWordsAsIdentifiers() { - - return Stream.of( // - Arguments.of("right", "rt"), // - Arguments.of("left", "lt"), // - Arguments.of("outer", "ou"), // - Arguments.of("full", "full"), // - Arguments.of("inner", "inr")); - } private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java index 8ebcfa83e0..c9beede02f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java @@ -17,7 +17,6 @@ import static org.assertj.core.api.Assertions.*; -import java.util.regex.Pattern; import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; @@ -35,16 +34,13 @@ * {@link JpqlQueryParser}. * * @author Greg Turnquist - * @since 3.1 */ class JpqlQueryTransformerTests { private static final String QUERY = "select u from User u"; private static final String SIMPLE_QUERY = "select u from User u"; private static final String COUNT_QUERY = "select count(u) from User u"; - private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?1"; - private static final Pattern MULTI_WHITESPACE = Pattern.compile("\\s+"); @Test void applyingSortShouldIntroduceOrderByCriteriaWhereNoneExists() { @@ -201,17 +197,29 @@ void applySortingAccountsForNewlinesInSubselect() { Sort sort = Sort.by(Sort.Order.desc("age")); - assertThat(newParser("select u\n" + // - "from user u\n" + // - "where exists (select u2\n" + // - "from user u2\n" + // - ")\n" + // - "").applySorting(sort)).isEqualToIgnoringWhitespace("select u\n" + // - "from user u\n" + // - "where exists (select u2\n" + // - "from user u2\n" + // - ")\n" + // - " order by u.age desc"); + // + // + // + // + // + // + // + // + // + // + assertThat(newParser(""" + select u + from user u + where exists (select u2 + from user u2 + ) + """).applySorting(sort)).isEqualToIgnoringWhitespace(""" + select u + from user u + where exists (select u2 + from user u2 + ) + order by u.age desc"""); } @Test // GH-2563 @@ -345,12 +353,14 @@ void detectsConstructorExpressionInDistinctQuery() { @Test // DATAJPA-938 void detectsComplexConstructorExpression() { - assertThat(hasConstructorExpression("select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) " // - + "from Bar lp join lp.investmentProduct ip " // - + "where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId " - // - + "group by ip.id, ip.name, lp.accountId " // - + "order by ip.name ASC")).isTrue(); + // + assertThat(hasConstructorExpression( + """ + select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) + from Bar lp join lp.investmentProduct ip + where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId group by ip.id, ip.name, lp.accountId + order by ip.name ASC""")) + .isTrue(); } @Test // DATAJPA-938 @@ -475,21 +485,39 @@ void detectsAliasWithGroupAndOrderBy() { @Test // DATAJPA-1500 void createCountQuerySupportsWhitespaceCharacters() { - assertThat(createCountQueryFor("select user from User user\n" + // - " where user.age = 18\n" + // - " order by user.name\n ")).isEqualToIgnoringWhitespace("select count(user) from User user\n" + // - " where user.age = 18\n "); + // + // + // + assertThat(createCountQueryFor(""" + select user from User user + where user.age = 18 + order by user.name + """)).isEqualToIgnoringWhitespace(""" + select count(user) from User user + where user.age = 18 + """); } @Test void createCountQuerySupportsLineBreaksInSelectClause() { - assertThat(createCountQueryFor("select user.age,\n" + // - " user.name\n" + // - " from User user\n" + // - " where user.age = 18\n" + // - " order\nby\nuser.name\n ")).isEqualToIgnoringWhitespace("select count(user) from User user\n" + // - " where user.age = 18\n "); + // + // + // + // + // + assertThat(createCountQueryFor(""" + select user.age, + user.name + from User user + where user.age = 18 + order + by + user.name + """)).isEqualToIgnoringWhitespace(""" + select count(user) from User user + where user.age = 18 + """); } @Test // DATAJPA-1061 @@ -540,11 +568,24 @@ void appliesSortCorrectlyForSimpleField() { @Test void createCountQuerySupportsLineBreakRightAfterDistinct() { - assertThat(createCountQueryFor("select\ndistinct\nuser.age,\n" + // - "user.name\n" + // - "from\nUser\nuser")).isEqualTo(createCountQueryFor("select\ndistinct user.age,\n" + // - "user.name\n" + // - "from\nUser\nuser")); + // + // + // + // + assertThat(createCountQueryFor(""" + select + distinct + user.age, + user.name + from + User + user""")).isEqualTo(createCountQueryFor(""" + select + distinct user.age, + user.name + from + User + user""")); } @Test @@ -652,34 +693,37 @@ void orderByShouldWorkWithSubSelectStatements() { Sort sort = Sort.by(Sort.Order.desc("age")); - assertThat(createQueryFor("select r " // - + "From DataRecord r " // - + "where " // - + " ( " // - + " r.adusrId = :userId " // - + " or EXISTS( select 1 FROM DataRecordDvsRight dr WHERE dr.adusrId = :userId AND dr.dataRecord = r ) " // - + ")", sort)).endsWith("order by r.age desc"); - - assertThat(createQueryFor("select distinct u " // - + "from FooBar u " // - + "where u.role = 'redacted' " // - + "and (" // - + " not exists (" // - + " select g from FooBarGroup g " // - + " where g in :excludedGroups " // - + " )" // - + ")", sort)).endsWith("order by u.age desc"); - - assertThat(createQueryFor("SELECT i " // - + "FROM Item i " // - + "WHERE i.id IN ( " // - + "SELECT max(i2.id) FROM Item i2 " // - + "WHERE i2.field.id = :fieldId " // - + "GROUP BY i2.field.id, i2.version)", sort)).endsWith("order by i.age desc"); + assertThat(createQueryFor(""" + select r + From DataRecord r + where + ( + r.adusrId = :userId + or EXISTS( select 1 FROM DataRecordDvsRight dr WHERE dr.adusrId = :userId AND dr.dataRecord = r ) + )""", sort)).endsWith("order by r.age desc"); + + assertThat(createQueryFor(""" + select distinct u + from FooBar u + where u.role = 'redacted' + and ( + not exists ( + select g from FooBarGroup g + where g in :excludedGroups + ) + )""", sort)).endsWith("order by u.age desc"); + + assertThat(createQueryFor(""" + SELECT i + FROM Item i + WHERE i.id IN ( + SELECT max(i2.id) FROM Item i2 + WHERE i2.field.id = :fieldId + GROUP BY i2.field.id, i2.version)""", sort)).endsWith("order by i.age desc"); } @Test // GH-2074 - void queryParserPicksCorrectAliasAmidstMultipleAlises() { + void queryParserPicksCorrectAliasAmidstMultipleAliases() { assertThat(alias("select u from User as u left join u.roles as r")).isEqualTo("u"); } @@ -698,9 +742,9 @@ where exists ( join iu.roles u2r join u2r.role r join r.rights r2r - join r2r.inner inr + join r2r.%s %s where - inr.code = :rightCode + %s.code = :rightCode and iu = u ) and ct.id = :teamId From 1534c872652154e706eb7b0209819b005ad9173c Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 27 Mar 2023 14:01:46 -0500 Subject: [PATCH 347/821] Support functions that have semicolons as arguments. A native SQL function with ';' as an argument, e.g. listagg(a.b, ';'), will fail to find anything after it (like an alias). By adding ';' to the list of approved function tokens, queries with functional aliases will work properly. Resolves #2884. Original pull request: #2891 --- .../data/jpa/repository/query/QueryUtils.java | 2 +- .../repository/query/QueryUtilsUnitTests.java | 36 ++++++++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index f581f191ff..0d2c65072b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -186,7 +186,7 @@ public abstract class QueryUtils { builder = new StringBuilder(); // any function call including parameters within the brackets - builder.append("\\w+\\s*\\([\\w\\.,\\s'=:\\\\?]+\\)"); + builder.append("\\w+\\s*\\([\\w\\.,\\s'=:;\\\\?]+\\)"); // the potential alias builder.append("\\s+[as|AS]+\\s+(([\\w\\.]+))"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 2ee00f42a2..99dada4923 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -15,14 +15,8 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.data.jpa.repository.query.QueryUtils.applySorting; -import static org.springframework.data.jpa.repository.query.QueryUtils.createCountQueryFor; -import static org.springframework.data.jpa.repository.query.QueryUtils.detectAlias; -import static org.springframework.data.jpa.repository.query.QueryUtils.getOuterJoinAliases; -import static org.springframework.data.jpa.repository.query.QueryUtils.hasConstructorExpression; -import static org.springframework.data.jpa.repository.query.QueryUtils.removeSubqueries; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jpa.repository.query.QueryUtils.*; import java.util.Collections; import java.util.Set; @@ -891,4 +885,30 @@ void orderByShouldWorkWithSubSelectStatements() { + ") as timestamp\n" // + "from foo f", sort)).endsWith("order by f.age desc"); } + + @Test // GH-2884 + void functionAliasShouldSupportArgumentsWithCommasOrArgumentsWithSemiColons() { + + assertThat(QueryUtils.getFunctionAliases(""" + select s.id as id, s.name as name, gp.points + from specialist s + left join ( + select q.specialist_id, listagg(q.points, ',') as points + from qualification q + group by q.specialist_id + ) gp on gp.specialist_id = s.id + where name like :name + """)).containsExactly("points"); + + assertThat(QueryUtils.getFunctionAliases(""" + select s.id as id, s.name as name, gp.points + from specialist s + left join ( + select q.specialist_id, listagg(q.points, ';') as points + from qualification q + group by q.specialist_id + ) gp on gp.specialist_id = s.id + where name like :name + """)).containsExactly("points"); + } } From 29467ef1adac2a32239e329660ae65e7338ee4e3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 28 Mar 2023 09:51:12 +0200 Subject: [PATCH 348/821] Polishing. Simplify tests. See #2884 Original pull request: #2891 --- .../HqlParserQueryEnhancerUnitTests.java | 19 +- .../JpqlParserQueryEnhancerUnitTests.java | 22 +- .../repository/query/QueryUtilsUnitTests.java | 277 ++++++++++-------- 3 files changed, 158 insertions(+), 160 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java index aefc1a2ca9..c572a27684 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserQueryEnhancerUnitTests.java @@ -17,7 +17,6 @@ import static org.assertj.core.api.Assumptions.*; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -25,14 +24,14 @@ * TCK Tests for {@link HqlQueryParser} mixed into {@link JpaQueryEnhancer}. * * @author Greg Turnquist - * @since 3.1 */ public class HqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { - public static final String HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES = "HqlParser does not support native queries"; - @Override QueryEnhancer createQueryEnhancer(DeclaredQuery query) { + + assumeThat(query.isNativeQuery()).isFalse(); + return JpaQueryEnhancer.forHql(query); } @@ -50,16 +49,4 @@ void shouldDeriveJpqlCountQuery(String query, String expected) { super.shouldDeriveJpqlCountQuery(query, expected); } - @Disabled(HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) - @Override - void findProjectionClauseWithIncludedFrom() {} - - @Disabled(HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) - @Override - void shouldDeriveNativeCountQuery(String query, String expected) {} - - @Disabled(HQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) - @Override - void shouldDeriveNativeCountQueryWithVariable(String query, String expected) {} - } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java index c16fcd9ce0..3efca4ad40 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlParserQueryEnhancerUnitTests.java @@ -17,7 +17,6 @@ import static org.assertj.core.api.Assumptions.*; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -25,15 +24,15 @@ * TCK Tests for {@link JpqlQueryParser} mixed into {@link JpaQueryEnhancer}. * * @author Greg Turnquist - * @since 3.1 */ public class JpqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { - public static final String JPQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES = "JpqlParser does not support native queries"; - @Override - QueryEnhancer createQueryEnhancer(DeclaredQuery declaredQuery) { - return JpaQueryEnhancer.forJpql(declaredQuery); + QueryEnhancer createQueryEnhancer(DeclaredQuery query) { + + assumeThat(query.isNativeQuery()).isFalse(); + + return JpaQueryEnhancer.forJpql(query); } @Override @@ -53,15 +52,4 @@ void shouldDeriveJpqlCountQuery(String query, String expected) { super.shouldDeriveJpqlCountQuery(query, expected); } - @Disabled(JPQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) - @Override - void findProjectionClauseWithIncludedFrom() {} - - @Disabled(JPQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) - @Override - void shouldDeriveNativeCountQuery(String query, String expected) {} - - @Disabled(JPQL_PARSER_DOES_NOT_SUPPORT_NATIVE_QUERIES) - @Override - void shouldDeriveNativeCountQueryWithVariable(String query, String expected) {} } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java index 99dada4923..4a31beb5a4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsUnitTests.java @@ -25,6 +25,8 @@ import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @@ -183,47 +185,52 @@ void testRemoveSubqueries() throws Exception { @Test // GH-2581 void testRemoveMultilineSubqueries() { - assertThat(normalizeWhitespace(removeSubqueries("select u from User u\n" // - + " where not exists (\n" // - + " from User u2\n" // - + " )"))).isEqualTo("select u from User u where not exists"); - - assertThat(normalizeWhitespace(removeSubqueries("(\n" // - + " select u from User u \n" // - + " where not exists (\n" // - + " from User u2\n" // - + " )\n" // - + ")"))).isEqualTo("( select u from User u where not exists )"); - - assertThat(normalizeWhitespace(removeSubqueries("select u from User u \n" // - + " where not exists (\n" // - + " from User u2 \n" // - + " where not exists (\n" // - + " from User u3\n" // - + " )\n" // - + " )"))).isEqualTo("select u from User u where not exists"); - - assertThat(normalizeWhitespace(removeSubqueries("select u from User u \n" // - + " where not exists (\n" // - + " (\n" // - + " from User u2 \n" // - + " where not exists (\n" // - + " from User u3\n" // - + " )\n" // - + " )\n" // - + " )"))).isEqualTo("select u from User u where not exists ( )"); - - assertThat(normalizeWhitespace(removeSubqueries("(\n" // - + " select u from User u \n" // - + " where not exists (\n" // - + " (\n" // - + " from User u2 \n" // - + " where not exists (\n" // - + " from User u3\n" // - + " )\n" // - + " )\n" // - + " )\n" // - + ")"))).isEqualTo("( select u from User u where not exists ( ) )"); + assertThat(normalizeWhitespace(removeSubqueries(""" + select u from User u + where not exists ( + from User u2 + )"""))).isEqualTo("select u from User u where not exists"); + + assertThat(normalizeWhitespace(removeSubqueries(""" + ( + select u from User u\s + where not exists ( + from User u2 + ) + )"""))).isEqualTo("( select u from User u where not exists )"); + + assertThat(normalizeWhitespace(removeSubqueries(""" + select u from User u\s + where not exists ( + from User u2\s + where not exists ( + from User u3 + ) + )"""))).isEqualTo("select u from User u where not exists"); + + assertThat(normalizeWhitespace(removeSubqueries(""" + select u from User u\s + where not exists ( + ( + from User u2\s + where not exists ( + from User u3 + ) + ) + )"""))).isEqualTo("select u from User u where not exists ( )"); + + assertThat(normalizeWhitespace(removeSubqueries(""" + ( + select u from User u\s + where not exists ( + ( + from User u2\s + where not exists ( + from User u3 + ) + ) + ) + )"""))).isEqualTo("( select u from User u where not exists ( ) )"); } @Test // GH-2557 @@ -231,28 +238,31 @@ void applySortingAccountsForNewlinesInSubselect() { Sort sort = Sort.by(Order.desc("age")); - assertThat(QueryUtils.applySorting("select u\n" + // - "from user u\n" + // - "where exists (select u2\n" + // - "from user u2\n" + // - ")\n" + // - "", sort)).isEqualTo("select u\n" + // - "from user u\n" + // - "where exists (select u2\n" + // - "from user u2\n" + // - ")\n" + // - " order by u.age desc"); + assertThat(QueryUtils.applySorting(""" + select u + from user u + where exists (select u2 + from user u2 + ) + """, sort)).isEqualTo(""" + select u + from user u + where exists (select u2 + from user u2 + ) + order by u.age desc"""); } @Test // GH-2563 void aliasDetectionProperlyHandlesNewlinesInSubselects() { - assertThat(detectAlias("SELECT o\n" + // - "FROM Order o\n" + // - "AND EXISTS(SELECT 1\n" + // - "FROM Vehicle vehicle\n" + // - "WHERE vehicle.vehicleOrderId = o.id\n" + // - "AND LOWER(COALESCE(vehicle.make, '')) LIKE :query)")).isEqualTo("o"); + assertThat(detectAlias(""" + SELECT o + FROM Order o + AND EXISTS(SELECT 1 + FROM Vehicle vehicle + WHERE vehicle.vehicleOrderId = o.id + AND LOWER(COALESCE(vehicle.make, '')) LIKE :query)""")).isEqualTo("o"); } private String normalizeWhitespace(String s) { @@ -567,10 +577,14 @@ void detectsAliasWithGroupAndOrderBy() { @Test // DATAJPA-1500 void createCountQuerySupportsWhitespaceCharacters() { - assertThat(createCountQueryFor("select * from User user\n" + // - " where user.age = 18\n" + // - " order by user.name\n ")).isEqualTo("select count(user) from User user\n" + // - " where user.age = 18\n "); + assertThat(createCountQueryFor(""" + select * from User user + where user.age = 18 + order by user.name + \s""")).isEqualTo(""" + select count(user) from User user + where user.age = 18 + \s"""); } @Test // GH-2341 @@ -581,12 +595,18 @@ void createCountQueryStarCharacterConverted() { @Test void createCountQuerySupportsLineBreaksInSelectClause() { - assertThat(createCountQueryFor("select user.age,\n" + // - " user.name\n" + // - " from User user\n" + // - " where user.age = 18\n" + // - " order\nby\nuser.name\n ")).isEqualTo("select count(user) from User user\n" + // - " where user.age = 18\n "); + assertThat(createCountQueryFor(""" + select user.age, + user.name + from User user + where user.age = 18 + order + by + user.name + \s""")).isEqualTo(""" + select count(user) from User user + where user.age = 18 + \s"""); } @Test // DATAJPA-1061 @@ -637,11 +657,20 @@ void appliesSortCorrectlyForSimpleField() { @Test void createCountQuerySupportsLineBreakRightAfterDistinct() { - assertThat(createCountQueryFor("select\ndistinct\nuser.age,\n" + // - "user.name\n" + // - "from\nUser\nuser")).isEqualTo(createCountQueryFor("select\ndistinct user.age,\n" + // - "user.name\n" + // - "from\nUser\nuser")); + assertThat(createCountQueryFor(""" + select + distinct + user.age, + user.name + from + User + user""")).isEqualTo(createCountQueryFor(""" + select + distinct user.age, + user.name + from + User + user""")); } @Test @@ -818,34 +847,37 @@ void orderByShouldWorkWithSubSelectStatements() { Sort sort = Sort.by(Order.desc("age")); - assertThat(QueryUtils.applySorting("SELECT\n" // - + " foo_bar.*\n" // - + "FROM\n" // - + " foo foo\n" // - + "INNER JOIN\n" // - + " foo_bar_dnrmv foo_bar ON\n" // - + " foo_bar.foo_id = foo.foo_id\n" // - + "INNER JOIN\n" // - + " (\n" // - + " SELECT\n" // - + " foo_bar_action.*,\n" // - + " RANK() OVER (PARTITION BY \"foo_bar_action\".attributes->>'baz' ORDER BY \"foo_bar_action\".attributes->>'qux' DESC) AS ranking\n" // - + " FROM\n" // - + " foo_bar_action\n" // - + " WHERE\n" // - + " foo_bar_action.deleted_ts IS NULL)\n" // - + " foo_bar_action ON\n" // - + " foo_bar.foo_bar_id = foo_bar_action.foo_bar_id\n" // - + " AND ranking = 1\n" // - + "INNER JOIN\n" // - + " bar bar ON\n" // - + " foo_bar.bar_id = bar.bar_id\n" // - + "INNER JOIN\n" // - + " bar_metadata bar_metadata ON\n" // - + " bar.bar_metadata_key = bar_metadata.bar_metadata_key\n" // - + "WHERE\n" // - + " foo.tenant_id =:tenantId\n" // - + "AND (foo.attributes ->> :serialNum IN (:serialNumValue))", sort)).endsWith("order by foo.age desc"); + assertThat(QueryUtils.applySorting( + """ + SELECT + foo_bar.* + FROM + foo foo + INNER JOIN + foo_bar_dnrmv foo_bar ON + foo_bar.foo_id = foo.foo_id + INNER JOIN + ( + SELECT + foo_bar_action.*, + RANK() OVER (PARTITION BY "foo_bar_action".attributes->>'baz' ORDER BY "foo_bar_action".attributes->>'qux' DESC) AS ranking + FROM + foo_bar_action + WHERE + foo_bar_action.deleted_ts IS NULL) + foo_bar_action ON + foo_bar.foo_bar_id = foo_bar_action.foo_bar_id + AND ranking = 1 + INNER JOIN + bar bar ON + foo_bar.bar_id = bar.bar_id + INNER JOIN + bar_metadata bar_metadata ON + bar.bar_metadata_key = bar_metadata.bar_metadata_key + WHERE + foo.tenant_id =:tenantId + AND (foo.attributes ->> :serialNum IN (:serialNumValue))""", + sort)).endsWith("order by foo.age desc"); assertThat(QueryUtils.applySorting("select r " // + "From DataRecord r " // @@ -874,41 +906,32 @@ void orderByShouldWorkWithSubSelectStatements() { + "+ \"WHERE i2.field.id = :fieldId \" " // + "+ \"GROUP BY i2.field.id, i2.version)", sort)).endsWith("order by i.age desc"); - assertThat(QueryUtils.applySorting("select \n" // - + " f.id,\n" // - + " (\n" // - + " select timestamp from bar\n" // - + " where date(bar.timestamp) > '2022-05-21'\n" // - + " and bar.foo_id = f.id \n" // - + " order by date(bar.timestamp) desc\n" // - + " limit 1\n" // - + ") as timestamp\n" // - + "from foo f", sort)).endsWith("order by f.age desc"); + assertThat(QueryUtils.applySorting(""" + select\s + f.id, + ( + select timestamp from bar + where date(bar.timestamp) > '2022-05-21' + and bar.foo_id = f.id\s + order by date(bar.timestamp) desc + limit 1 + ) as timestamp + from foo f""", sort)).endsWith("order by f.age desc"); } - @Test // GH-2884 - void functionAliasShouldSupportArgumentsWithCommasOrArgumentsWithSemiColons() { - - assertThat(QueryUtils.getFunctionAliases(""" - select s.id as id, s.name as name, gp.points - from specialist s - left join ( - select q.specialist_id, listagg(q.points, ',') as points - from qualification q - group by q.specialist_id - ) gp on gp.specialist_id = s.id - where name like :name - """)).containsExactly("points"); + @ParameterizedTest // GH-2884 + @ValueSource(strings = { ",", ";" }) + void functionAliasShouldSupportArgumentsWithCommasOrArgumentsWithSemiColons(String arg) { - assertThat(QueryUtils.getFunctionAliases(""" + assertThat(QueryUtils.getFunctionAliases(String.format(""" select s.id as id, s.name as name, gp.points from specialist s left join ( - select q.specialist_id, listagg(q.points, ';') as points + select q.specialist_id, listagg(q.points, '%s') as points from qualification q group by q.specialist_id ) gp on gp.specialist_id = s.id where name like :name - """)).containsExactly("points"); + """, arg))).containsExactly("points"); } } From 8121a5429d4edb0e4b99f34c5204c4fe5f5d281b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 28 Mar 2023 13:58:10 -0500 Subject: [PATCH 349/821] Update CI properties. See #2873 --- ci/pipeline.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 5b54445231..2f45263dbc 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,8 +1,10 @@ # Java versions java.main.tag=17.0.6_10-jdk-focal +java.next.tag=20-jdk-jammy # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} +docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag} # Supported versions of MongoDB docker.mongodb.4.4.version=4.4.18 From e300bd89dd6cfa98a81969db8944a5ab9250083b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 22 Mar 2023 10:24:54 -0500 Subject: [PATCH 350/821] Drop FETCH from JOIN clauses when creating a count query in Hibernate. Resolves #2348. Supercedes #2686. --- .../repository/query/HqlQueryTransformer.java | 23 +++++++++++++++++++ .../query/HqlQueryTransformerTests.java | 10 ++++++++ 2 files changed, 33 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java index 1b45d4e6a4..906942f75f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java @@ -276,6 +276,29 @@ public List visitFromRoot(HqlParser.FromRootContext ctx) { return tokens; } + @Override + public List visitJoin(HqlParser.JoinContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.joinType())); + tokens.add(new JpaQueryParsingToken(ctx.JOIN())); + + if (!countQuery) { + if (ctx.FETCH() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FETCH())); + } + } + + tokens.addAll(visit(ctx.joinTarget())); + + if (ctx.joinRestriction() != null) { + tokens.addAll(visit(ctx.joinRestriction())); + } + + return tokens; + } + @Override public List visitAlias(HqlParser.AliasContext ctx) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index df1e8df656..6d33af2fe0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -903,6 +903,16 @@ void detectAliasWithCastCorrectly() { } + @Test // GH-2348 + void removeFetchFromJoinsDuringCountQueryCreation() { + + assertCountQuery("select u from User u left outer join fetch u.roles r left outer JOIN FETCH u.accounts a", + "select count(u) from User u left outer join u.roles r left outer JOIN u.accounts a"); + + assertCountQuery("SELECT DISTINCT b FROM Board b LEFT JOIN FETCH b.comments ORDER BY b.id", + "SELECT count(DISTINCT b) FROM Board b LEFT JOIN b.comments"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 1b5dabb11cdafdf7d565ac2bcdaa011747d36668 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 22 Mar 2023 10:39:42 +0100 Subject: [PATCH 351/821] Support for Keyset scrolling. Closes #2878 Original pull request #2885 See https://github.com/spring-projects/spring-data-commons/issues/2151 --- .../jpa/repository/query/CollectionUtils.java | 61 ++++++ .../query/JpaKeysetScrollQueryCreator.java | 83 +++++++ .../jpa/repository/query/JpaQueryCreator.java | 8 +- .../repository/query/JpaQueryExecution.java | 39 +++- .../jpa/repository/query/JpaQueryFactory.java | 10 + .../query/KeysetScrollDelegate.java | 197 +++++++++++++++++ .../query/KeysetScrollSpecification.java | 128 +++++++++++ .../data/jpa/repository/query/NamedQuery.java | 4 + .../repository/query/PartTreeJpaQuery.java | 39 +++- .../jpa/repository/query/ScrollDelegate.java | 105 +++++++++ .../FetchableFluentQueryByExample.java | 206 ------------------ .../FetchableFluentQueryByPredicate.java | 79 +++++-- .../FetchableFluentQueryBySpecification.java | 76 +++++-- .../support/FluentQuerySupport.java | 14 +- .../support/JpaEntityInformation.java | 15 +- .../JpaMetamodelEntityInformation.java | 31 ++- .../data/jpa/repository/support/Querydsl.java | 29 ++- .../support/QuerydslJpaPredicateExecutor.java | 79 ++++++- .../support/SimpleJpaRepository.java | 57 +++-- .../data/jpa/domain/sample/Item.java | 21 ++ .../RepositoryWithIdClassKeyTests.java | 28 ++- .../jpa/repository/UserRepositoryTests.java | 189 +++++++++++++++- .../query/CollectionUtilsUnitTests.java | 46 ++++ .../jpa/repository/sample/ItemRepository.java | 3 +- .../jpa/repository/sample/UserRepository.java | 5 + ...etchableFluentQueryByExampleUnitTests.java | 43 ---- ...chableFluentQueryByPredicateUnitTests.java | 4 +- .../JpaEntityInformationSupportUnitTests.java | 15 +- src/main/asciidoc/index.adoc | 3 +- src/main/asciidoc/jpa.adoc | 31 ++- 30 files changed, 1311 insertions(+), 337 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/CollectionUtils.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java delete mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CollectionUtilsUnitTests.java delete mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/CollectionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/CollectionUtils.java new file mode 100644 index 0000000000..582d30eb1d --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/CollectionUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.List; + +/** + * Utility methods to obtain sublists. + * + * @author Mark Paluch + */ +class CollectionUtils { + + /** + * Return the first {@code count} items from the list. + * + * @param count the number of first elements to be included in the returned list. + * @param list must not be {@literal null} + * @return the returned sublist if the {@code list} is greater {@code count}. + * @param + */ + public static List getFirst(int count, List list) { + + if (count > 0 && list.size() > count) { + return list.subList(0, count); + } + + return list; + } + + /** + * Return the last {@code count} items from the list. + * + * @param count the number of last elements to be included in the returned list. + * @param list must not be {@literal null} + * @return the returned sublist if the {@code list} is greater {@code count}. + * @param + */ + public static List getLast(int count, List list) { + + if (count > 0 && list.size() > count) { + return list.subList(list.size() - (count), list.size()); + } + + return list; + } + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java new file mode 100644 index 0000000000..5dbf036b4b --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java @@ -0,0 +1,83 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.support.JpaEntityInformation; +import org.springframework.data.repository.query.ReturnedType; +import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.lang.Nullable; + +/** + * Extension to {@link JpaQueryCreator} to create queries considering {@link KeysetScrollPosition keyset scrolling}. + * + * @author Mark Paluch + * @since 3.1 + */ +class JpaKeysetScrollQueryCreator extends JpaQueryCreator { + + private final JpaEntityInformation entityInformation; + private final KeysetScrollPosition scrollPosition; + + public JpaKeysetScrollQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder, + ParameterMetadataProvider provider, JpaEntityInformation entityInformation, + KeysetScrollPosition scrollPosition) { + super(tree, type, builder, provider); + this.entityInformation = entityInformation; + this.scrollPosition = scrollPosition; + } + + @Override + protected CriteriaQuery complete(@Nullable Predicate predicate, Sort sort, CriteriaQuery query, + CriteriaBuilder builder, Root root) { + + KeysetScrollSpecification keysetSpec = new KeysetScrollSpecification<>(scrollPosition, sort, + entityInformation); + Predicate keysetPredicate = keysetSpec.createPredicate(root, builder); + + CriteriaQuery queryToUse = super.complete(predicate, keysetSpec.sort(), query, builder, root); + + if (keysetPredicate != null) { + if (queryToUse.getRestriction() != null) { + return queryToUse.where(builder.and(queryToUse.getRestriction(), keysetPredicate)); + } + return queryToUse.where(keysetPredicate); + } + + return queryToUse; + } + + @Override + Collection getRequiredSelection(Sort sort, ReturnedType returnedType) { + + Sort sortToUse = KeysetScrollSpecification.createSort(scrollPosition, sort, entityInformation); + + Set selection = new LinkedHashSet<>(returnedType.getInputProperties()); + sortToUse.forEach(it -> selection.add(it.getProperty())); + + return selection; + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index b6def0ee40..431d1b7111 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -118,7 +118,6 @@ public List> getParameterExpressions() { @Override protected Predicate create(Part part, Iterator iterator) { - return toPredicate(part, root); } @@ -158,9 +157,10 @@ protected CriteriaQuery complete(@Nullable Predicate predicate if (returnedType.needsCustomConstruction()) { + Collection requiredSelection = getRequiredSelection(sort, returnedType); List> selections = new ArrayList<>(); - for (String property : returnedType.getInputProperties()) { + for (String property : requiredSelection) { PropertyPath path = PropertyPath.from(property, returnedType.getDomainType()); selections.add(toExpressionRecursively(root, path, true).alias(property)); @@ -195,6 +195,10 @@ protected CriteriaQuery complete(@Nullable Predicate predicate return predicate == null ? select : select.where(predicate); } + Collection getRequiredSelection(Sort sort, ReturnedType returnedType) { + return returnedType.getInputProperties(); + } + /** * Creates a {@link Predicate} from the given {@link Part}. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 6ee5ec163a..618ba586c2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -15,23 +15,25 @@ */ package org.springframework.data.jpa.repository.query; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.List; -import java.util.Optional; - import jakarta.persistence.EntityManager; import jakarta.persistence.NoResultException; import jakarta.persistence.Query; import jakarta.persistence.StoredProcedureQuery; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor; import org.springframework.data.support.PageableExecutionUtils; @@ -128,6 +130,33 @@ protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccesso } } + /** + * Executes the query to return a {@link org.springframework.data.domain.Window} of entities. + * + * @author Mark Paluch + * @since 3.1 + */ + static class ScrollExecution extends JpaQueryExecution { + + private final Sort sort; + private final ScrollDelegate delegate; + + ScrollExecution(Sort sort, ScrollDelegate delegate) { + this.sort = sort; + this.delegate = delegate; + } + + @Override + @SuppressWarnings("unchecked") + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { + + ScrollPosition scrollPosition = accessor.getScrollPosition(); + Query scrollQuery = query.createQuery(accessor); + + return delegate.scroll(scrollQuery, sort.and(accessor.getSort()), scrollPosition); + } + } + /** * Executes the query to return a {@link Slice} of entities. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 8a91b5ea5c..ef95696cb6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -18,6 +18,7 @@ import jakarta.persistence.EntityManager; import org.springframework.data.jpa.repository.QueryRewriter; +import org.springframework.data.repository.query.QueryCreationException; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -49,6 +50,10 @@ AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager @Nullable String countQueryString, QueryRewriter queryRewriter, QueryMethodEvaluationContextProvider evaluationContextProvider) { + if (method.isScrollQuery()) { + throw QueryCreationException.create(method, "Scroll queries are not supported using String-based queries"); + } + return method.isNativeQuery() ? new NativeJpaQuery(method, em, queryString, countQueryString, queryRewriter, evaluationContextProvider, PARSER) @@ -64,6 +69,11 @@ AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager * @return */ public StoredProcedureJpaQuery fromProcedureAnnotation(JpaQueryMethod method, EntityManager em) { + + if (method.isScrollQuery()) { + throw QueryCreationException.create(method, "Scroll queries are not supported using stored procedures"); + } + return new StoredProcedureJpaQuery(method, em); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java new file mode 100644 index 0000000000..0ce89ff114 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java @@ -0,0 +1,197 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.KeysetScrollPosition.Direction; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.lang.Nullable; + +/** + * Delegate for keyset scrolling. + * + * @author Mark Paluch + * @since 3.1 + */ +public class KeysetScrollDelegate { + + private static final KeysetScrollDelegate forward = new KeysetScrollDelegate(); + private static final KeysetScrollDelegate reverse = new ReverseKeysetScrollDelegate(); + + /** + * Factory method to obtain the right {@link KeysetScrollDelegate}. + * + * @param direction + * @return + */ + public static KeysetScrollDelegate of(Direction direction) { + return direction == Direction.Forward ? forward : reverse; + } + + @Nullable + public P createPredicate(KeysetScrollPosition keyset, Sort sort, QueryStrategy strategy) { + + Map keysetValues = keyset.getKeys(); + + // first query doesn't come with a keyset + if (keysetValues.isEmpty()) { + return null; + } + + List

    or = new ArrayList<>(); + int i = 0; + + // progressive query building to reconstruct a query matching sorting rules + for (Order order : sort) { + + if (!keysetValues.containsKey(order.getProperty())) { + throw new IllegalStateException(String + .format("KeysetScrollPosition does not contain all keyset values. Missing key: %s", order.getProperty())); + } + + List

    sortConstraint = new ArrayList<>(); + + int j = 0; + for (Order inner : sort) { + + E propertyExpression = strategy.createExpression(inner.getProperty()); + Object o = keysetValues.get(inner.getProperty()); + + if (j >= i) { // tail segment + + sortConstraint.add(strategy.compare(inner, propertyExpression, o)); + break; + } + + sortConstraint.add(strategy.compare(propertyExpression, o)); + j++; + } + + if (!sortConstraint.isEmpty()) { + or.add(strategy.and(sortConstraint)); + } + + i++; + } + + if (or.isEmpty()) { + return null; + } + + return strategy.or(or); + } + + protected Sort getSortOrders(Sort sort) { + return sort; + } + + @SuppressWarnings("unchecked") + protected List postProcessResults(List result) { + return result; + } + + protected List getResultWindow(List list, int limit) { + return CollectionUtils.getFirst(limit, list); + } + + /** + * Reverse scrolling variant applying {@link Direction#Backward}. In reverse scrolling, we need to flip directions for + * the actual query so that we do not get everything from the top position and apply the limit but rather flip the + * sort direction, apply the limit and then reverse the result to restore the actual sort order. + */ + private static class ReverseKeysetScrollDelegate extends KeysetScrollDelegate { + + protected Sort getSortOrders(Sort sort) { + + List orders = new ArrayList<>(); + for (Order order : sort) { + orders.add(new Order(order.isAscending() ? Sort.Direction.DESC : Sort.Direction.ASC, order.getProperty())); + } + + return Sort.by(orders); + } + + @Override + protected List postProcessResults(List result) { + Collections.reverse(result); + return result; + } + + @Override + protected List getResultWindow(List list, int limit) { + return CollectionUtils.getLast(limit, list); + } + } + + /** + * Adapter to construct scroll queries. + * + * @param property path expression type. + * @param

    predicate type. + */ + public interface QueryStrategy { + + /** + * Create an expression object from the given {@code property} path. + * + * @param property must not be {@literal null}. + * @return + */ + E createExpression(String property); + + /** + * Create a comparison object according to the {@link Order}. + * + * @param order must not be {@literal null}. + * @param propertyExpression must not be {@literal null}. + * @param value + * @return + */ + P compare(Order order, E propertyExpression, Object value); + + /** + * Create an equals-comparison object. + * + * @param propertyExpression must not be {@literal null}. + * @param value + * @return + */ + P compare(E propertyExpression, @Nullable Object value); + + /** + * AND-combine the {@code intermediate} predicates. + * + * @param intermediate + * @return + */ + P and(List

    intermediate); + + /** + * OR-combine the {@code intermediate} predicates. + * + * @param intermediate + * @return + */ + P or(List

    intermediate); + } + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java new file mode 100644 index 0000000000..cfec4f06df --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java @@ -0,0 +1,128 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.From; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; + +import java.util.List; + +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.query.KeysetScrollDelegate.QueryStrategy; +import org.springframework.data.jpa.repository.support.JpaEntityInformation; +import org.springframework.data.mapping.PropertyPath; +import org.springframework.lang.Nullable; + +/** + * {@link Specification} to create scroll queries using keyset-scrolling. + * + * @author Mark Paluch + * @author Christoph Strobl + * @since 3.1 + */ +public record KeysetScrollSpecification (KeysetScrollPosition position, Sort sort, + JpaEntityInformation entity) implements Specification { + + public KeysetScrollSpecification(KeysetScrollPosition position, Sort sort, JpaEntityInformation entity) { + + this.position = position; + this.entity = entity; + this.sort = createSort(position, sort, entity); + } + + /** + * Create a {@link Sort} object to be used with the actual query. + * + * @param position must not be {@literal null}. + * @param sort must not be {@literal null}. + * @param entity must not be {@literal null}. + * @return + */ + public static Sort createSort(KeysetScrollPosition position, Sort sort, JpaEntityInformation entity) { + + KeysetScrollDelegate delegate = KeysetScrollDelegate.of(position.getDirection()); + + Sort sortToUse; + if (entity.hasCompositeId()) { + sortToUse = sort.and(Sort.by(entity.getIdAttributeNames().toArray(new String[0]))); + } else { + sortToUse = sort.and(Sort.by(entity.getRequiredIdAttribute().getName())); + } + + return delegate.getSortOrders(sortToUse); + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) { + return createPredicate(root, criteriaBuilder); + } + + @Nullable + public Predicate createPredicate(Root root, CriteriaBuilder criteriaBuilder) { + + KeysetScrollDelegate delegate = KeysetScrollDelegate.of(position.getDirection()); + return delegate.createPredicate(position, sort, new JpaQueryStrategy(root, criteriaBuilder)); + } + + @SuppressWarnings("rawtypes") + private static class JpaQueryStrategy implements QueryStrategy, Predicate> { + + private final From from; + private final CriteriaBuilder cb; + + public JpaQueryStrategy(From from, CriteriaBuilder cb) { + + this.from = from; + this.cb = cb; + } + + @Override + public Expression createExpression(String property) { + + PropertyPath path = PropertyPath.from(property, from.getJavaType()); + return QueryUtils.toExpressionRecursively(from, path); + } + + @Override + public Predicate compare(Order order, Expression propertyExpression, Object value) { + + return order.isAscending() ? cb.greaterThan(propertyExpression, (Comparable) value) + : cb.lessThan(propertyExpression, (Comparable) value); + } + + @Override + public Predicate compare(Expression propertyExpression, @Nullable Object value) { + return value == null ? cb.isNull(propertyExpression) : cb.equal(propertyExpression, value); + } + + @Override + public Predicate and(List intermediate) { + return cb.and(intermediate.toArray(new Predicate[0])); + } + + @Override + public Predicate or(List intermediate) { + return cb.or(intermediate.toArray(new Predicate[0])); + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 45442a4c49..311f47eafd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -145,6 +145,10 @@ public static RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em return null; } + if (method.isScrollQuery()) { + throw QueryCreationException.create(method, "Scroll queries are not supported using String-based queries"); + } + try { RepositoryQuery query = new NamedQuery(method, em); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 8bfbb72a49..78cfde34a5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -15,19 +15,25 @@ */ package org.springframework.data.jpa.repository.query; -import java.util.List; - import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceUnitUtil; import jakarta.persistence.Query; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; +import java.util.List; + +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.OffsetScrollPosition; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.jpa.repository.query.JpaQueryExecution.DeleteExecution; import org.springframework.data.jpa.repository.query.JpaQueryExecution.ExistsExecution; +import org.springframework.data.jpa.repository.query.JpaQueryExecution.ScrollExecution; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; +import org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.parser.Part; @@ -55,6 +61,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { private final QueryPreparer countQuery; private final EntityManager em; private final EscapeCharacter escape; + private final JpaMetamodelEntityInformation entityInformation; /** * Creates a new {@link PartTreeJpaQuery}. @@ -79,10 +86,14 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { this.em = em; this.escape = escape; - Class domainClass = method.getEntityInformation().getJavaType(); this.parameters = method.getParameters(); - boolean recreationRequired = parameters.hasDynamicProjection() || parameters.potentiallySortsDynamically(); + Class domainClass = method.getEntityInformation().getJavaType(); + PersistenceUnitUtil persistenceUnitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil(); + this.entityInformation = new JpaMetamodelEntityInformation<>(domainClass, em.getMetamodel(), persistenceUnitUtil); + + boolean recreationRequired = parameters.hasDynamicProjection() || parameters.potentiallySortsDynamically() + || method.isScrollQuery(); try { @@ -111,7 +122,9 @@ public TypedQuery doCreateCountQuery(JpaParametersParameterAccessor access @Override protected JpaQueryExecution getExecution() { - if (this.tree.isDelete()) { + if (this.getQueryMethod().isScrollQuery()) { + return new ScrollExecution(this.tree.getSort(), new ScrollDelegate<>(entityInformation)); + } else if (this.tree.isDelete()) { return new DeleteExecution(em); } else if (this.tree.isExistsProjection()) { return new ExistsExecution(); @@ -228,7 +241,11 @@ public Query createQuery(JpaParametersParameterAccessor accessor) { TypedQuery query = createQuery(criteriaQuery); - return restrictMaxResultsIfNecessary(invokeBinding(parameterBinder, query, accessor, this.metadataCache)); + ScrollPosition scrollPosition = accessor.getParameters().hasScrollPositionParameter() + ? accessor.getScrollPosition() + : null; + return restrictMaxResultsIfNecessary(invokeBinding(parameterBinder, query, accessor, this.metadataCache), + scrollPosition); } /** @@ -236,10 +253,14 @@ public Query createQuery(JpaParametersParameterAccessor accessor) { * limited. */ @SuppressWarnings("ConstantConditions") - private Query restrictMaxResultsIfNecessary(Query query) { + private Query restrictMaxResultsIfNecessary(Query query, @Nullable ScrollPosition scrollPosition) { if (tree.isLimiting()) { + if (scrollPosition instanceof OffsetScrollPosition offset) { + query.setFirstResult(Math.toIntExact(offset.getOffset())); + } + if (query.getMaxResults() != Integer.MAX_VALUE) { /* * In order to return the correct results, we have to adjust the first result offset to be returned if: @@ -298,6 +319,10 @@ protected JpaQueryCreator createCreator(@Nullable JpaParametersParameterAccessor returnedType = processor.getReturnedType(); } + if (accessor != null && accessor.getScrollPosition()instanceof KeysetScrollPosition keyset) { + return new JpaKeysetScrollQueryCreator(tree, returnedType, builder, provider, entityInformation, keyset); + } + return new JpaQueryCreator(tree, returnedType, builder, provider); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java new file mode 100644 index 0000000000..94cc960a9b --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java @@ -0,0 +1,105 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import jakarta.persistence.Query; + +import java.util.List; +import java.util.Map; +import java.util.function.IntFunction; + +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.KeysetScrollPosition.Direction; +import org.springframework.data.domain.OffsetScrollPosition; +import org.springframework.data.domain.ScrollPosition; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.data.domain.Window; +import org.springframework.data.jpa.repository.support.JpaEntityInformation; +import org.springframework.util.Assert; + +/** + * Delegate to run {@link ScrollPosition scroll queries} and create result {@link Window}. + * + * @author Mark Paluch + * @since 3.1 + */ +public class ScrollDelegate { + + private final JpaEntityInformation entity; + + protected ScrollDelegate(JpaEntityInformation entity) { + this.entity = entity; + } + + /** + * Run the {@link Query} and return a scroll {@link Window}. + * + * @param query must not be {@literal null}. + * @param sort must not be {@literal null}. + * @param scrollPosition must not be {@literal null}. + * @return the scroll {@link Window}. + */ + @SuppressWarnings("unchecked") + public Window scroll(Query query, Sort sort, ScrollPosition scrollPosition) { + + Assert.notNull(scrollPosition, "ScrollPosition must not be null"); + + int limit = query.getMaxResults(); + if (limit > 0 && limit != Integer.MAX_VALUE) { + query = query.setMaxResults(limit + 1); + } + + List result = query.getResultList(); + + if (scrollPosition instanceof KeysetScrollPosition keyset) { + return createWindow(sort, limit, keyset.getDirection(), entity, result); + } + + if (scrollPosition instanceof OffsetScrollPosition offset) { + return createWindow(result, limit, OffsetScrollPosition.positionFunction(offset.getOffset())); + } + + throw new UnsupportedOperationException("ScrollPosition " + scrollPosition + " not supported"); + } + + private static Window createWindow(Sort sort, int limit, Direction direction, + JpaEntityInformation entity, List result) { + + KeysetScrollDelegate delegate = KeysetScrollDelegate.of(direction); + List resultsToUse = delegate.postProcessResults(result); + + IntFunction positionFunction = value -> { + + T object = result.get(value); + Map keys = entity.getKeyset(sort.stream().map(Order::getProperty).toList(), object); + + return KeysetScrollPosition.of(keys); + }; + + return Window.from(delegate.getResultWindow(resultsToUse, limit), positionFunction, hasMoreElements(result, limit)); + } + + private static Window createWindow(List result, int limit, + IntFunction positionFunction) { + return Window.from(CollectionUtils.getFirst(limit, result), positionFunction, hasMoreElements(result, limit)); + } + + private static boolean hasMoreElements(List result, int limit) { + return !result.isEmpty() && result.size() > limit; + } + +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java deleted file mode 100644 index d1ddea66ba..0000000000 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2021-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.support; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.TypedQuery; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Stream; - -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.jpa.repository.query.EscapeCharacter; -import org.springframework.data.jpa.support.PageableUtils; -import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; -import org.springframework.data.support.PageableExecutionUtils; -import org.springframework.util.Assert; - -/** - * Immutable implementation of {@link FetchableFluentQuery} based on Query by {@link Example}. All methods that return a - * {@link FetchableFluentQuery} will return a new instance, not the original. - * - * @param Domain type - * @param Result type - * @author Greg Turnquist - * @author Mark Paluch - * @author Jens Schauder - * @author J.R. Onyschak - * @since 2.6 - */ -class FetchableFluentQueryByExample extends FluentQuerySupport implements FetchableFluentQuery { - - private final Example example; - private final Function> finder; - private final Function, Long> countOperation; - private final Function, Boolean> existsOperation; - private final EntityManager entityManager; - private final EscapeCharacter escapeCharacter; - - public FetchableFluentQueryByExample(Example example, Function> finder, - Function, Long> countOperation, Function, Boolean> existsOperation, - EntityManager entityManager, EscapeCharacter escapeCharacter) { - this(example, example.getProbeType(), (Class) example.getProbeType(), Sort.unsorted(), Collections.emptySet(), - finder, countOperation, existsOperation, entityManager, escapeCharacter); - } - - private FetchableFluentQueryByExample(Example example, Class entityType, Class returnType, Sort sort, - Collection properties, Function> finder, Function, Long> countOperation, - Function, Boolean> existsOperation, EntityManager entityManager, EscapeCharacter escapeCharacter) { - - super(returnType, sort, properties, entityType); - this.example = example; - this.finder = finder; - this.countOperation = countOperation; - this.existsOperation = existsOperation; - this.entityManager = entityManager; - this.escapeCharacter = escapeCharacter; - } - - @Override - public FetchableFluentQuery sortBy(Sort sort) { - - Assert.notNull(sort, "Sort must not be null"); - - return new FetchableFluentQueryByExample<>(example, entityType, resultType, this.sort.and(sort), properties, finder, - countOperation, existsOperation, entityManager, escapeCharacter); - } - - @Override - public FetchableFluentQuery as(Class resultType) { - - Assert.notNull(resultType, "Projection target type must not be null"); - if (!resultType.isInterface()) { - throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); - } - - return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, properties, finder, - countOperation, existsOperation, entityManager, escapeCharacter); - } - - @Override - public FetchableFluentQuery project(Collection properties) { - - return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, mergeProperties(properties), - finder, countOperation, existsOperation, entityManager, escapeCharacter); - } - - @Override - public R oneValue() { - - TypedQuery limitedQuery = createSortedAndProjectedQuery(); - limitedQuery.setMaxResults(2); // Never need more than 2 values - - List results = limitedQuery.getResultList(); - - if (results.size() > 1) { - throw new IncorrectResultSizeDataAccessException(1); - } - - return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); - } - - @Override - public R firstValue() { - - TypedQuery limitedQuery = createSortedAndProjectedQuery(); - limitedQuery.setMaxResults(1); // Never need more than 1 value - - List results = limitedQuery.getResultList(); - - return results.isEmpty() ? null : getConversionFunction().apply(results.get(0)); - } - - @Override - public List all() { - - List resultList = createSortedAndProjectedQuery().getResultList(); - - return convert(resultList); - } - - @Override - public Page page(Pageable pageable) { - return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); - } - - @Override - public Stream stream() { - - return createSortedAndProjectedQuery() // - .getResultStream() // - .map(getConversionFunction()); - } - - @Override - public long count() { - return countOperation.apply(example); - } - - @Override - public boolean exists() { - return existsOperation.apply(example); - } - - private Page readPage(Pageable pageable) { - - TypedQuery pagedQuery = createSortedAndProjectedQuery(); - - if (pageable.isPaged()) { - pagedQuery.setFirstResult(PageableUtils.getOffsetAsInteger(pageable)); - pagedQuery.setMaxResults(pageable.getPageSize()); - } - - List paginatedResults = convert(pagedQuery.getResultList()); - - return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(example)); - } - - private TypedQuery createSortedAndProjectedQuery() { - - TypedQuery query = finder.apply(sort); - - if (!properties.isEmpty()) { - query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties)); - } - - return query; - } - - private List convert(List resultList) { - - Function conversionFunction = getConversionFunction(); - List mapped = new ArrayList<>(resultList.size()); - - for (S s : resultList) { - mapped.add(conversionFunction.apply(s)); - } - return mapped; - } - - private Function getConversionFunction() { - return getConversionFunction(example.getProbeType(), resultType); - } - -} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index b70b3b4d42..1ac5affdea 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.repository.support; import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import java.util.ArrayList; import java.util.Collection; @@ -29,7 +30,10 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; +import org.springframework.data.jpa.repository.query.ScrollDelegate; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.util.Assert; @@ -53,27 +57,31 @@ class FetchableFluentQueryByPredicate extends FluentQuerySupport imp private final Predicate predicate; private final Function> finder; + + private final PredicateScrollDelegate scroll; private final BiFunction> pagedFinder; private final Function countOperation; private final Function existsOperation; private final EntityManager entityManager; public FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, - Function> finder, BiFunction> pagedFinder, - Function countOperation, Function existsOperation, - EntityManager entityManager) { - this(predicate, entityType, (Class) entityType, Sort.unsorted(), Collections.emptySet(), finder, pagedFinder, - countOperation, existsOperation, entityManager); + Function> finder, PredicateScrollDelegate scroll, + BiFunction> pagedFinder, Function countOperation, + Function existsOperation, EntityManager entityManager) { + this(predicate, entityType, (Class) entityType, Sort.unsorted(), 0, Collections.emptySet(), finder, scroll, + pagedFinder, countOperation, existsOperation, entityManager); } private FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, Class resultType, Sort sort, - Collection properties, Function> finder, - BiFunction> pagedFinder, Function countOperation, - Function existsOperation, EntityManager entityManager) { + int limit, Collection properties, Function> finder, + PredicateScrollDelegate scroll, BiFunction> pagedFinder, + Function countOperation, Function existsOperation, + EntityManager entityManager) { - super(resultType, sort, properties, entityType); + super(resultType, sort, limit, properties, entityType); this.predicate = predicate; this.finder = finder; + this.scroll = scroll; this.pagedFinder = pagedFinder; this.countOperation = countOperation; this.existsOperation = existsOperation; @@ -85,8 +93,17 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null"); - return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, this.sort.and(sort), properties, - finder, pagedFinder, countOperation, existsOperation, entityManager); + return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, this.sort.and(sort), limit, + properties, finder, scroll, pagedFinder, countOperation, existsOperation, entityManager); + } + + @Override + public FetchableFluentQuery limit(int limit) { + + Assert.isTrue(limit >= 0, "Limit must not be negative"); + + return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, limit, properties, finder, + scroll, pagedFinder, countOperation, existsOperation, entityManager); } @Override @@ -98,15 +115,15 @@ public FetchableFluentQuery as(Class resultType) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); } - return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, properties, finder, - pagedFinder, countOperation, existsOperation, entityManager); + return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, limit, properties, finder, + scroll, pagedFinder, countOperation, existsOperation, entityManager); } @Override public FetchableFluentQuery project(Collection properties) { - return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, mergeProperties(properties), - finder, pagedFinder, countOperation, existsOperation, entityManager); + return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, limit, + mergeProperties(properties), finder, scroll, pagedFinder, countOperation, existsOperation, entityManager); } @Override @@ -138,6 +155,14 @@ public List all() { return convert(createSortedAndProjectedQuery().fetch()); } + @Override + public Window scroll(ScrollPosition scrollPosition) { + + Assert.notNull(scrollPosition, "ScrollPosition must not be null"); + + return scroll.scroll(sort, limit, scrollPosition).map(getConversionFunction()); + } + @Override public Page page(Pageable pageable) { return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); @@ -169,6 +194,10 @@ public boolean exists() { query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties)); } + if (limit != 0) { + query.limit(limit); + } + return query; } @@ -201,4 +230,24 @@ private Function getConversionFunction() { return getConversionFunction(entityType, resultType); } + + static class PredicateScrollDelegate extends ScrollDelegate { + + private final ScrollQueryFactory scrollFunction; + + PredicateScrollDelegate(ScrollQueryFactory scrollQueryFactory, JpaEntityInformation entity) { + super(entity); + this.scrollFunction = scrollQueryFactory; + } + + public Window scroll(Sort sort, int limit, ScrollPosition scrollPosition) { + + Query query = scrollFunction.createQuery(sort, scrollPosition); + if (limit > 0) { + query = query.setMaxResults(limit); + } + return scroll(query, sort, scrollPosition); + } + } + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java index 74d2d67afd..d8193f52a4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java @@ -16,6 +16,7 @@ package org.springframework.data.jpa.repository.support; import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import jakarta.persistence.TypedQuery; import java.util.ArrayList; @@ -29,8 +30,11 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.query.ScrollDelegate; import org.springframework.data.jpa.support.PageableUtils; import org.springframework.data.repository.query.FluentQuery; import org.springframework.data.support.PageableExecutionUtils; @@ -50,26 +54,28 @@ class FetchableFluentQueryBySpecification extends FluentQuerySupport private final Specification spec; private final Function> finder; + private final SpecificationScrollDelegate scroll; private final Function, Long> countOperation; private final Function, Boolean> existsOperation; private final EntityManager entityManager; - public FetchableFluentQueryBySpecification(Specification spec, Class entityType, Sort sort, - Collection properties, Function> finder, + public FetchableFluentQueryBySpecification(Specification spec, Class entityType, + Function> finder, SpecificationScrollDelegate scrollDelegate, Function, Long> countOperation, Function, Boolean> existsOperation, EntityManager entityManager) { - this(spec, entityType, (Class) entityType, Sort.unsorted(), Collections.emptySet(), finder, countOperation, - existsOperation, entityManager); + this(spec, entityType, (Class) entityType, Sort.unsorted(), 0, Collections.emptySet(), finder, scrollDelegate, + countOperation, existsOperation, entityManager); } private FetchableFluentQueryBySpecification(Specification spec, Class entityType, Class resultType, - Sort sort, Collection properties, Function> finder, - Function, Long> countOperation, Function, Boolean> existsOperation, - EntityManager entityManager) { + Sort sort, int limit, Collection properties, Function> finder, + SpecificationScrollDelegate scrollDelegate, Function, Long> countOperation, + Function, Boolean> existsOperation, EntityManager entityManager) { - super(resultType, sort, properties, entityType); + super(resultType, sort, limit, properties, entityType); this.spec = spec; this.finder = finder; + this.scroll = scrollDelegate; this.countOperation = countOperation; this.existsOperation = existsOperation; this.entityManager = entityManager; @@ -80,8 +86,17 @@ public FetchableFluentQuery sortBy(Sort sort) { Assert.notNull(sort, "Sort must not be null"); - return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, this.sort.and(sort), properties, - finder, countOperation, existsOperation, entityManager); + return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, this.sort.and(sort), limit, + properties, finder, scroll, countOperation, existsOperation, entityManager); + } + + @Override + public FetchableFluentQuery limit(int limit) { + + Assert.isTrue(limit >= 0, "Limit must not be negative"); + + return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, this.sort.and(sort), limit, + properties, finder, scroll, countOperation, existsOperation, entityManager); } @Override @@ -92,15 +107,15 @@ public FetchableFluentQuery as(Class resultType) { throw new UnsupportedOperationException("Class-based DTOs are not yet supported."); } - return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, properties, finder, - countOperation, existsOperation, entityManager); + return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, limit, properties, finder, + scroll, countOperation, existsOperation, entityManager); } @Override public FetchableFluentQuery project(Collection properties) { - return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, properties, finder, - countOperation, existsOperation, entityManager); + return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, limit, properties, finder, + scroll, countOperation, existsOperation, entityManager); } @Override @@ -132,6 +147,14 @@ public List all() { return convert(createSortedAndProjectedQuery().getResultList()); } + @Override + public Window scroll(ScrollPosition scrollPosition) { + + Assert.notNull(scrollPosition, "ScrollPosition must not be null"); + + return scroll.scroll(sort, limit, scrollPosition).map(getConversionFunction()); + } + @Override public Page page(Pageable pageable) { return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable); @@ -163,6 +186,10 @@ private TypedQuery createSortedAndProjectedQuery() { query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties)); } + if (limit != 0) { + query.setMaxResults(limit); + } + return query; } @@ -194,4 +221,25 @@ private List convert(List resultList) { private Function getConversionFunction() { return getConversionFunction(entityType, resultType); } + + static class SpecificationScrollDelegate extends ScrollDelegate { + + private final ScrollQueryFactory scrollFunction; + + SpecificationScrollDelegate(ScrollQueryFactory scrollQueryFactory, JpaEntityInformation entity) { + super(entity); + this.scrollFunction = scrollQueryFactory; + } + + public Window scroll(Sort sort, int limit, ScrollPosition scrollPosition) { + + Query query = scrollFunction.createQuery(sort, scrollPosition); + + if (limit > 0) { + query = query.setMaxResults(limit); + } + + return scroll(query, sort, scrollPosition); + } + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java index 4ac26874a6..5aa8352ed8 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository.support; +import jakarta.persistence.Query; + import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -22,6 +24,7 @@ import java.util.function.Function; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.lang.Nullable; @@ -32,21 +35,25 @@ * @param The resulting type of the query. * @author Greg Turnquist * @author Jens Schauder + * @author Mark Paluch * @since 2.6 */ abstract class FluentQuerySupport { protected final Class resultType; protected final Sort sort; + protected final int limit; protected final Set properties; protected final Class entityType; private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); - FluentQuerySupport(Class resultType, Sort sort, @Nullable Collection properties, Class entityType) { + FluentQuerySupport(Class resultType, Sort sort, int limit, @Nullable Collection properties, + Class entityType) { this.resultType = resultType; this.sort = sort; + this.limit = limit; if (properties != null) { this.properties = new HashSet<>(properties); @@ -78,4 +85,9 @@ final Function getConversionFunction(Class inputType, Class tar return o -> DefaultConversionService.getSharedInstance().convert(o, targetType); } + + interface ScrollQueryFactory { + Query createQuery(Sort sort, ScrollPosition scrollPosition); + } + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java index f8b438ff82..f192386db0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -17,6 +17,9 @@ import jakarta.persistence.metamodel.SingularAttribute; +import java.util.Collection; +import java.util.Map; + import org.springframework.data.jpa.repository.query.JpaEntityMetadata; import org.springframework.data.repository.core.EntityInformation; import org.springframework.lang.Nullable; @@ -70,7 +73,7 @@ public interface JpaEntityInformation extends EntityInformation, J * * @return */ - Iterable getIdAttributeNames(); + Collection getIdAttributeNames(); /** * Extracts the value for the given id attribute from a composite id @@ -81,4 +84,14 @@ public interface JpaEntityInformation extends EntityInformation, J */ @Nullable Object getCompositeIdAttributeValue(Object id, String idAttribute); + + /** + * Extract a keyset for {@code propertyPaths} and the primary key (including composite key components if applicable). + * + * @param propertyPaths + * @param entity + * @return + * @since 3.1 + */ + Map getKeyset(Iterable propertyPaths, T entity); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index fa64f1cc51..c740887b27 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -26,9 +26,12 @@ import jakarta.persistence.metamodel.Type; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -61,7 +64,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu /** * Creates a new {@link JpaMetamodelEntityInformation} for the given domain class and {@link Metamodel}. - * + * * @param domainClass must not be {@literal null}. * @param metamodel must not be {@literal null}. * @param persistenceUnitUtil must not be {@literal null}. @@ -190,7 +193,7 @@ public boolean hasCompositeId() { } @Override - public Iterable getIdAttributeNames() { + public Collection getIdAttributeNames() { List attributeNames = new ArrayList<>(idMetadata.attributes.size()); @@ -222,6 +225,30 @@ public boolean isNew(T entity) { return versionAttribute.map(it -> wrapper.getPropertyValue(it.getName()) == null).orElse(true); } + @Override + public Map getKeyset(Iterable propertyPaths, T entity) { + + // TODO: Proxy handling requires more elaborate refactoring, see + // https://github.com/spring-projects/spring-data-jpa/issues/2784 + BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity); + + Map keyset = new LinkedHashMap<>(); + + if (hasCompositeId()) { + for (String idAttributeName : getIdAttributeNames()) { + keyset.put(idAttributeName, entityWrapper.getPropertyValue(idAttributeName)); + } + } else { + keyset.put(getIdAttribute().getName(), getId(entity)); + } + + for (String propertyPath : propertyPaths) { + keyset.put(propertyPath, entityWrapper.getPropertyValue(propertyPath)); + } + + return keyset; + } + /** * Simple value object to encapsulate id specific metadata. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 1f4c5883e7..2e38c4c2b3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jpa.repository.support; -import java.util.List; - import jakarta.persistence.EntityManager; +import java.util.List; + import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @@ -245,4 +245,29 @@ private Expression buildOrderPropertyPathFrom(Order order) { return sortPropertyExpression; } + + /** + * Creates an {@link Expression} for the given {@code property} property. + * + * @param property must not be {@literal null}. + * @return + */ + Expression createExpression(String property) { + + Assert.notNull(property, "Property must not be null"); + + PropertyPath path = PropertyPath.from(property, builder.getType()); + Expression sortPropertyExpression = builder; + + while (path != null) { + + sortPropertyExpression = !path.hasNext() && String.class.equals(path.getType()) // + ? Expressions.stringPath((Path) sortPropertyExpression, path.getSegment()) // + : Expressions.path(path.getType(), (Path) sortPropertyExpression, path.getSegment()); + + path = path.next(); + } + + return sortPropertyExpression; + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 2396e39ae3..c899d051ab 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -15,19 +15,27 @@ */ package org.springframework.data.jpa.repository.support; +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; + import java.util.List; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; -import jakarta.persistence.EntityManager; -import jakarta.persistence.LockModeType; - import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.OffsetScrollPosition; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.query.KeysetScrollDelegate; +import org.springframework.data.jpa.repository.query.KeysetScrollDelegate.QueryStrategy; +import org.springframework.data.jpa.repository.query.KeysetScrollSpecification; +import org.springframework.data.jpa.repository.support.FetchableFluentQueryByPredicate.PredicateScrollDelegate; +import org.springframework.data.jpa.repository.support.FluentQuerySupport.ScrollQueryFactory; import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.querydsl.QSort; import org.springframework.data.querydsl.QuerydslPredicateExecutor; @@ -37,9 +45,14 @@ import org.springframework.util.Assert; import com.querydsl.core.NonUniqueResultException; +import com.querydsl.core.types.ConstantImpl; import com.querydsl.core.types.EntityPath; +import com.querydsl.core.types.Expression; +import com.querydsl.core.types.NullExpression; +import com.querydsl.core.types.Ops; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.PathBuilder; import com.querydsl.jpa.JPQLQuery; @@ -63,6 +76,7 @@ public class QuerydslJpaPredicateExecutor implements QuerydslPredicateExecuto private final JpaEntityInformation entityInformation; private final EntityPath path; private final Querydsl querydsl; + private final QuerydslQueryStrategy scrollQueryAdapter; private final EntityManager entityManager; private final CrudMethodMetadata metadata; @@ -83,6 +97,7 @@ public QuerydslJpaPredicateExecutor(JpaEntityInformation entityInformation this.path = resolver.createPath(entityInformation.getJavaType()); this.querydsl = new Querydsl(entityManager, new PathBuilder(path.getType(), path.getMetadata())); this.entityManager = entityManager; + this.scrollQueryAdapter = new QuerydslQueryStrategy(); } @Override @@ -160,6 +175,33 @@ public R findBy(Predicate predicate, Function scroll = (sort, scrollPosition) -> { + + Predicate predicateToUse = predicate; + + if (scrollPosition instanceof KeysetScrollPosition keyset) { + + KeysetScrollDelegate delegate = KeysetScrollDelegate.of(keyset.getDirection()); + sort = KeysetScrollSpecification.createSort(keyset, sort, entityInformation); + BooleanExpression keysetPredicate = delegate.createPredicate(keyset, sort, scrollQueryAdapter); + + if (keysetPredicate != null) { + predicateToUse = predicate instanceof BooleanExpression be ? be.and(keysetPredicate) + : keysetPredicate.and(predicate); + } + } + + AbstractJPAQuery select = (AbstractJPAQuery) createQuery(predicateToUse).select(path); + + select = (AbstractJPAQuery) querydsl.applySorting(sort, select); + + if (scrollPosition instanceof OffsetScrollPosition offset) { + select.offset(offset.getOffset()); + } + + return select.createQuery(); + }; + BiFunction> pagedFinder = (sort, pageable) -> { AbstractJPAQuery select = finder.apply(sort); @@ -175,6 +217,7 @@ public R findBy(Predicate predicate, Function(scroll, entityInformation), // pagedFinder, // this::count, // this::exists, // @@ -285,4 +328,34 @@ private List executeSorted(JPQLQuery query, OrderSpecifier... orders) { private List executeSorted(JPQLQuery query, Sort sort) { return querydsl.applySorting(sort, query).fetch(); } + + class QuerydslQueryStrategy implements QueryStrategy, BooleanExpression> { + + @Override + public Expression createExpression(String property) { + return querydsl.createExpression(property); + } + + @Override + public BooleanExpression compare(Order order, Expression propertyExpression, Object value) { + return Expressions.booleanOperation(order.isAscending() ? Ops.GT : Ops.LT, propertyExpression, + ConstantImpl.create(value)); + } + + @Override + public BooleanExpression compare(Expression propertyExpression, @Nullable Object value) { + return Expressions.booleanOperation(Ops.EQ, propertyExpression, + value == null ? NullExpression.DEFAULT : ConstantImpl.create(value)); + } + + @Override + public BooleanExpression and(List intermediate) { + return Expressions.allOf(intermediate.toArray(new BooleanExpression[0])); + } + + @Override + public BooleanExpression or(List intermediate) { + return Expressions.anyOf(intermediate.toArray(new BooleanExpression[0])); + } + } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index d83d5f50ff..788ea6fae3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -43,6 +43,8 @@ import java.util.stream.StreamSupport; import org.springframework.data.domain.Example; +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.OffsetScrollPosition; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -52,7 +54,10 @@ import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.query.EscapeCharacter; +import org.springframework.data.jpa.repository.query.KeysetScrollSpecification; import org.springframework.data.jpa.repository.query.QueryUtils; +import org.springframework.data.jpa.repository.support.FetchableFluentQueryBySpecification.SpecificationScrollDelegate; +import org.springframework.data.jpa.repository.support.FluentQuerySupport.ScrollQueryFactory; import org.springframework.data.jpa.repository.support.QueryHints.NoHints; import org.springframework.data.jpa.support.PageableUtils; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; @@ -507,10 +512,40 @@ public R findBy(Specification spec, Function> finder = sort -> getQuery(spec, getDomainClass(), sort); + return doFindBy(spec, getDomainClass(), queryFunction); + } + + private R doFindBy(Specification spec, Class domainClass, + Function, R> queryFunction) { + + Assert.notNull(spec, "Specification must not be null"); + Assert.notNull(queryFunction, "Query function must not be null"); + + ScrollQueryFactory scrollFunction = (sort, scrollPosition) -> { + + Specification specToUse = spec; + + if (scrollPosition instanceof KeysetScrollPosition keyset) { + KeysetScrollSpecification keysetSpec = new KeysetScrollSpecification<>(keyset, sort, entityInformation); + sort = keysetSpec.sort(); + specToUse = specToUse.and(keysetSpec); + } + + TypedQuery query = getQuery(specToUse, domainClass, sort); + + if (scrollPosition instanceof OffsetScrollPosition offset) { + query.setFirstResult(Math.toIntExact(offset.getOffset())); + } - FetchableFluentQuery fluentQuery = new FetchableFluentQueryBySpecification(spec, getDomainClass(), - Sort.unsorted(), null, finder, this::count, this::exists, this.em); + return query; + }; + + Function> finder = sort -> getQuery(spec, domainClass, sort); + + SpecificationScrollDelegate scrollDelegate = new SpecificationScrollDelegate<>(scrollFunction, + entityInformation); + FetchableFluentQuery fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass, finder, + scrollDelegate, this::count, this::exists, this.em); return queryFunction.apply((FetchableFluentQuery) fluentQuery); } @@ -544,7 +579,6 @@ public boolean exists(Example example) { return query.setMaxResults(1).getResultList().size() == 1; } - @Override public List findAll(Example example) { return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted()) @@ -572,21 +606,12 @@ public R findBy(Example example, Function> finder = sort -> { - - ExampleSpecification spec = new ExampleSpecification<>(example, escapeCharacter); - Class probeType = example.getProbeType(); - - return getQuery(spec, probeType, sort); - }; - - FetchableFluentQuery fluentQuery = new FetchableFluentQueryByExample<>(example, finder, this::count, - this::exists, this.em, this.escapeCharacter); + ExampleSpecification spec = new ExampleSpecification<>(example, escapeCharacter); + Class probeType = example.getProbeType(); - return queryFunction.apply(fluentQuery); + return doFindBy((Specification) spec, (Class) probeType, queryFunction); } - @Override public long count() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java index c2364a298b..6fce01ece1 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java @@ -21,6 +21,8 @@ import jakarta.persistence.IdClass; import jakarta.persistence.JoinColumn; import jakarta.persistence.Table; +import lombok.EqualsAndHashCode; +import lombok.ToString; /** * @author Mark Paluch @@ -30,12 +32,16 @@ @Entity @Table @IdClass(ItemId.class) +@EqualsAndHashCode +@ToString public class Item { @Id @Column(columnDefinition = "INT") private Integer id; @Id @JoinColumn(name = "manufacturer_id", columnDefinition = "INT") private Integer manufacturerId; + private String name; + public Item() {} public Item(Integer id, Integer manufacturerId) { @@ -43,6 +49,12 @@ public Item(Integer id, Integer manufacturerId) { this.manufacturerId = manufacturerId; } + public Item(Integer id, Integer manufacturerId, String name) { + this.id = id; + this.manufacturerId = manufacturerId; + this.name = name; + } + public Integer getId() { return id; } @@ -50,4 +62,13 @@ public Integer getId() { public Integer getManufacturerId() { return manufacturerId; } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java index 314998dcb9..c530cb83ff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java @@ -15,8 +15,9 @@ */ package org.springframework.data.jpa.repository; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; +import java.util.Arrays; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -24,6 +25,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; import org.springframework.data.jpa.domain.sample.Item; import org.springframework.data.jpa.domain.sample.ItemId; import org.springframework.data.jpa.domain.sample.ItemSite; @@ -75,6 +79,28 @@ void shouldSaveAndLoadEntitiesWithDerivedIdentities() { assertThat(loaded).isPresent(); } + @Test // GH-2878 + void shouldScrollWithKeyset() { + + Item item1 = new Item(1, 2, "a"); + Item item2 = new Item(2, 3, "b"); + Item item3 = new Item(3, 4, "c"); + + itemRepository.saveAllAndFlush(Arrays.asList(item1, item2, item3)); + + Window first = itemRepository.findBy((root, query, criteriaBuilder) -> { + return criteriaBuilder.isNotNull(root.get("name")); + }, q -> q.limit(1).sortBy(Sort.by("name")).scroll(KeysetScrollPosition.initial())); + + assertThat(first).containsOnly(item1); + + Window next = itemRepository.findBy((root, query, criteriaBuilder) -> { + return criteriaBuilder.isNotNull(root.get("name")); + }, q -> q.limit(1).sortBy(Sort.by("name")).scroll(first.positionAt(0))); + + assertThat(next).containsOnly(item2); + } + @Configuration @EnableJpaRepositories(basePackageClasses = SampleConfig.class) static abstract class Config { 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 61fc82a88e..026598b868 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 @@ -34,6 +34,7 @@ import lombok.Data; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -54,14 +55,7 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; +import org.springframework.data.domain.*; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; @@ -1235,6 +1229,185 @@ void findAllByTypedSpecialUserExampleShouldReturnSubTypesOfRepositoryEntity() { assertThat(result).hasSize(1); } + @Test // GH-2878 + void scrollByExampleOffset() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Example example = Example.of(new User("J", null, null), + matching().withMatcher("firstname", GenericPropertyMatcher::startsWith).withIgnorePaths("age", "createdAt", + "dateOfBirth")); + Window firstWindow = repository.findBy(example, + q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(OffsetScrollPosition.initial())); + + assertThat(firstWindow).containsExactly(jane1, jane2); + assertThat(firstWindow.hasNext()).isTrue(); + + Window nextWindow = repository.findBy(example, + q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(firstWindow.positionAt(1))); + + assertThat(nextWindow).containsExactly(john1, john2); + assertThat(nextWindow.hasNext()).isFalse(); + } + + @Test // GH-2878 + void scrollByExampleKeyset() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Example example = Example.of(new User("J", null, null), + matching().withMatcher("firstname", GenericPropertyMatcher::startsWith).withIgnorePaths("age", "createdAt", + "dateOfBirth")); + Window firstWindow = repository.findBy(example, + q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")).scroll(KeysetScrollPosition.initial())); + + assertThat(firstWindow).containsOnly(jane1); + assertThat(firstWindow.hasNext()).isTrue(); + + Window nextWindow = repository.findBy(example, + q -> q.limit(2).sortBy(Sort.by("firstname", "emailAddress")).scroll(firstWindow.positionAt(0))); + + assertThat(nextWindow).containsExactly(jane2, john1); + assertThat(nextWindow.hasNext()).isTrue(); + } + + @Test // GH-2878 + void scrollByExampleKeysetBackward() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Example example = Example.of(new User("J", null, null), + matching().withMatcher("firstname", GenericPropertyMatcher::startsWith).withIgnorePaths("age", "createdAt", + "dateOfBirth")); + Window firstWindow = repository.findBy(example, + q -> q.limit(4).sortBy(Sort.by("firstname", "emailAddress")).scroll(KeysetScrollPosition.initial())); + + KeysetScrollPosition scrollPosition = (KeysetScrollPosition) firstWindow.positionAt(2); + Window previousWindow = repository.findBy(example, + q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")) + .scroll(KeysetScrollPosition.of(scrollPosition.getKeys(), KeysetScrollPosition.Direction.Backward))); + + assertThat(previousWindow).containsOnly(jane2); + assertThat(previousWindow.hasNext()).isTrue(); + } + + @Test // GH-2878 + void scrollByPredicateOffset() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Window firstWindow = repository.findBy(QUser.user.firstname.startsWith("J"), + q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(OffsetScrollPosition.initial())); + + assertThat(firstWindow).containsExactly(jane1, jane2); + assertThat(firstWindow.hasNext()).isTrue(); + + Window nextWindow = repository.findBy(QUser.user.firstname.startsWith("J"), + q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(firstWindow.positionAt(1))); + + assertThat(nextWindow).containsExactly(john1, john2); + assertThat(nextWindow.hasNext()).isFalse(); + } + + @Test // GH-2878 + void scrollByPredicateKeyset() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Window firstWindow = repository.findBy(QUser.user.firstname.startsWith("J"), + q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")).scroll(KeysetScrollPosition.initial())); + + assertThat(firstWindow).containsOnly(jane1); + assertThat(firstWindow.hasNext()).isTrue(); + + Window nextWindow = repository.findBy(QUser.user.firstname.startsWith("J"), + q -> q.limit(2).sortBy(Sort.by("firstname", "emailAddress")).scroll(firstWindow.positionAt(0))); + + assertThat(nextWindow).containsExactly(jane2, john1); + assertThat(nextWindow.hasNext()).isTrue(); + } + + @Test // GH-2878 + void scrollByPredicateKeysetBackward() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Window firstWindow = repository.findBy(QUser.user.firstname.startsWith("J"), + q -> q.limit(3).sortBy(Sort.by("firstname", "emailAddress")).scroll(KeysetScrollPosition.initial())); + + assertThat(firstWindow).containsExactly(jane1, jane2, john1); + assertThat(firstWindow.hasNext()).isTrue(); + + KeysetScrollPosition scrollPosition = (KeysetScrollPosition) firstWindow.positionAt(2); + KeysetScrollPosition backward = KeysetScrollPosition.of(scrollPosition.getKeys(), + KeysetScrollPosition.Direction.Backward); + Window previousWindow = repository.findBy(QUser.user.firstname.startsWith("J"), + q -> q.limit(3).sortBy(Sort.by("firstname", "emailAddress")).scroll(backward)); + + assertThat(previousWindow).containsExactly(jane1, jane2); + + // no more items before this window + assertThat(previousWindow.hasNext()).isFalse(); + } + + @Test // GH-2878 + void scrollByPartTreeKeysetBackward() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Window firstWindow = repository.findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc("J", + KeysetScrollPosition.initial()); + + assertThat(firstWindow).containsExactly(jane1, jane2, john1); + assertThat(firstWindow.hasNext()).isTrue(); + + KeysetScrollPosition scrollPosition = (KeysetScrollPosition) firstWindow.positionAt(2); + KeysetScrollPosition backward = KeysetScrollPosition.of(scrollPosition.getKeys(), + KeysetScrollPosition.Direction.Backward); + Window previousWindow = repository.findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc("J", + backward); + + assertThat(previousWindow).containsExactly(jane1, jane2); + + // no more items before this window + assertThat(previousWindow.hasNext()).isFalse(); + } + @Test // DATAJPA-491 void sortByNestedAssociationPropertyWithSortInPageable() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CollectionUtilsUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CollectionUtilsUnitTests.java new file mode 100644 index 0000000000..6deb691d6e --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CollectionUtilsUnitTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link CollectionUtils}. + * + * @author Mark Paluch + */ +class CollectionUtilsUnitTests { + + @Test // GH-2878 + void shouldReturnFirstItems() { + + assertThat(CollectionUtils.getFirst(2, List.of(1, 2, 3))).containsExactly(1, 2); + assertThat(CollectionUtils.getFirst(2, List.of(1, 2))).containsExactly(1, 2); + assertThat(CollectionUtils.getFirst(2, List.of(1))).containsExactly(1); + } + + @Test // GH-2878 + void shouldReturnLastItems() { + + assertThat(CollectionUtils.getLast(2, List.of(1, 2, 3))).containsExactly(2, 3); + assertThat(CollectionUtils.getLast(2, List.of(1, 2))).containsExactly(1, 2); + assertThat(CollectionUtils.getLast(2, List.of(1))).containsExactly(1); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java index bbf051e231..b4ed4f38af 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/ItemRepository.java @@ -18,10 +18,11 @@ import org.springframework.data.jpa.domain.sample.Item; import org.springframework.data.jpa.domain.sample.ItemId; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author Mark Paluch * @see Final JPA 2.1 * Specification 2.4.1.3 Derived Identities Example 2 */ -public interface ItemRepository extends JpaRepository {} +public interface ItemRepository extends JpaRepository, JpaSpecificationExecutor {} 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 83e77e5be2..24362a2a77 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 @@ -29,8 +29,10 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; @@ -150,6 +152,9 @@ public interface UserRepository extends JpaRepository, JpaSpecifi Page findByFirstnameIn(Pageable pageable, String... firstnames); + Window findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(String firstname, + ScrollPosition position); + List findByFirstnameNotIn(Collection firstnames); // DATAJPA-292 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java deleted file mode 100644 index ca6c80f2fe..0000000000 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExampleUnitTests.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2022-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jpa.repository.support; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Order; -import org.springframework.data.jpa.repository.support.FetchableFluentQueryByExample; - -/** - * Unit tests for {@link FetchableFluentQueryByExample}. - * - * @author J.R. Onyschak - */ -class FetchableFluentQueryByExampleUnitTests { - - @Test // GH-2438 - @SuppressWarnings({ "rawtypes", "unchecked" }) - void multipleSortBy() { - - Sort s1 = Sort.by(Order.by("s1")); - Sort s2 = Sort.by(Order.by("s2")); - FetchableFluentQueryByExample f = new FetchableFluentQueryByExample(Example.of(""), null, null, null, null, null); - f = (FetchableFluentQueryByExample) f.sortBy(s1).sortBy(s2); - assertThat(f.sort).isEqualTo(s1.and(s2)); - } -} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java index 0691c6e87a..82e9fa65d4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.Test; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; -import org.springframework.data.jpa.repository.support.FetchableFluentQueryByPredicate; /** * Unit tests for {@link FetchableFluentQueryByPredicate}. @@ -35,7 +34,8 @@ void multipleSortBy() { Sort s1 = Sort.by(Order.by("s1")); Sort s2 = Sort.by(Order.by("s2")); - FetchableFluentQueryByPredicate f = new FetchableFluentQueryByPredicate(null, null, null, null, null, null, null); + FetchableFluentQueryByPredicate f = new FetchableFluentQueryByPredicate(null, null, null, null, null, null, null, + null); f = (FetchableFluentQueryByPredicate) f.sortBy(s1).sortBy(s2); assertThat(f.sort).isEqualTo(s1.and(s2)); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java index 78268f7b12..52df503449 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaEntityInformationSupportUnitTests.java @@ -18,9 +18,6 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.io.Serializable; -import java.util.Collections; - import jakarta.persistence.Entity; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; @@ -28,6 +25,11 @@ import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.SingularAttribute; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -97,7 +99,7 @@ public Class getIdType() { } @Override - public Iterable getIdAttributeNames() { + public Collection getIdAttributeNames() { return Collections.emptySet(); } @@ -110,6 +112,11 @@ public boolean hasCompositeId() { public Object getCompositeIdAttributeValue(Object id, String idAttribute) { return null; } + + @Override + public Map getKeyset(Iterable propertyPaths, T entity) { + return null; + } } @Entity(name = "AnotherNamedUser") diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index ea94f95ac3..3e3d7ec919 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -5,8 +5,9 @@ Oliver Gierke; Thomas Darimont; Christoph Strobl; Mark Paluch; Jay Bryant; Greg ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]] :spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc :spring-framework-docs: https://docs.spring.io/spring-framework/docs/{springVersion}/spring-framework-reference/ +:feature-scroll: true -(C) 2008-2022 The original authors. +(C) 2008-2023 The original authors. NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index d03f63d787..723989c40f 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -521,20 +521,45 @@ repo.findByAndSort("stark", Sort.by("LENGTH(firstname)")); <2> repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); <3> repo.findByAsArrayAndSort("bolton", Sort.by("fn_len")); <4> ---- + <1> Valid `Sort` expression pointing to property in domain model. -<2> Invalid `Sort` containing function call. Throws Exception. +<2> Invalid `Sort` containing function call. +Throws Exception. <3> Valid `Sort` containing explicitly _unsafe_ `Order`. <4> Valid `Sort` expression pointing to aliased function. ==== +[[jpa.query-methods.scroll]] +=== Scrolling Large Query Results + +When working with large data sets, <> can help to process those results efficiently without loading all results into memory. + +You have multiple options to consume large query results: + +1. <>. +You have learned in the previous chapter about `Pageable` and `PageRequest`. +2. <>. +This is a lighter variant than paging because it does not require the total result count. +3. <>. +This method avoids https://use-the-index-luke.com/no-offset[the shortcomings of offset-based result retrieval by leveraging database indexes]. + +Read more on <> for your particular arrangement. + +You can use the Scroll API with query methods, <>, and <>. + +NOTE: Scrolling with String-based query methods is not yet supported. +Scrolling is also not supported using stored `@Procedure` query methods. + [[jpa.named-parameters]] === Using Named Parameters -By default, Spring Data JPA uses position-based parameter binding, as described in all the preceding examples. This makes query methods a little error-prone when refactoring regarding the parameter position. To solve this issue, you can use `@Param` annotation to give a method parameter a concrete name and bind the name in the query, as shown in the following example: +By default, Spring Data JPA uses position-based parameter binding, as described in all the preceding examples. +This makes query methods a little error-prone when refactoring regarding the parameter position. +To solve this issue, you can use `@Param` annotation to give a method parameter a concrete name and bind the name in the query, as shown in the following example: .Using named parameters ==== -[source, java] +[source,java] ---- public interface UserRepository extends JpaRepository { From 30511fef09ef54b455008e31fca01e73d29e3ea3 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 29 Mar 2023 13:46:22 +0200 Subject: [PATCH 352/821] Polishing. See #2878 Original pull request #2885 --- .../jpa/repository/query/CollectionUtils.java | 4 ++-- .../query/JpaKeysetScrollQueryCreator.java | 2 ++ .../repository/query/JpaQueryExecution.java | 1 + .../query/KeysetScrollDelegate.java | 22 +++++++++---------- .../query/KeysetScrollSpecification.java | 1 - .../data/jpa/repository/query/NamedQuery.java | 11 ++++------ .../FetchableFluentQueryByPredicate.java | 4 ++-- .../FetchableFluentQueryBySpecification.java | 4 ++-- .../support/FluentQuerySupport.java | 2 +- .../support/JpaEntityInformation.java | 17 +++++--------- .../JpaMetamodelEntityInformation.java | 6 +---- .../data/jpa/repository/support/Querydsl.java | 8 ------- .../support/QuerydslJpaPredicateExecutor.java | 2 +- .../support/SimpleJpaRepository.java | 2 +- .../data/jpa/domain/sample/Item.java | 1 + 15 files changed, 33 insertions(+), 54 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/CollectionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/CollectionUtils.java index 582d30eb1d..b6bd980858 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/CollectionUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/CollectionUtils.java @@ -30,7 +30,7 @@ class CollectionUtils { * @param count the number of first elements to be included in the returned list. * @param list must not be {@literal null} * @return the returned sublist if the {@code list} is greater {@code count}. - * @param + * @param the element type of the lists. */ public static List getFirst(int count, List list) { @@ -47,7 +47,7 @@ public static List getFirst(int count, List list) { * @param count the number of last elements to be included in the returned list. * @param list must not be {@literal null} * @return the returned sublist if the {@code list} is greater {@code count}. - * @param + * @param the element type of the lists. */ public static List getLast(int count, List list) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java index 5dbf036b4b..7ee3d04fa4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java @@ -45,7 +45,9 @@ class JpaKeysetScrollQueryCreator extends JpaQueryCreator { public JpaKeysetScrollQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder, ParameterMetadataProvider provider, JpaEntityInformation entityInformation, KeysetScrollPosition scrollPosition) { + super(tree, type, builder, provider); + this.entityInformation = entityInformation; this.scrollPosition = scrollPosition; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 618ba586c2..90ddbaa671 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -142,6 +142,7 @@ static class ScrollExecution extends JpaQueryExecution { private final ScrollDelegate delegate; ScrollExecution(Sort sort, ScrollDelegate delegate) { + this.sort = sort; this.delegate = delegate; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java index 0ce89ff114..f6061a9c7e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java @@ -40,8 +40,8 @@ public class KeysetScrollDelegate { /** * Factory method to obtain the right {@link KeysetScrollDelegate}. * - * @param direction - * @return + * @param direction the direction of scrolling. + * @return a {@link KeysetScrollDelegate} matching the requested direction. */ public static KeysetScrollDelegate of(Direction direction) { return direction == Direction.Forward ? forward : reverse; @@ -104,7 +104,6 @@ protected Sort getSortOrders(Sort sort) { return sort; } - @SuppressWarnings("unchecked") protected List postProcessResults(List result) { return result; } @@ -154,7 +153,6 @@ public interface QueryStrategy { * Create an expression object from the given {@code property} path. * * @param property must not be {@literal null}. - * @return */ E createExpression(String property); @@ -163,8 +161,8 @@ public interface QueryStrategy { * * @param order must not be {@literal null}. * @param propertyExpression must not be {@literal null}. - * @param value - * @return + * @param value the value to compare with. Must not be {@literal null}. + * @return an object representing the comparison predicate. */ P compare(Order order, E propertyExpression, Object value); @@ -172,24 +170,24 @@ public interface QueryStrategy { * Create an equals-comparison object. * * @param propertyExpression must not be {@literal null}. - * @param value - * @return + * @param value the value to compare with. Must not be {@literal null}. + * @return an object representing the comparison predicate. */ P compare(E propertyExpression, @Nullable Object value); /** * AND-combine the {@code intermediate} predicates. * - * @param intermediate - * @return + * @param intermediate the predicates to combine. Must not be {@literal null}. + * @return a single predicate. */ P and(List

    intermediate); /** * OR-combine the {@code intermediate} predicates. * - * @param intermediate - * @return + * @param intermediate the predicates to combine. Must not be {@literal null}. + * @return a single predicate. */ P or(List

    intermediate); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java index cfec4f06df..ee4979a637 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java @@ -56,7 +56,6 @@ public KeysetScrollSpecification(KeysetScrollPosition position, Sort sort, JpaEn * @param position must not be {@literal null}. * @param sort must not be {@literal null}. * @param entity must not be {@literal null}. - * @return */ public static Sort createSort(KeysetScrollPosition position, Sort sort, JpaEntityInformation entity) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 311f47eafd..e78c9c2531 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -49,7 +49,6 @@ final class NamedQuery extends AbstractJpaQuery { private final String queryName; private final String countQueryName; private final @Nullable String countProjection; - private final QueryExtractor extractor; private final boolean namedCountQueryIsPresent; private final DeclaredQuery declaredQuery; private final QueryParameterSetter.QueryMetadataCache metadataCache; @@ -63,7 +62,7 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { this.queryName = method.getNamedQueryName(); this.countQueryName = method.getNamedCountQueryName(); - this.extractor = method.getQueryExtractor(); + QueryExtractor extractor = method.getQueryExtractor(); this.countProjection = method.getCountQueryProjection(); Parameters parameters = method.getParameters(); @@ -81,7 +80,7 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { this.declaredQuery = DeclaredQuery.of(queryString, false); boolean weNeedToCreateCountQuery = !namedCountQueryIsPresent && method.getParameters().hasPageableParameter(); - boolean cantExtractQuery = !this.extractor.canExtractQuery(); + boolean cantExtractQuery = !extractor.canExtractQuery(); if (weNeedToCreateCountQuery && cantExtractQuery) { throw QueryCreationException.create(method, CANNOT_EXTRACT_QUERY); @@ -99,9 +98,8 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { /** * Returns whether the named query with the given name exists. * - * @param em must not be {@literal null}. + * @param em must not be {@literal null}. * @param queryName must not be {@literal null}. - * @return */ static boolean hasNamedQuery(EntityManager em, String queryName) { @@ -129,8 +127,7 @@ static boolean hasNamedQuery(EntityManager em, String queryName) { * Looks up a named query for the given {@link org.springframework.data.repository.query.QueryMethod}. * * @param method must not be {@literal null}. - * @param em must not be {@literal null}. - * @return + * @param em must not be {@literal null}. */ @Nullable public static RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index 1ac5affdea..bba18ff9f4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -233,9 +233,9 @@ private Function getConversionFunction() { static class PredicateScrollDelegate extends ScrollDelegate { - private final ScrollQueryFactory scrollFunction; + private final ScrollQueryFactory scrollFunction; - PredicateScrollDelegate(ScrollQueryFactory scrollQueryFactory, JpaEntityInformation entity) { + PredicateScrollDelegate(ScrollQueryFactory scrollQueryFactory, JpaEntityInformation entity) { super(entity); this.scrollFunction = scrollQueryFactory; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java index d8193f52a4..287f825b79 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java @@ -224,9 +224,9 @@ private Function getConversionFunction() { static class SpecificationScrollDelegate extends ScrollDelegate { - private final ScrollQueryFactory scrollFunction; + private final ScrollQueryFactory scrollFunction; - SpecificationScrollDelegate(ScrollQueryFactory scrollQueryFactory, JpaEntityInformation entity) { + SpecificationScrollDelegate(ScrollQueryFactory scrollQueryFactory, JpaEntityInformation entity) { super(entity); this.scrollFunction = scrollQueryFactory; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java index 5aa8352ed8..4af8f84ee4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -86,7 +86,7 @@ final Function getConversionFunction(Class inputType, Class tar return o -> DefaultConversionService.getSharedInstance().convert(o, targetType); } - interface ScrollQueryFactory { + interface ScrollQueryFactory { Query createQuery(Sort sort, ScrollPosition scrollPosition); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java index f192386db0..d59e6316f9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -35,8 +35,6 @@ public interface JpaEntityInformation extends EntityInformation, J /** * Returns the id attribute of the entity. - * - * @return */ @Nullable SingularAttribute getIdAttribute(); @@ -62,25 +60,20 @@ public interface JpaEntityInformation extends EntityInformation, J /** * Returns {@literal true} if the entity has a composite id. - * - * @return */ boolean hasCompositeId(); /** * Returns the attribute names of the id attributes. If the entity has a composite id, then all id attribute names are * returned. If the entity has a single id attribute then this single attribute name is returned. - * - * @return */ Collection getIdAttributeNames(); /** * Extracts the value for the given id attribute from a composite id * - * @param id - * @param idAttribute - * @return + * @param id the composite id from which to extract the attribute. + * @param idAttribute the attribute name to extract. */ @Nullable Object getCompositeIdAttributeValue(Object id, String idAttribute); @@ -88,9 +81,9 @@ public interface JpaEntityInformation extends EntityInformation, J /** * Extract a keyset for {@code propertyPaths} and the primary key (including composite key components if applicable). * - * @param propertyPaths - * @param entity - * @return + * @param propertyPaths the property paths that make up the keyset in combination with the composite key components. + * @param entity the entity to extract values from + * @return a map mapping String representations of the paths to values from the entity. * @since 3.1 */ Map getKeyset(Iterable propertyPaths, T entity); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index c740887b27..dbf7285a00 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -85,12 +85,10 @@ public JpaMetamodelEntityInformation(Class domainClass, Metamodel metamodel, this.entityName = type instanceof EntityType ? ((EntityType) type).getName() : null; - if (!(type instanceof IdentifiableType)) { + if (!(type instanceof IdentifiableType identifiableType)) { throw new IllegalArgumentException("The given domain class does not contain an id attribute"); } - IdentifiableType identifiableType = (IdentifiableType) type; - this.idMetadata = new IdMetadata<>(identifiableType, PersistenceProvider.fromMetamodel(metamodel)); this.versionAttribute = findVersionAttribute(identifiableType, metamodel); @@ -108,7 +106,6 @@ public String getEntityName() { * * @param type must not be {@literal null}. * @param metamodel must not be {@literal null}. - * @return */ @SuppressWarnings("unchecked") private static Optional> findVersionAttribute(IdentifiableType type, @@ -262,7 +259,6 @@ private static class IdMetadata implements Iterable> attributes; private @Nullable Class idType; - @SuppressWarnings("unchecked") IdMetadata(IdentifiableType source, PersistenceProvider persistenceProvider) { this.type = source; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 2e38c4c2b3..25fb4abec2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -73,8 +73,6 @@ public Querydsl(EntityManager em, PathBuilder builder) { /** * Creates the {@link JPQLQuery} instance based on the configured {@link EntityManager}. - * - * @return */ public AbstractJPAQuery> createQuery() { @@ -93,7 +91,6 @@ public AbstractJPAQuery> createQuery() { * Creates the {@link JPQLQuery} instance based on the configured {@link EntityManager}. * * @param paths must not be {@literal null}. - * @return */ public AbstractJPAQuery> createQuery(EntityPath... paths) { @@ -167,7 +164,6 @@ private JPQLQuery addOrderByFrom(QSort qsort, JPQLQuery query) { * * @param sort must not be {@literal null}. * @param query must not be {@literal null}. - * @return */ private JPQLQuery addOrderByFrom(Sort sort, JPQLQuery query) { @@ -185,7 +181,6 @@ private JPQLQuery addOrderByFrom(Sort sort, JPQLQuery query) { * Transforms a plain {@link Order} into a QueryDsl specific {@link OrderSpecifier}. * * @param order must not be {@literal null}. - * @return */ @SuppressWarnings({ "rawtypes", "unchecked" }) private OrderSpecifier toOrderSpecifier(Order order) { @@ -200,7 +195,6 @@ private OrderSpecifier toOrderSpecifier(Order order) { * {@link NullHandling}. * * @param nullHandling must not be {@literal null}. - * @return * @since 1.6 */ private NullHandling toQueryDslNullHandling(org.springframework.data.domain.Sort.NullHandling nullHandling) { @@ -225,7 +219,6 @@ private NullHandling toQueryDslNullHandling(org.springframework.data.domain.Sort * Creates an {@link Expression} for the given {@link Order} property. * * @param order must not be {@literal null}. - * @return */ private Expression buildOrderPropertyPathFrom(Order order) { @@ -250,7 +243,6 @@ private Expression buildOrderPropertyPathFrom(Order order) { * Creates an {@link Expression} for the given {@code property} property. * * @param property must not be {@literal null}. - * @return */ Expression createExpression(String property) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index c899d051ab..1cfcd69bee 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -175,7 +175,7 @@ public R findBy(Predicate predicate, Function scroll = (sort, scrollPosition) -> { + ScrollQueryFactory scroll = (sort, scrollPosition) -> { Predicate predicateToUse = predicate; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 788ea6fae3..5358af188c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -521,7 +521,7 @@ private R doFindBy(Specification spec, Class domainClass, Assert.notNull(spec, "Specification must not be null"); Assert.notNull(queryFunction, "Query function must not be null"); - ScrollQueryFactory scrollFunction = (sort, scrollPosition) -> { + ScrollQueryFactory scrollFunction = (sort, scrollPosition) -> { Specification specToUse = spec; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java index 6fce01ece1..38c6b83b04 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java @@ -50,6 +50,7 @@ public Item(Integer id, Integer manufacturerId) { } public Item(Integer id, Integer manufacturerId, String name) { + this.id = id; this.manufacturerId = manufacturerId; this.name = name; From 1a381e5118c9a946b648e1e77dfa03cb8b2a594f Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 29 Mar 2023 10:25:21 -0500 Subject: [PATCH 353/821] Polishing. --- Jenkinsfile | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index fc44aa5924..ee3c0f6715 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ def p = [:] node { - checkout scm - p = readProperties interpolate: true, file: 'ci/pipeline.properties' + checkout scm + p = readProperties interpolate: true, file: 'ci/pipeline.properties' } pipeline { @@ -31,13 +31,11 @@ pipeline { } options { timeout(time: 30, unit: 'MINUTES') } environment { - DOCKER_HUB = credentials("${p['docker.credentials']}") ARTIFACTORY = credentials("${p['artifactory.credentials']}") } steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { - sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}" sh 'PROFILE=all-dbs ci/test.sh' sh "ci/clean.sh" } @@ -55,25 +53,23 @@ pipeline { } parallel { - stage("test: eclipselink-next") { + stage("test: eclipselink-next") { agent { - label 'data' + label 'data' } options { timeout(time: 30, unit: 'MINUTES')} environment { - DOCKER_HUB = credentials("${p['docker.credentials']}") - ARTIFACTORY = credentials("${p['artifactory.credentials']}") + ARTIFACTORY = credentials("${p['artifactory.credentials']}") } steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { - sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}" sh 'PROFILE=all-dbs,eclipselink-next ci/test.sh' sh "ci/clean.sh" } } } - } + } } } From 9293c911e9d48b79fbc7ac235513be02b5738bc4 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 5 Apr 2023 15:23:10 +0200 Subject: [PATCH 354/821] Fix naming of integration tests to make sure we can run the ones for Hibernate and Eclipselink independently. Ticket: #2899. --- ...ts.java => EclipseLinkQueryByExampleIntegrationTests.java} | 2 +- ...egrationTests.java => QueryByExampleIntegrationTests.java} | 2 +- ... EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java} | 2 +- ...sts.java => MetaAnnotatedQueryMethodIntegrationTests.java} | 2 +- ...ationTests.java => QueryWithNullLikeIntegrationTests.java} | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) rename spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/{QueryByExampleEclipseLinkIntegrationTests.java => EclipseLinkQueryByExampleIntegrationTests.java} (98%) rename spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/{QueryByExampleHibernateIntegrationTests.java => QueryByExampleIntegrationTests.java} (98%) rename spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/{MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java => EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java} (99%) rename spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/{MetaAnnotatedQueryMethodHibernateIntegrationTests.java => MetaAnnotatedQueryMethodIntegrationTests.java} (99%) rename spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/{QueryWithNullLikeHibernateIntegrationTests.java => QueryWithNullLikeIntegrationTests.java} (98%) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkQueryByExampleIntegrationTests.java similarity index 98% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkQueryByExampleIntegrationTests.java index b0abec19ee..69e900a1a0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleEclipseLinkIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkQueryByExampleIntegrationTests.java @@ -43,7 +43,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:eclipselink.xml", "classpath:config/namespace-application-context.xml" }) @Transactional -class QueryByExampleEclipseLinkIntegrationTests { +class EclipseLinkQueryByExampleIntegrationTests { @Autowired RoleRepository repository; @Autowired EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleIntegrationTests.java similarity index 98% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleIntegrationTests.java index 5d7c257f28..592d30d574 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/QueryByExampleIntegrationTests.java @@ -43,7 +43,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration({ "classpath:hibernate.xml", "classpath:config/namespace-application-context.xml" }) @Transactional -class QueryByExampleHibernateIntegrationTests { +class QueryByExampleIntegrationTests { @Autowired RoleRepository repository; @Autowired EntityManager em; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java similarity index 99% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java index 21673b6f97..70ac8cf11f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodEclipseLinkIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EclipseLinkMetaAnnotatedQueryMethodIntegrationTests.java @@ -69,7 +69,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration @Transactional -class MetaAnnotatedQueryMethodEclipseLinkIntegrationTests { +class EclipseLinkMetaAnnotatedQueryMethodIntegrationTests { @Autowired RoleRepositoryWithMeta repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodIntegrationTests.java similarity index 99% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodIntegrationTests.java index ae69439836..351373c4f8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodIntegrationTests.java @@ -67,7 +67,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration @Transactional -class MetaAnnotatedQueryMethodHibernateIntegrationTests { +class MetaAnnotatedQueryMethodIntegrationTests { @Autowired RoleRepositoryWithMeta repository; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java similarity index 98% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java index 37dbb792a9..9afd65db6b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeHibernateIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java @@ -55,9 +55,9 @@ * @author Yuriy Tsarkov */ @ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = QueryWithNullLikeHibernateIntegrationTests.Config.class) +@ContextConfiguration(classes = QueryWithNullLikeIntegrationTests.Config.class) @Transactional -class QueryWithNullLikeHibernateIntegrationTests { +class QueryWithNullLikeIntegrationTests { @Autowired EmployeeWithNullLikeRepository repository; From 372109738b35eacc26e238700bdc0c6345bee5c1 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 5 Apr 2023 14:55:47 +0200 Subject: [PATCH 355/821] Polishing. Avoid Hibernate debug logging to leak out of MetaAnnotatedQueryMethodHibernateIntegrationTests. Related ticket #2899. --- .../query/MetaAnnotatedQueryMethodIntegrationTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodIntegrationTests.java index 351373c4f8..7703e978d2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/MetaAnnotatedQueryMethodIntegrationTests.java @@ -85,6 +85,7 @@ void setUp() { @AfterEach void clearUp() { + testLogger.setLevel(Level.ERROR); testLogger.detachAppender(testAppender); } From 39e12eae6d64ba7192835e052a082b93b287d911 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 15 Mar 2023 11:08:30 -0500 Subject: [PATCH 356/821] Introduce support for ordering by aliased columns. If a projection of either an HQL or JPQL query is aliased, do NOT prefix the FROM clause's alias prefix to any relevant applied sorting. Same for function-based order by arguments. Resolves #2863. Related: #2626, #2322, #1655. Original pull request: #2865. --- .../data/jpa/repository/query/Jpql.g4 | 14 +- .../repository/query/HqlQueryTransformer.java | 75 ++++------ .../query/JpaQueryParserSupport.java | 26 ---- .../query/JpaQueryTransformerSupport.java | 134 ++++++++++++++++++ .../query/JpqlQueryTransformer.java | 59 ++++---- .../query/HqlQueryTransformerTests.java | 121 +++++++++++----- .../query/JpqlQueryTransformerTests.java | 51 +++---- 7 files changed, 306 insertions(+), 174 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 9bad5f4c72..00491c8415 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -68,11 +68,11 @@ identification_variable_declaration ; range_variable_declaration - : entity_name (AS)? identification_variable + : entity_name AS? identification_variable ; join - : join_spec join_association_path_expression (AS)? identification_variable (join_condition)? + : join_spec join_association_path_expression AS? identification_variable (join_condition)? ; fetch_join @@ -103,7 +103,7 @@ join_single_valued_path_expression ; collection_member_declaration - : IN '(' collection_valued_path_expression ')' (AS)? identification_variable + : IN '(' collection_valued_path_expression ')' AS? identification_variable ; qualified_identification_variable @@ -160,7 +160,7 @@ collection_valued_path_expression ; update_clause - : UPDATE entity_name ((AS)? identification_variable)? SET update_item (',' update_item)* + : UPDATE entity_name (AS? identification_variable)? SET update_item (',' update_item)* ; update_item @@ -174,7 +174,7 @@ new_value ; delete_clause - : DELETE FROM entity_name ((AS)? identification_variable)? + : DELETE FROM entity_name (AS? identification_variable)? ; select_clause @@ -182,7 +182,7 @@ select_clause ; select_item - : select_expression ((AS)? result_variable)? + : select_expression (AS? result_variable)? ; select_expression @@ -247,7 +247,7 @@ subquery_from_clause subselect_identification_variable_declaration : identification_variable_declaration - | derived_path_expression (AS)? identification_variable (join)* + | derived_path_expression AS? identification_variable (join)* | derived_collection_member_declaration ; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java index 906942f75f..18a40573e5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java @@ -41,13 +41,15 @@ class HqlQueryTransformer extends HqlQueryRenderer { private final @Nullable String countProjection; - private @Nullable String alias = null; + private @Nullable String primaryFromAlias = null; private List projection = Collections.emptyList(); private boolean projectionProcessed; private boolean hasConstructorExpression = false; + private JpaQueryTransformerSupport transformerSupport; + HqlQueryTransformer() { this(Sort.unsorted(), false, null); } @@ -67,11 +69,12 @@ private HqlQueryTransformer(Sort sort, boolean countQuery, @Nullable String coun this.sort = sort; this.countQuery = countQuery; this.countProjection = countProjection; + this.transformerSupport = new JpaQueryTransformerSupport(); } @Nullable public String getAlias() { - return this.alias; + return this.primaryFromAlias; } public List getProjection() { @@ -118,7 +121,7 @@ public List visitOrderedQuery(HqlParser.OrderedQueryContex tokens.addAll(visit(ctx.queryOrder())); } - if (this.sort.isSorted()) { + if (sort.isSorted()) { if (ctx.queryOrder() != null) { @@ -130,29 +133,7 @@ public List visitOrderedQuery(HqlParser.OrderedQueryContex tokens.add(TOKEN_ORDER_BY); } - this.sort.forEach(order -> { - - JpaQueryParserSupport.checkSortExpression(order); - - if (order.isIgnoreCase()) { - tokens.add(TOKEN_LOWER_FUNC); - } - tokens.add(new JpaQueryParsingToken(() -> { - - if (order.getProperty().contains("(")) { - return order.getProperty(); - } - - return this.alias + "." + order.getProperty(); - }, true)); - if (order.isIgnoreCase()) { - NOSPACE(tokens); - tokens.add(TOKEN_CLOSE_PAREN); - } - tokens.add(order.isDescending() ? TOKEN_DESC : TOKEN_ASC); - tokens.add(TOKEN_COMMA); - }); - CLIP(tokens); + tokens.addAll(transformerSupport.generateOrderByArguments(primaryFromAlias, sort)); } } else { @@ -176,7 +157,7 @@ public List visitFromQuery(HqlParser.FromQueryContext ctx) if (countProjection != null) { tokens.add(new JpaQueryParsingToken(countProjection)); } else { - tokens.add(new JpaQueryParsingToken(() -> this.alias, false)); + tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias, false)); } tokens.add(TOKEN_CLOSE_PAREN); @@ -240,8 +221,8 @@ public List visitFromRoot(HqlParser.FromRootContext ctx) { if (ctx.variable() != null) { tokens.addAll(visit(ctx.variable())); - if (this.alias == null && !isSubquery(ctx)) { - this.alias = tokens.get(tokens.size() - 1).getToken(); + if (primaryFromAlias == null && !isSubquery(ctx)) { + primaryFromAlias = tokens.get(tokens.size() - 1).getToken(); } } else { @@ -250,8 +231,8 @@ public List visitFromRoot(HqlParser.FromRootContext ctx) { tokens.add(TOKEN_AS); tokens.add(TOKEN_DOUBLE_UNDERSCORE); - if (this.alias == null && !isSubquery(ctx)) { - this.alias = TOKEN_DOUBLE_UNDERSCORE.getToken(); + if (primaryFromAlias == null && !isSubquery(ctx)) { + primaryFromAlias = TOKEN_DOUBLE_UNDERSCORE.getToken(); } } } @@ -267,8 +248,8 @@ public List visitFromRoot(HqlParser.FromRootContext ctx) { if (ctx.variable() != null) { tokens.addAll(visit(ctx.variable())); - if (this.alias == null && !isSubquery(ctx)) { - this.alias = tokens.get(tokens.size() - 1).getToken(); + if (primaryFromAlias == null && !isSubquery(ctx)) { + primaryFromAlias = tokens.get(tokens.size() - 1).getToken(); } } } @@ -302,16 +283,22 @@ public List visitJoin(HqlParser.JoinContext ctx) { @Override public List visitAlias(HqlParser.AliasContext ctx) { - List tokens = newArrayList(); + List tokens = super.visitAlias(ctx); - if (ctx.AS() != null) { - tokens.add(new JpaQueryParsingToken(ctx.AS())); + if (primaryFromAlias == null && !isSubquery(ctx)) { + primaryFromAlias = tokens.get(tokens.size() - 1).getToken(); } - tokens.addAll(visit(ctx.identifier())); + return tokens; + } + + @Override + public List visitVariable(HqlParser.VariableContext ctx) { + + List tokens = super.visitVariable(ctx); - if (this.alias == null && !isSubquery(ctx)) { - this.alias = tokens.get(tokens.size() - 1).getToken(); + if (ctx.identifier() != null) { + transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); } return tokens; @@ -346,13 +333,13 @@ public List visitSelectClause(HqlParser.SelectClauseContex if (selectionListTokens.stream().anyMatch(hqlToken -> hqlToken.getToken().contains("new"))) { // constructor - tokens.add(new JpaQueryParsingToken(() -> this.alias)); + tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias)); } else { // keep all the select items to distinct against tokens.addAll(selectionListTokens); } } else { - tokens.add(new JpaQueryParsingToken(() -> this.alias)); + tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias)); } } @@ -363,8 +350,8 @@ public List visitSelectClause(HqlParser.SelectClauseContex } if (!projectionProcessed && !isSubquery(ctx)) { - this.projection = selectionListTokens; - this.projectionProcessed = true; + projection = selectionListTokens; + projectionProcessed = true; } return tokens; @@ -373,7 +360,7 @@ public List visitSelectClause(HqlParser.SelectClauseContex @Override public List visitInstantiation(HqlParser.InstantiationContext ctx) { - this.hasConstructorExpression = true; + hasConstructorExpression = true; return super.visitInstantiation(ctx); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParserSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParserSupport.java index 892476ad23..a1a8665368 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParserSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParserSupport.java @@ -18,15 +18,12 @@ import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; import java.util.List; -import java.util.regex.Pattern; import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.atn.PredictionMode; -import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; -import org.springframework.data.jpa.domain.JpaSort; import org.springframework.data.util.Lazy; import org.springframework.lang.Nullable; @@ -39,12 +36,6 @@ */ abstract class JpaQueryParserSupport { - private static final Pattern PUNCTUATION_PATTERN = Pattern.compile(".*((?![._])[\\p{Punct}|\\s])"); - - private static final String UNSAFE_PROPERTY_REFERENCE = "Sort expression '%s' must only contain property references or " - + "aliases used in the select clause; If you really want to use something other than that for sorting, please use " - + "JpaSort.unsafe(…)"; - private final ParseState state; JpaQueryParserSupport(String query) { @@ -177,23 +168,6 @@ protected abstract List doCreateCountQuery(ParserRuleConte protected abstract boolean doCheckForConstructor(ParserRuleContext parsedQuery); - /** - * Check any given {@link JpaSort.JpaOrder#isUnsafe()} order for presence of at least one property offending the - * {@link #PUNCTUATION_PATTERN} and throw an {@link Exception} indicating potential unsafe order by expression. - * - * @param order - */ - static void checkSortExpression(Sort.Order order) { - - if (order instanceof JpaSort.JpaOrder && ((JpaSort.JpaOrder) order).isUnsafe()) { - return; - } - - if (PUNCTUATION_PATTERN.matcher(order.getProperty()).find()) { - throw new InvalidDataAccessApiUsageException(String.format(UNSAFE_PROPERTY_REFERENCE, order)); - } - } - /** * Parser state capturing the lazily-parsed parser context. */ diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java new file mode 100644 index 0000000000..acc73c772f --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java @@ -0,0 +1,134 @@ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.JpaSort; +import org.springframework.lang.Nullable; + +/** + * Transformational operations needed to support either {@link HqlQueryTransformer} or {@link JpqlQueryTransformer}. + * + * @author Greg Turnquist + * @since 3.1 + */ +class JpaQueryTransformerSupport { + + private static final Pattern PUNCTUATION_PATTERN = Pattern.compile(".*((?![._])[\\p{Punct}|\\s])"); + + private static final String UNSAFE_PROPERTY_REFERENCE = "Sort expression '%s' must only contain property references or " + + "aliases used in the select clause; If you really want to use something other than that for sorting, please use " + + "JpaSort.unsafe(…)"; + + private Set projectionAliases; + + JpaQueryTransformerSupport() { + this.projectionAliases = new HashSet<>(); + } + + /** + * Register an {@literal alias} so it can later be evaluated when applying {@link Sort}s. + * + * @param token + */ + void registerAlias(String token) { + projectionAliases.add(token); + } + + /** + * Using the primary {@literal FROM} clause's alias and a {@link Sort}, construct all the {@literal ORDER BY} + * arguments. + * + * @param primaryFromAlias + * @param sort + * @return + */ + List generateOrderByArguments(String primaryFromAlias, Sort sort) { + + List tokens = new ArrayList<>(); + + sort.forEach(order -> { + + checkSortExpression(order); + + if (order.isIgnoreCase()) { + tokens.add(TOKEN_LOWER_FUNC); + } + + tokens.add(new JpaQueryParsingToken(() -> generateOrderByArgument(primaryFromAlias, order))); + + if (order.isIgnoreCase()) { + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + tokens.add(order.isDescending() ? TOKEN_DESC : TOKEN_ASC); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + /** + * Check any given {@link JpaSort.JpaOrder#isUnsafe()} order for presence of at least one property offending the + * {@link #PUNCTUATION_PATTERN} and throw an {@link Exception} indicating potential unsafe order by expression. + * + * @param order + */ + private void checkSortExpression(Sort.Order order) { + + if (order instanceof JpaSort.JpaOrder && ((JpaSort.JpaOrder) order).isUnsafe()) { + return; + } + + if (PUNCTUATION_PATTERN.matcher(order.getProperty()).find()) { + throw new InvalidDataAccessApiUsageException(String.format(UNSAFE_PROPERTY_REFERENCE, order)); + } + } + + /** + * Using the {@code primaryFromAlias} and the {@link org.springframework.data.domain.Sort.Order}, construct a suitable + * argument to be added to an {@literal ORDER BY} expression. + * + * @param primaryFromAlias + * @param order + * @return + */ + private String generateOrderByArgument(@Nullable String primaryFromAlias, Sort.Order order) { + + if (shouldPrefixWithAlias(order)) { + return primaryFromAlias + "." + order.getProperty(); + } else { + return order.getProperty(); + } + } + + /** + * Determine when an {@link org.springframework.data.domain.Sort.Order} parameter should be prefixed with the primary + * FROM clause's alias. + * + * @param order + * @return boolean whether or not to apply the primary FROM clause's alias as a prefix + */ + private boolean shouldPrefixWithAlias(Sort.Order order) { + + // If the Sort contains a function + if (order.getProperty().contains("(")) { + return false; + } + + // If the Sort references an alias + if (projectionAliases.contains(order.getProperty())) { + return false; + } + + return true; + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java index 3749250ff6..a89abcb687 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java @@ -39,13 +39,15 @@ class JpqlQueryTransformer extends JpqlQueryRenderer { private final @Nullable String countProjection; - private @Nullable String alias = null; + private @Nullable String primaryFromAlias = null; private List projection = Collections.emptyList(); private boolean projectionProcessed; private boolean hasConstructorExpression = false; + private JpaQueryTransformerSupport transformerSupport; + JpqlQueryTransformer() { this(Sort.unsorted(), false, null); } @@ -65,11 +67,12 @@ private JpqlQueryTransformer(Sort sort, boolean countQuery, @Nullable String cou this.sort = sort; this.countQuery = countQuery; this.countProjection = countProjection; + this.transformerSupport = new JpaQueryTransformerSupport(); } @Nullable public String getAlias() { - return this.alias; + return this.primaryFromAlias; } public List getProjection() { @@ -106,7 +109,7 @@ public List visitSelect_statement(JpqlParser.Select_statem tokens.addAll(visit(ctx.orderby_clause())); } - if (this.sort.isSorted()) { + if (sort.isSorted()) { if (ctx.orderby_clause() != null) { @@ -118,29 +121,7 @@ public List visitSelect_statement(JpqlParser.Select_statem tokens.add(TOKEN_ORDER_BY); } - this.sort.forEach(order -> { - - JpaQueryParserSupport.checkSortExpression(order); - - if (order.isIgnoreCase()) { - tokens.add(TOKEN_LOWER_FUNC); - } - tokens.add(new JpaQueryParsingToken(() -> { - - if (order.getProperty().contains("(")) { - return order.getProperty(); - } - - return this.alias + "." + order.getProperty(); - }, true)); - if (order.isIgnoreCase()) { - NOSPACE(tokens); - tokens.add(TOKEN_CLOSE_PAREN); - } - tokens.add(order.isDescending() ? TOKEN_DESC : TOKEN_ASC); - tokens.add(TOKEN_COMMA); - }); - CLIP(tokens); + tokens.addAll(transformerSupport.generateOrderByArguments(primaryFromAlias, sort)); } } @@ -182,13 +163,13 @@ public List visitSelect_clause(JpqlParser.Select_clauseCon if (selectItemTokens.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) { // constructor - tokens.add(new JpaQueryParsingToken(() -> this.alias)); + tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias)); } else { // keep all the select items to distinct against tokens.addAll(selectItemTokens); } } else { - tokens.add(new JpaQueryParsingToken(() -> this.alias)); + tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias)); } } @@ -199,8 +180,20 @@ public List visitSelect_clause(JpqlParser.Select_clauseCon } if (!projectionProcessed) { - this.projection = selectItemTokens; - this.projectionProcessed = true; + projection = selectItemTokens; + projectionProcessed = true; + } + + return tokens; + } + + @Override + public List visitSelect_item(JpqlParser.Select_itemContext ctx) { + + List tokens = super.visitSelect_item(ctx); + + if (ctx.result_variable() != null) { + transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); } return tokens; @@ -219,8 +212,8 @@ public List visitRange_variable_declaration(JpqlParser.Ran tokens.addAll(visit(ctx.identification_variable())); - if (this.alias == null) { - this.alias = tokens.get(tokens.size() - 1).getToken(); + if (primaryFromAlias == null) { + primaryFromAlias = tokens.get(tokens.size() - 1).getToken(); } return tokens; @@ -229,7 +222,7 @@ public List visitRange_variable_declaration(JpqlParser.Ran @Override public List visitConstructor_expression(JpqlParser.Constructor_expressionContext ctx) { - this.hasConstructorExpression = true; + hasConstructorExpression = true; return super.visitConstructor_expression(ctx); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index 6d33af2fe0..0b691a4c35 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.JpaSort; import org.springframework.lang.Nullable; @@ -392,94 +393,85 @@ void doesNotPrefixUnsafeJpaSortFunctionCalls() { assertThat(createQueryFor("select p from Person p", sort)).endsWith("order by sum(foo) asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixMultipleAliasedFunctionCalls() { String query = "SELECT AVG(m.price) AS avgPrice, SUM(m.stocks) AS sumStocks FROM Magazine m"; Sort sort = Sort.by("avgPrice", "sumStocks"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc, sumStocks asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc, sumStocks asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixSingleAliasedFunctionCalls() { String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("avgPrice"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("someOtherProperty"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by m.someOtherProperty asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by m.someOtherProperty asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainsAliasedFunctionForDifferentProperty() { String query = "SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("name", "avgPrice"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by m.name asc, avgPrice asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by m.name asc, avgPrice asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() { String query = "SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m"; Sort sort = Sort.by("trimmedName"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by trimmedName asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by trimmedName asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() { String query = "SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m"; Sort sort = Sort.by("extendedName"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by extendedName asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by extendedName asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { String query = "SELECT AVG(m.price) AS avg_price FROM Magazine m"; Sort sort = Sort.by("avg_price"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by avg_price asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by avg_price asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWithDots() { String query = "SELECT AVG(m.price) AS m.avg FROM Magazine m"; Sort sort = Sort.by("m.avg"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by m.avg asc"); + assertThatIllegalArgumentException().isThrownBy(() -> createQueryFor(query, sort)); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpaces() { String query = "SELECT AVG( m.price ) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("avgPrice"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc"); } @Test // DATAJPA-1506 @@ -529,7 +521,7 @@ select count(user) from User user """); } - @Test // DATAJPA-1061 + @Test // DATAJPA-1061, GH-2863 void appliesSortCorrectlyForFieldAliases() { String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; @@ -537,10 +529,10 @@ void appliesSortCorrectlyForFieldAliases() { String fullQuery = createQueryFor(query, sort); - assertThat(fullQuery).endsWith("order by m.authorName asc"); + assertThat(fullQuery).endsWith("order by authorName asc"); } - @Test // GH-2280 + @Test // GH-2280, GH-2863 void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { String query = "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer"; @@ -549,10 +541,10 @@ void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { String fullQuery = createQueryFor(query, sort); assertThat(fullQuery).isEqualTo( - "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(customer.name) asc"); + "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(name) asc"); } - @Test // DATAJPA-1061 + @Test // DATAJPA-1061, GH-2863 void appliesSortCorrectlyForFunctionAliases() { String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; @@ -560,7 +552,7 @@ void appliesSortCorrectlyForFunctionAliases() { String fullQuery = createQueryFor(query, sort); - assertThat(fullQuery).endsWith("order by m.title asc"); + assertThat(fullQuery).endsWith("order by title asc"); } @Test // DATAJPA-1061 @@ -902,7 +894,6 @@ void detectAliasWithCastCorrectly() { .isEqualTo("u"); } - @Test // GH-2348 void removeFetchFromJoinsDuringCountQueryCreation() { @@ -913,6 +904,68 @@ void removeFetchFromJoinsDuringCountQueryCreation() { "SELECT count(DISTINCT b) FROM Board b LEFT JOIN b.comments"); } + @Test // GH-2626, GH-2863 + void orderByAliasedColumn() { + + assertThat(createQueryFor(""" + select + max(resource.name) as resourceName, + max(resource.id) as id, + max(resource.description) as description, + max(resource.uuid) as uuid, + max(resource.type) as type, + max(resource.createdOn) as createdOn, + max(users.firstName) as authorFirstName, + max(users.lastName) as authorLastName, + max(file.version) as version, + max(file.comment) as comment, + file.deployed as deployed, + max(log.date) as modifiedOn + from Resource resource + where ( + cast(:startDate as date) is null + or resource.latestLogRecord.date between cast(:startDate as date) and cast(:endDate as date) + ) + group by resource.id, file.deployed, log.author.firstName, file.comment + """, Sort.by(Sort.Direction.DESC, "uuid"))).endsWith("order by uuid desc"); + } + + @Test // GH-2863, GH-2322 + void sortShouldWorkWhenAliasingFunctions() { + + assertThat(createQueryFor(""" + SELECT + DISTINCT(event.id) as id, + event.name as name, + MIN(bundle.base_price_amount) as cheapestBundlePrice, + MIN(DATE(bundle.start)) as earliestBundleStart + FROM event event + LEFT JOIN bundle bundle ON event.id = bundle.event_id + GROUP BY event.id + """, Sort.by(Sort.Direction.ASC, "cheapestBundlePrice") // + .and(Sort.by(Sort.Direction.ASC, "earliestBundleStart")) // + .and(Sort.by(Sort.Direction.ASC, "name")))) + .endsWith(" order by cheapestBundlePrice asc, earliestBundleStart asc, name asc"); + } + + @Test // GH-2863, GH-1655 + void shouldHandleAliasInsideCaseStatement() { + + Sort sort = PageRequest.of(0, 20, Sort.Direction.DESC, "newDateDue").getSort(); + + assertThat(createQueryFor("Select DISTINCT new " + // + "com.api.dto.FilterDTO(c.id, p.id, CASE WHEN item.dateDue IS NOT NULL THEN item.dateDue ELSE p.dateDue END AS newDateDue) " + + "FROM Customer c " + // + "join c.productOrder p " + // + "JOIN p.items item", // + sort)).isEqualTo("Select DISTINCT new " + // + "com.api.dto.FilterDTO(c.id, p.id, CASE WHEN item.dateDue IS NOT NULL THEN item.dateDue ELSE p.dateDue END AS newDateDue) " + + "FROM Customer c " + // + "join c.productOrder p " + // + "JOIN p.items item " + // + "order by newDateDue desc"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java index c9beede02f..bd2b6a4696 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java @@ -383,94 +383,85 @@ void doesNotPrefixUnsafeJpaSortFunctionCalls() { assertThat(createQueryFor("select p from Person p", sort)).endsWith("order by sum(foo) asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixMultipleAliasedFunctionCalls() { String query = "SELECT AVG(m.price) AS avgPrice, SUM(m.stocks) AS sumStocks FROM Magazine m"; Sort sort = Sort.by("avgPrice", "sumStocks"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc, sumStocks asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc, sumStocks asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixSingleAliasedFunctionCalls() { String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("avgPrice"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by avgPrice asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("someOtherProperty"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by m.someOtherProperty asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by m.someOtherProperty asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainsAliasedFunctionForDifferentProperty() { String query = "SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("name", "avgPrice"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by m.name asc, avgPrice asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by m.name asc, avgPrice asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() { String query = "SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m"; Sort sort = Sort.by("trimmedName"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by trimmedName asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by trimmedName asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() { String query = "SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m"; Sort sort = Sort.by("extendedName"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by extendedName asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by extendedName asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { String query = "SELECT AVG(m.price) AS avg_price FROM Magazine m"; Sort sort = Sort.by("avg_price"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by avg_price asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by avg_price asc"); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWithDots() { String query = "SELECT AVG(m.price) AS m.avg FROM Magazine m"; Sort sort = Sort.by("m.avg"); - // TODO: Add support for aliased functions - // assertThat(query(query, (Sort) "m")).endsWith("order by m.avg asc"); + assertThatIllegalArgumentException().isThrownBy(() -> createQueryFor(query, sort)); } - @Test // DATAJPA-965, DATAJPA-970 + @Test // DATAJPA-965, DATAJPA-970, GH-2863 void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpaces() { String query = "SELECT AVG( m.price ) AS avgPrice FROM Magazine m"; Sort sort = Sort.by("avgPrice"); - // TODO: Add support for aliased functions - // assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc"); } @Test // DATAJPA-1506 @@ -528,7 +519,7 @@ void appliesSortCorrectlyForFieldAliases() { String fullQuery = createQueryFor(query, sort); - assertThat(fullQuery).endsWith("order by m.authorName asc"); + assertThat(fullQuery).endsWith("order by authorName asc"); } @Test // GH-2280 @@ -540,7 +531,7 @@ void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { String fullQuery = createQueryFor(query, sort); assertThat(fullQuery).isEqualTo( - "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(customer.name) asc"); + "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(name) asc"); } @Test // DATAJPA-1061 @@ -551,7 +542,7 @@ void appliesSortCorrectlyForFunctionAliases() { String fullQuery = createQueryFor(query, sort); - assertThat(fullQuery).endsWith("order by m.title asc"); + assertThat(fullQuery).endsWith("order by title asc"); } @Test // DATAJPA-1061 From 415c7b222231cc438ac2897c7aef5b3fa1547152 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 6 Apr 2023 16:16:24 +0200 Subject: [PATCH 357/821] Upgrade to Maven Wrapper 3.9.1. See #2905 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index bae44aada3..b54a804bd3 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Mon Feb 20 11:58:03 CET 2023 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.0/apache-maven-3.9.0-bin.zip +#Thu Apr 06 16:16:24 CEST 2023 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip From 5e9198e2b7c72df824ccf35ba2bdb22ebba7cccb Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 6 Apr 2023 17:04:47 +0200 Subject: [PATCH 358/821] Remove superfluous plugin repository declaration. See #2873 --- pom.xml | 15 --------------- settings.xml | 7 +------ 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index c77686453f..ca4da8012c 100644 --- a/pom.xml +++ b/pom.xml @@ -231,19 +231,4 @@ - - - spring-plugins-release - https://repo.spring.io/plugins-release - - - spring-libs-milestone - https://repo.spring.io/libs-milestone - - - spring-libs-snapshot - https://repo.spring.io/libs-snapshot - - - diff --git a/settings.xml b/settings.xml index b3227cc110..00cdfe00ba 100644 --- a/settings.xml +++ b/settings.xml @@ -4,11 +4,6 @@ https://maven.apache.org/xsd/settings-1.0.0.xsd"> - - spring-plugins-release - ${env.ARTIFACTORY_USR} - ${env.ARTIFACTORY_PSW} - spring-libs-snapshot ${env.ARTIFACTORY_USR} @@ -26,4 +21,4 @@ - \ No newline at end of file + From 5086764e6183a5930b27b6448d3ba8827721eaa5 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 6 Apr 2023 12:08:33 -0500 Subject: [PATCH 359/821] Polish repository settings for build jobs. See #2873. --- pom.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pom.xml b/pom.xml index ca4da8012c..ca3be69572 100644 --- a/pom.xml +++ b/pom.xml @@ -228,7 +228,23 @@ spring-libs-snapshot https://repo.spring.io/libs-snapshot + + true + + + + spring-libs-milestone + https://repo.spring.io/libs-milestone + + + spring-libs-snapshot + https://repo.spring.io/libs-snapshot + + true + + + From efd572aca89920ee73709e4526dd235a095e62af Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 29 Mar 2023 10:29:42 -0500 Subject: [PATCH 360/821] Test against Java 20 in CI. Resolves #2892. --- Jenkinsfile | 19 ++++++++++++++++++- pom.xml | 2 -- spring-data-jpa/pom.xml | 31 +++++++------------------------ 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ee3c0f6715..199996f064 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -18,7 +18,7 @@ pipeline { } stages { - stage("test: baseline (Java 17)") { + stage("test: baseline (main)") { when { beforeAgent(true) anyOf { @@ -53,6 +53,23 @@ pipeline { } parallel { + stage("test: baseline (next)") { + agent { + label 'data' + } + options { timeout(time: 30, unit: 'MINUTES')} + environment { + ARTIFACTORY = credentials("${p['artifactory.credentials']}") + } + steps { + script { + docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { + sh 'PROFILE=all-dbs ci/test.sh' + sh "ci/clean.sh" + } + } + } + } stage("test: eclipselink-next") { agent { label 'data' diff --git a/pom.xml b/pom.xml index ca3be69572..b724b3a967 100644 --- a/pom.xml +++ b/pom.xml @@ -181,7 +181,6 @@ -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar - -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} @@ -196,7 +195,6 @@ **/EclipseLink*Tests.java - -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 27313e9e3c..c8fcf610a1 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -155,8 +155,14 @@ hibernate-core ${hibernate} true + + + net.bytebuddy + byte-buddy + + - + ${hibernate.groupId}.orm hibernate-jpamodelgen @@ -252,27 +258,6 @@ - - - org.jacoco - jacoco-maven-plugin - ${jacoco} - - ${jacoco.destfile} - - - - jacoco-initialize - - prepare-agent - - - - - org.apache.maven.plugins maven-surefire-plugin @@ -326,7 +311,6 @@ -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar - -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} @@ -341,7 +325,6 @@ **/EclipseLink*Tests.java - -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} -javaagent:${settings.localRepository}/org/eclipse/persistence/org.eclipse.persistence.jpa/${eclipselink}/org.eclipse.persistence.jpa-${eclipselink}.jar -javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring}/spring-instrument-${spring}.jar From 0c90ff375905dd742d382a1780bb6efc529137a5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 12 Apr 2023 11:32:20 +0200 Subject: [PATCH 361/821] Adopt to changed Hibernate behavior returning domain types using tuple queries. We now allow using the domain type when it is returned from a tuple query. This allows projections if the return value is not a Map. Closes #2815 --- .../repository/query/AbstractJpaQuery.java | 5 ++-- .../repository/UserRepositoryFinderTests.java | 23 +++++++++++++++++-- .../jpa/repository/sample/UserRepository.java | 7 ++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 70ec15d0e6..7e70d350f2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -314,18 +314,17 @@ public TupleConverter(ReturnedType type) { @Override public Object convert(Object source) { - if (!(source instanceof Tuple)) { + if (!(source instanceof Tuple tuple)) { return source; } - Tuple tuple = (Tuple) source; List> elements = tuple.getElements(); if (elements.size() == 1) { Object value = tuple.get(elements.get(0)); - if (type.isInstance(value) || value == null) { + if (type.getDomainType().isInstance(value) || type.isInstance(value) || value == null) { return value; } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 4d5522244a..6fa3e2b00f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -40,6 +40,8 @@ import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.sample.RoleRepository; import org.springframework.data.jpa.repository.sample.UserRepository; +import org.springframework.data.jpa.repository.sample.UserRepository.IdOnly; +import org.springframework.data.jpa.repository.sample.UserRepository.RolesAndFirstname; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -281,11 +283,28 @@ void translatesNotContainsToNotMemberOf() { .containsExactlyInAnyOrder(dave, oliver); } - @Test // DATAJPA-974 + @Test // DATAJPA-974, GH-2815 void executesQueryWithProjectionContainingReferenceToPluralAttribute() { - assertThat(userRepository.findRolesAndFirstnameBy()) // + List rolesAndFirstnameBy = userRepository.findRolesAndFirstnameBy(); + + assertThat(rolesAndFirstnameBy) .isNotNull(); + + for (RolesAndFirstname rolesAndFirstname : rolesAndFirstnameBy) { + assertThat(rolesAndFirstname.getFirstname()).isNotNull(); + assertThat(rolesAndFirstname.getRoles()).isNotNull(); + } + } + + @Test // GH-2815 + void executesQueryWithProjectionThroughStringQuery() { + + List ids = userRepository.findIdOnly(); + + assertThat(ids).isNotNull(); + + assertThat(ids).extracting(IdOnly::getId).doesNotContainNull(); } @Test // DATAJPA-1023, DATACMNS-959 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 24362a2a77..5eb7490398 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 @@ -556,6 +556,9 @@ List findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity List findRolesAndFirstnameBy(); + @Query(value = "FROM User u") + List findIdOnly(); + // DATAJPA-1172 @Query("select u from User u where u.age = :age") List findByStringAge(@Param("age") String age); @@ -732,4 +735,8 @@ interface NameOnly { interface EmailOnly { String getEmailAddress(); } + + interface IdOnly { + int getId(); + } } From efd799c4de3bb357e36878ab515875b91cc6e74a Mon Sep 17 00:00:00 2001 From: Fouad Almalki Date: Wed, 5 Apr 2023 06:12:57 +0300 Subject: [PATCH 362/821] Upgrade to Hibernate 6.2. Upgrade Postgres to 10 for stored procedure related tests. Move off deprecated Hibernate Postgres 9.1 dialect. Switch order of persistence provider dependencies. Hibernate 6.2 needs to be declared before Eclipselink as the latter pulls in JPA 3.0 but the former now strongly requires 3.1. Related tickets: #2899. --- pom.xml | 2 +- spring-data-jpa/pom.xml | 14 +++++++------- .../PostgresStoredProcedureIntegrationTests.java | 6 +++--- ...toredProcedureNullHandlingIntegrationTests.java | 6 +++--- .../src/test/resources/META-INF/persistence.xml | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index b724b3a967..cddfd1c0d2 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 4.10.1 3.0.3 - 6.1.4.Final + 6.2.0.Final 2.7.1 4.5 8.0.31 diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index c8fcf610a1..ea9125d029 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -143,13 +143,6 @@ - - org.eclipse.persistence - org.eclipse.persistence.jpa - ${eclipselink} - true - - ${hibernate.groupId}.orm hibernate-core @@ -183,6 +176,13 @@ ${jakarta-annotation-api} + + org.eclipse.persistence + org.eclipse.persistence.jpa + ${eclipselink} + true + + com.querydsl diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index 48907cbccb..88e560b128 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -35,7 +35,7 @@ import javax.sql.DataSource; -import org.hibernate.dialect.PostgreSQL91Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.postgresql.ds.PGSimpleDataSource; @@ -201,7 +201,7 @@ static class Config { @Bean(initMethod = "start", destroyMethod = "stop") public PostgreSQLContainer container() { - return new PostgreSQLContainer<>("postgres:9.6.12") // + return new PostgreSQLContainer<>("postgres:10.21") // .withUsername("postgres"); } @@ -226,7 +226,7 @@ public AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSour Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "create"); - properties.setProperty("hibernate.dialect", PostgreSQL91Dialect.class.getCanonicalName()); + properties.setProperty("hibernate.dialect", PostgreSQLDialect.class.getCanonicalName()); factoryBean.setJpaProperties(properties); return factoryBean; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java index 2d508020d3..10aa204f37 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java @@ -31,7 +31,7 @@ import javax.sql.DataSource; -import org.hibernate.dialect.PostgreSQL91Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.postgresql.ds.PGSimpleDataSource; @@ -109,7 +109,7 @@ static class Config { @Bean(initMethod = "start", destroyMethod = "stop") public PostgreSQLContainer container() { - return new PostgreSQLContainer<>("postgres:9.6.12") // + return new PostgreSQLContainer<>("postgres:10.21") // .withUsername("postgres"); } @@ -135,7 +135,7 @@ public AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSour Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "create"); - properties.setProperty("hibernate.dialect", PostgreSQL91Dialect.class.getCanonicalName()); + properties.setProperty("hibernate.dialect", PostgreSQLDialect.class.getCanonicalName()); properties.setProperty("hibernate.proc.param_null_passing", "true"); properties.setProperty("hibernate.globally_quoted_identifiers", "true"); properties.setProperty("hibernate.globally_quoted_identifiers_skip_column_definitions", "true"); diff --git a/spring-data-jpa/src/test/resources/META-INF/persistence.xml b/spring-data-jpa/src/test/resources/META-INF/persistence.xml index 280312885a..c5e0e90b06 100644 --- a/spring-data-jpa/src/test/resources/META-INF/persistence.xml +++ b/spring-data-jpa/src/test/resources/META-INF/persistence.xml @@ -177,7 +177,7 @@ org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId true - + @@ -195,7 +195,7 @@ org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId true - + From c370425721929ca7c145ddfae63336533247ae8d Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 11 Apr 2023 17:18:25 +0200 Subject: [PATCH 363/821] Avoid strong proxy checks in test case for identifier access. Under still to clarify circumstances a class processed by Hibernate might result in proxies *not* created for a otherwise proxied relationship. So far, our tests have relied on those cases always return a proxy reliably but some optimizations in Hibernate 6.2 (likely [0]) don't allow creating proxies reliably. Until we find a better way to reliably create a proxy we back off from strictly checking whether we deal with a proxy. Related tickets: #2899. [0] https://hibernate.atlassian.net/browse/HHH-15790 --- .../JpaMetamodelMappingContextIntegrationTests.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java index 127abcedd7..5febb4b46f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContextIntegrationTests.java @@ -17,16 +17,15 @@ import static org.assertj.core.api.Assertions.*; -import java.util.Collections; - import jakarta.persistence.EntityManager; +import java.util.Collections; + import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; @@ -145,8 +144,10 @@ void lookingUpIdentifierOfProxyDoesNotInitializeProxy() { IdentifierAccessor accessor = entity.getIdentifierAccessor(loadedProduct); assertThat(accessor.getIdentifier()).isEqualTo(category.getProduct().getId()); - assertThat(loadedProduct).isInstanceOf(HibernateProxy.class); - assertThat(((HibernateProxy) loadedProduct).getHibernateLazyInitializer().isUninitialized()).isTrue(); + + if (loadedProduct instanceof HibernateProxy proxy) { + assertThat(proxy.getHibernateLazyInitializer().isUninitialized()).isTrue(); + } status.setRollbackOnly(); From 7729747163544df331b09a3a3836c52ab506757c Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 12 Apr 2023 11:33:26 +0200 Subject: [PATCH 364/821] Temporarily disable stored procedure tests for Postgres. On Hibernate 6.2 these tests run into class loading problems on CI (likely related to [0]). Cleanup is tracked in #2911. Related ticket: #2899. [0 https://hibernate.atlassian.net/browse/HHH-15790] --- .../procedures/PostgresStoredProcedureIntegrationTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index 88e560b128..b6997ef259 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -36,6 +36,7 @@ import javax.sql.DataSource; import org.hibernate.dialect.PostgreSQLDialect; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.postgresql.ds.PGSimpleDataSource; @@ -67,6 +68,7 @@ * @author Greg Turnquist * @author Yanming Zhou */ +@Disabled @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = PostgresStoredProcedureIntegrationTests.Config.class) From 7ed12765dc2ef534af2082353c5e5a12ebee6ae8 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 14 Apr 2023 09:14:54 -0700 Subject: [PATCH 365/821] Upgrade to Hibernate 6.2.1.Final. Closes #2917. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cddfd1c0d2..8f57f74bac 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 4.10.1 3.0.3 - 6.2.0.Final + 6.2.1.Final 2.7.1 4.5 8.0.31 From ff52cf341c71d085e63f65a0b7941e711362e68e Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 14 Apr 2023 11:53:17 -0500 Subject: [PATCH 366/821] Prepare 3.1 RC1 (2023.0.0). See #2873 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 8f57f74bac..9b17b91fb0 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-SNAPSHOT + 3.1.0-RC1 @@ -37,7 +37,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-SNAPSHOT + 3.1.0-RC1 0.10.3 org.hibernate @@ -224,8 +224,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-milestone + https://repo.spring.io/libs-milestone true From 01a0e5b156c8282d5e82bab246ad57a31adc5984 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 14 Apr 2023 11:53:58 -0500 Subject: [PATCH 367/821] Release version 3.1 RC1 (2023.0.0). See #2873 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 9b17b91fb0..ec653385c6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-RC1 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index db915d7c3b..470a1e663b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-SNAPSHOT + 3.1.0-RC1 org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-RC1 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a5cb2f09b5..ee198ab3fd 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-RC1 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index ea9125d029..113e46593e 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-SNAPSHOT + 3.1.0-RC1 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0-RC1 ../pom.xml From 699b18f30797a1fcaeb6fa2fa04ad130ce922fc0 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 14 Apr 2023 11:59:55 -0500 Subject: [PATCH 368/821] Prepare next development iteration. See #2873 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index ec653385c6..9b17b91fb0 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-RC1 + 3.1.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 470a1e663b..db915d7c3b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-RC1 + 3.1.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.1.0-RC1 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index ee198ab3fd..a5cb2f09b5 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-RC1 + 3.1.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 113e46593e..ea9125d029 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-RC1 + 3.1.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-RC1 + 3.1.0-SNAPSHOT ../pom.xml From 9fae43d85bac4034cacc613044f5d1d6716e1baa Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 14 Apr 2023 12:00:01 -0500 Subject: [PATCH 369/821] After release cleanups. See #2873 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 9b17b91fb0..8f57f74bac 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-RC1 + 3.1.0-SNAPSHOT @@ -37,7 +37,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-RC1 + 3.1.0-SNAPSHOT 0.10.3 org.hibernate @@ -224,8 +224,8 @@ - spring-libs-milestone - https://repo.spring.io/libs-milestone + spring-libs-snapshot + https://repo.spring.io/libs-snapshot true From 393fa635bd4ecf71a5aa4af951c2acbb898290de Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 18 Apr 2023 10:25:14 +0200 Subject: [PATCH 370/821] Added missing null check for metadata. See #2716 Closes #2897 --- .../data/jpa/repository/support/SimpleJpaRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 5358af188c..e537c9537d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -309,7 +309,7 @@ public Optional findById(ID id) { getQueryHints().withFetchGraphs(em).forEach(hints::put); - if (metadata.getComment() != null && provider.getCommentHintKey() != null) { + if (metadata != null && metadata.getComment() != null && provider.getCommentHintKey() != null) { hints.put(provider.getCommentHintKey(), provider.getCommentHintValue(metadata.getComment())); } From 1bcdbc27e788eb3ecf87d3875fe352d332cfeb2b Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 18 Apr 2023 15:47:50 +0200 Subject: [PATCH 371/821] Pagination now supports sorting by property. Closes https://github.com/spring-projects/spring-data-envers/issues/379 Original Pull Request https://github.com/spring-projects/spring-data-envers/pull/381 --- .../support/EnversRevisionRepositoryImpl.java | 78 +++++++++++++------ .../support/RepositoryIntegrationTests.java | 58 ++++++++------ .../data/envers/sample/Country.java | 6 ++ 3 files changed, 98 insertions(+), 44 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index 2309fe1ebd..259f510edb 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -15,14 +15,7 @@ */ package org.springframework.data.envers.repository.support; -import static org.springframework.data.history.RevisionMetadata.RevisionType.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - import jakarta.persistence.EntityManager; - import org.hibernate.Hibernate; import org.hibernate.envers.AuditReader; import org.hibernate.envers.AuditReaderFactory; @@ -32,10 +25,12 @@ import org.hibernate.envers.RevisionType; import org.hibernate.envers.query.AuditEntity; import org.hibernate.envers.query.AuditQuery; +import org.hibernate.envers.query.criteria.AuditProperty; import org.hibernate.envers.query.order.AuditOrder; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.history.AnnotationRevisionMetadata; import org.springframework.data.history.Revision; import org.springframework.data.history.RevisionMetadata; @@ -48,6 +43,13 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.springframework.data.history.RevisionMetadata.RevisionType.*; + /** * Repository implementation using Hibernate Envers to implement revision specific query methods. * @@ -58,6 +60,7 @@ * @author Julien Millau * @author Mark Paluch * @author Sander Bylemans + * @author Niklas Loechte */ @Transactional(readOnly = true) public class EnversRevisionRepositoryImpl> @@ -70,14 +73,14 @@ public class EnversRevisionRepositoryImpl entityInformation, - RevisionEntityInformation revisionEntityInformation, EntityManager entityManager) { + RevisionEntityInformation revisionEntityInformation, EntityManager entityManager) { - Assert.notNull(revisionEntityInformation, "RevisionEntityInformation must not be null"); + Assert.notNull(revisionEntityInformation, "RevisionEntityInformation must not be null!"); this.entityInformation = entityInformation; this.entityManager = entityManager; @@ -91,7 +94,7 @@ public Optional> findLastChangeRevision(ID id) { .setMaxResults(1) // .getResultList(); - Assert.state(singleResult.size() <= 1, "We expect at most one result"); + Assert.state(singleResult.size() <= 1, "We expect at most one result."); if (singleResult.isEmpty()) { return Optional.empty(); @@ -104,14 +107,14 @@ public Optional> findLastChangeRevision(ID id) { @SuppressWarnings("unchecked") public Optional> findRevision(ID id, N revisionNumber) { - Assert.notNull(id, "Identifier must not be null"); - Assert.notNull(revisionNumber, "Revision number must not be null"); + Assert.notNull(id, "Identifier must not be null!"); + Assert.notNull(revisionNumber, "Revision number must not be null!"); List singleResult = (List) createBaseQuery(id) // .add(AuditEntity.revisionNumber().eq(revisionNumber)) // .getResultList(); - Assert.state(singleResult.size() <= 1, "We expect at most one result"); + Assert.state(singleResult.size() <= 1, "We expect at most one result."); if (singleResult.isEmpty()) { return Optional.empty(); @@ -133,15 +136,46 @@ public Revisions findRevisions(ID id) { return Revisions.of(revisionList); } - @SuppressWarnings("unchecked") - public Page> findRevisions(ID id, Pageable pageable) { - AuditOrder sorting = RevisionSort.getRevisionDirection(pageable.getSort()).isDescending() // + private AuditOrder mapRevisionSort(RevisionSort revisionSort) { + + return RevisionSort.getRevisionDirection(revisionSort).isDescending() // ? AuditEntity.revisionNumber().desc() // : AuditEntity.revisionNumber().asc(); + } + + private List mapPropertySort(Sort sort) { + + if (sort.isEmpty()) { + return Collections.singletonList(AuditEntity.revisionNumber().asc()); + } + + List result = new ArrayList<>(); + for (Sort.Order order : sort) { + + AuditProperty property = AuditEntity.property(order.getProperty()); + AuditOrder auditOrder = order.getDirection().isAscending() ? + property.asc() : + property.desc(); + + result.add(auditOrder); + } + + return result; + } + + @SuppressWarnings("unchecked") + public Page> findRevisions(ID id, Pageable pageable) { + + AuditQuery baseQuery = createBaseQuery(id); + + List orderMapped = (pageable.getSort() instanceof RevisionSort) ? + Collections.singletonList(mapRevisionSort((RevisionSort) pageable.getSort())) : + mapPropertySort(pageable.getSort()); + + orderMapped.forEach(baseQuery::addOrder); - List resultList = createBaseQuery(id) // - .addOrder(sorting) // + List resultList = baseQuery // .setFirstResult((int) pageable.getOffset()) // .setMaxResults(pageable.getPageSize()) // .getResultList(); @@ -185,7 +219,7 @@ static class QueryResult { Assert.notNull(data, "Data must not be null"); Assert.isTrue( // data.length == 3, // - () -> String.format("Data must have length three, but has length %d", data.length)); + () -> String.format("Data must have length three, but has length %d.", data.length)); Assert.isTrue( // data[2] instanceof RevisionType, // () -> String.format("The third array element must be of type Revision type, but is of type %s", @@ -201,7 +235,7 @@ RevisionMetadata createRevisionMetadata() { return metadata instanceof DefaultRevisionEntity // ? new DefaultRevisionMetadata((DefaultRevisionEntity) metadata, revisionType) // : new AnnotationRevisionMetadata<>(Hibernate.unproxy(metadata), RevisionNumber.class, RevisionTimestamp.class, - revisionType); + revisionType); } private static RevisionMetadata.RevisionType convertRevisionType(RevisionType datum) { diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java index 1ee0d26b1f..d00f723615 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/repository/support/RepositoryIntegrationTests.java @@ -15,23 +15,13 @@ */ package org.springframework.data.envers.repository.support; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.data.history.RevisionMetadata.RevisionType.DELETE; -import static org.springframework.data.history.RevisionMetadata.RevisionType.INSERT; -import static org.springframework.data.history.RevisionMetadata.RevisionType.UPDATE; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Optional; - -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.data.envers.Config; import org.springframework.data.envers.sample.Country; import org.springframework.data.envers.sample.CountryRepository; @@ -43,19 +33,30 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.time.Instant; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.history.RevisionMetadata.RevisionType.*; + /** * Integration tests for repositories. * * @author Oliver Gierke * @author Jens Schauder - * @author Krzysztof Krason + * @author Niklas Loechte */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = Config.class) class RepositoryIntegrationTests { - @Autowired LicenseRepository licenseRepository; - @Autowired CountryRepository countryRepository; + @Autowired + LicenseRepository licenseRepository; + @Autowired + CountryRepository countryRepository; @BeforeEach void setUp() { @@ -64,13 +65,6 @@ void setUp() { countryRepository.deleteAll(); } - @AfterEach - void tearDown() { - - licenseRepository.deleteAll(); - countryRepository.deleteAll(); - } - @Test void testLifeCycle() { @@ -241,7 +235,27 @@ void shortCircuitingWhenOffsetIsToLarge() { @Test // #47 void paginationWithEmptyResult() { - check(23L, 0, 0, 0); + check(-23L, 0, 0, 0); + } + + + @Test // Envers #379 + void testSort_pageableByProperty() { + + Country de = new Country(); + de.code = "de"; + de.name = "Deutschland"; + de.timestamp = Instant.parse("2000-01-01T00:00:00Z"); + countryRepository.save(de); + + de.timestamp = Instant.parse("2000-01-04T00:01:00Z"); + countryRepository.save(de); + + de.timestamp = Instant.parse("2000-01-04T00:00:00Z"); + countryRepository.save(de); + + assertThat(countryRepository.findRevisions(de.id, PageRequest.of(0, 3, Sort.by("timestamp"))).map(Revision::getEntity).map(country -> country.timestamp).getContent()) + .isSortedAccordingTo(Instant::compareTo); } void check(Long id, int page, int expectedSize, int expectedTotalSize) { diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java index a2818be011..f974349488 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java @@ -20,11 +20,14 @@ import lombok.ToString; import org.hibernate.envers.Audited; +import java.time.Instant; + /** * Sample domain class. * * @author Oliver Gierke * @author Jens Schauder + * @author Niklas Loechte */ @Audited @Entity @@ -32,5 +35,8 @@ public class Country extends AbstractEntity { public String code; + + public Instant timestamp; + public String name; } From f778a43f28f7ca0b49c79fcc6a3f76b4fb17c059 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 24 Apr 2023 13:18:02 -0500 Subject: [PATCH 372/821] Drop hibernate-next profile. Hibernate is not publishing snapshots for 6.1 or 6.2, so there is no need to keep this profile in our build system. Resolves #2923. --- pom.xml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pom.xml b/pom.xml index 8f57f74bac..48280a3f71 100644 --- a/pom.xml +++ b/pom.xml @@ -91,18 +91,6 @@ - - hibernate-next - - 6.0.0-SNAPSHOT - - - - jboss - https://repository.jboss.org/nexus/content/repositories/public - - - eclipselink-next From 4d1c4adf6e5931182a367cf93f256d824cafc86b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 25 Apr 2023 22:53:54 -0500 Subject: [PATCH 373/821] Introduce conditional testing for Hibernate. Certain test cases should be disabled based on the version of Hibernate. We should also run the entire test suite against both Hibernate 6.1 and 6.2, to ensure maxiumum support. Resolves #2927. --- Jenkinsfile | 17 +++++ pom.xml | 6 ++ ...stgresStoredProcedureIntegrationTests.java | 6 +- ...odelEntityInformationIntegrationTests.java | 26 +++++++- .../data/jpa/util/DisabledOnHibernate61.java | 36 +++++++++++ .../data/jpa/util/DisabledOnHibernate62.java | 36 +++++++++++ .../data/jpa/util/HibernateSupport.java | 62 +++++++++++++++++++ 7 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/util/DisabledOnHibernate61.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/util/DisabledOnHibernate62.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HibernateSupport.java diff --git a/Jenkinsfile b/Jenkinsfile index 199996f064..8c14e1abff 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -53,6 +53,23 @@ pipeline { } parallel { + stage("test: baseline (hibernate 6.1)") { + agent { + label 'data' + } + options { timeout(time: 30, unit: 'MINUTES')} + environment { + ARTIFACTORY = credentials("${p['artifactory.credentials']}") + } + steps { + script { + docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { + sh 'PROFILE=all-dbs,hibernate-61 ci/test.sh' + sh "ci/clean.sh" + } + } + } + } stage("test: baseline (next)") { agent { label 'data' diff --git a/pom.xml b/pom.xml index 48280a3f71..849cb35379 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,12 @@ + + hibernate-61 + + 6.1.7.Final + + all-dbs diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index b6997ef259..10c07c1a78 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -16,7 +16,7 @@ package org.springframework.data.jpa.repository.procedures; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import jakarta.persistence.Entity; import jakarta.persistence.EntityManagerFactory; @@ -36,7 +36,6 @@ import javax.sql.DataSource; import org.hibernate.dialect.PostgreSQLDialect; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.postgresql.ds.PGSimpleDataSource; @@ -48,6 +47,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.jpa.util.DisabledOnHibernate62; import org.springframework.jdbc.datasource.init.DataSourceInitializer; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; @@ -68,7 +68,6 @@ * @author Greg Turnquist * @author Yanming Zhou */ -@Disabled @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = PostgresStoredProcedureIntegrationTests.Config.class) @@ -116,6 +115,7 @@ void testNamedOutputParameter() { new Employee(4, "Gabriel")); } + @DisabledOnHibernate62 @Test // 2256 void testSingleEntityFromResultSet() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java index 2628d87bf3..0716d5e64d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/HibernateJpaMetamodelEntityInformationIntegrationTests.java @@ -15,7 +15,9 @@ */ package org.springframework.data.jpa.repository.support; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.data.jpa.util.DisabledOnHibernate61; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -26,11 +28,31 @@ */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") -class HibernateJpaMetamodelEntityInformationIntegrationTests - extends JpaMetamodelEntityInformationIntegrationTests { +class HibernateJpaMetamodelEntityInformationIntegrationTests extends JpaMetamodelEntityInformationIntegrationTests { @Override String getMetadadataPersistenceUnitName() { return "metadata-id-handling"; } + + @DisabledOnHibernate61 + @Test + @Override + void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() { + super.correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType(); + } + + @DisabledOnHibernate61 + @Test + @Override + void prefersPrivateGetterOverFieldAccess() { + super.prefersPrivateGetterOverFieldAccess(); + } + + @DisabledOnHibernate61 + @Test + @Override + void findsIdClassOnMappedSuperclass() { + super.findsIdClassOnMappedSuperclass(); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/DisabledOnHibernate61.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/DisabledOnHibernate61.java new file mode 100644 index 0000000000..221083a120 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/DisabledOnHibernate61.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Annotation to flag JUnit 5 test cases to ONLY activate when Hibernate 6.2 is on the classpath. + * + * @author Greg Turnquist + * @since 3.1 + */ +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(HibernateSupport.DisabledWhenHibernate61OnClasspath.class) +public @interface DisabledOnHibernate61 { + +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/DisabledOnHibernate62.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/DisabledOnHibernate62.java new file mode 100644 index 0000000000..5ab5042a3d --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/DisabledOnHibernate62.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Annotation to flag JUnit 5 test cases to ONLY activate when Hibernate 6.1 is on the classpath. + * + * @author Greg Turnquist + * @since 3.1 + */ +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(HibernateSupport.DisabledWhenHibernate62OnClasspath.class) +public @interface DisabledOnHibernate62 { + +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HibernateSupport.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HibernateSupport.java new file mode 100644 index 0000000000..3afeaa16b9 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/util/HibernateSupport.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.util; + +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.springframework.util.ClassUtils; + +/** + * JUnit 5 utilities to support conditional test cases based upon Hibernate classpath settings. + * + * @author Greg Turnquist + * @since 3.1 + */ +abstract class HibernateSupport { + + /** + * {@literal org.hibernate.dialect.PostgreSQL91Dialect} is deprecated in Hibernate 6.1 and fully removed in Hibernate + * 6.2, making it a perfect detector between the two. + */ + private static final boolean HIBERNATE_61_ON_CLASSPATH = ClassUtils + .isPresent("org.hibernate.dialect.PostgreSQL91Dialect", HibernateSupport.class.getClassLoader()); + + private static final boolean HIBERNATE_62_ON_CLASSPATH = !HIBERNATE_61_ON_CLASSPATH; + + static class DisabledWhenHibernate61OnClasspath implements ExecutionCondition { + + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext extensionContext) { + + return HibernateSupport.HIBERNATE_61_ON_CLASSPATH + ? ConditionEvaluationResult.disabled("Disabled because Hibernate 6.1 is on the classpath") + : ConditionEvaluationResult.enabled("NOT disabled because Hibernate 6.2 is on the classpath"); + } + } + + static class DisabledWhenHibernate62OnClasspath implements ExecutionCondition { + + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext extensionContext) { + + return HibernateSupport.HIBERNATE_62_ON_CLASSPATH + ? ConditionEvaluationResult.disabled("Disabled because Hibernate 6.2 is on the classpath") + : ConditionEvaluationResult.enabled("NOT disabled because Hibernate 6.1 is on the classpath"); + } + + } +} From 87bd57821be612db06085b81638a36147c025662 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 26 Apr 2023 17:14:55 +0200 Subject: [PATCH 374/821] Adapt to ScrollPosition API changes in Spring Data Commons. Fixes #2931. Related ticket #2824. --- .../query/KeysetScrollDelegate.java | 9 +++--- .../jpa/repository/query/ScrollDelegate.java | 6 ++-- .../RepositoryWithIdClassKeyTests.java | 4 +-- .../jpa/repository/UserRepositoryTests.java | 28 +++++++++---------- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java index f6061a9c7e..d40951c3c9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollDelegate.java @@ -21,7 +21,7 @@ import java.util.Map; import org.springframework.data.domain.KeysetScrollPosition; -import org.springframework.data.domain.KeysetScrollPosition.Direction; +import org.springframework.data.domain.ScrollPosition.Direction; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; import org.springframework.lang.Nullable; @@ -34,8 +34,8 @@ */ public class KeysetScrollDelegate { - private static final KeysetScrollDelegate forward = new KeysetScrollDelegate(); - private static final KeysetScrollDelegate reverse = new ReverseKeysetScrollDelegate(); + private static final KeysetScrollDelegate FORWARD = new KeysetScrollDelegate(); + private static final KeysetScrollDelegate REVERSE = new ReverseKeysetScrollDelegate(); /** * Factory method to obtain the right {@link KeysetScrollDelegate}. @@ -44,7 +44,7 @@ public class KeysetScrollDelegate { * @return a {@link KeysetScrollDelegate} matching the requested direction. */ public static KeysetScrollDelegate of(Direction direction) { - return direction == Direction.Forward ? forward : reverse; + return direction == Direction.FORWARD ? FORWARD : REVERSE; } @Nullable @@ -119,6 +119,7 @@ protected List getResultWindow(List list, int limit) { */ private static class ReverseKeysetScrollDelegate extends KeysetScrollDelegate { + @Override protected Sort getSortOrders(Sort sort) { List orders = new ArrayList<>(); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java index 94cc960a9b..10681ac698 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java @@ -22,9 +22,9 @@ import java.util.function.IntFunction; import org.springframework.data.domain.KeysetScrollPosition; -import org.springframework.data.domain.KeysetScrollPosition.Direction; import org.springframework.data.domain.OffsetScrollPosition; import org.springframework.data.domain.ScrollPosition; +import org.springframework.data.domain.ScrollPosition.Direction; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; import org.springframework.data.domain.Window; @@ -82,12 +82,12 @@ private static Window createWindow(Sort sort, int limit, Direction direct KeysetScrollDelegate delegate = KeysetScrollDelegate.of(direction); List resultsToUse = delegate.postProcessResults(result); - IntFunction positionFunction = value -> { + IntFunction positionFunction = value -> { T object = result.get(value); Map keys = entity.getKeyset(sort.stream().map(Order::getProperty).toList(), object); - return KeysetScrollPosition.of(keys); + return ScrollPosition.forward(keys); }; return Window.from(delegate.getResultWindow(resultsToUse, limit), positionFunction, hasMoreElements(result, limit)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java index c530cb83ff..e19eb33b25 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/RepositoryWithIdClassKeyTests.java @@ -25,7 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; -import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Window; import org.springframework.data.jpa.domain.sample.Item; @@ -90,7 +90,7 @@ void shouldScrollWithKeyset() { Window first = itemRepository.findBy((root, query, criteriaBuilder) -> { return criteriaBuilder.isNotNull(root.get("name")); - }, q -> q.limit(1).sortBy(Sort.by("name")).scroll(KeysetScrollPosition.initial())); + }, q -> q.limit(1).sortBy(Sort.by("name")).scroll(ScrollPosition.keyset())); assertThat(first).containsOnly(item1); 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 026598b868..844e2ec9fa 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 @@ -56,6 +56,8 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.*; +import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; +import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; @@ -1243,7 +1245,7 @@ void scrollByExampleOffset() { matching().withMatcher("firstname", GenericPropertyMatcher::startsWith).withIgnorePaths("age", "createdAt", "dateOfBirth")); Window firstWindow = repository.findBy(example, - q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(OffsetScrollPosition.initial())); + q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(ScrollPosition.offset())); assertThat(firstWindow).containsExactly(jane1, jane2); assertThat(firstWindow.hasNext()).isTrue(); @@ -1269,7 +1271,7 @@ void scrollByExampleKeyset() { matching().withMatcher("firstname", GenericPropertyMatcher::startsWith).withIgnorePaths("age", "createdAt", "dateOfBirth")); Window firstWindow = repository.findBy(example, - q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")).scroll(KeysetScrollPosition.initial())); + q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")).scroll(ScrollPosition.keyset())); assertThat(firstWindow).containsOnly(jane1); assertThat(firstWindow.hasNext()).isTrue(); @@ -1295,12 +1297,12 @@ void scrollByExampleKeysetBackward() { matching().withMatcher("firstname", GenericPropertyMatcher::startsWith).withIgnorePaths("age", "createdAt", "dateOfBirth")); Window firstWindow = repository.findBy(example, - q -> q.limit(4).sortBy(Sort.by("firstname", "emailAddress")).scroll(KeysetScrollPosition.initial())); + q -> q.limit(4).sortBy(Sort.by("firstname", "emailAddress")).scroll(ScrollPosition.keyset())); KeysetScrollPosition scrollPosition = (KeysetScrollPosition) firstWindow.positionAt(2); Window previousWindow = repository.findBy(example, q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")) - .scroll(KeysetScrollPosition.of(scrollPosition.getKeys(), KeysetScrollPosition.Direction.Backward))); + .scroll(ScrollPosition.backward(scrollPosition.getKeys()))); assertThat(previousWindow).containsOnly(jane2); assertThat(previousWindow.hasNext()).isTrue(); @@ -1317,7 +1319,7 @@ void scrollByPredicateOffset() { repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); Window firstWindow = repository.findBy(QUser.user.firstname.startsWith("J"), - q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(OffsetScrollPosition.initial())); + q -> q.limit(2).sortBy(Sort.by("firstname")).scroll(ScrollPosition.offset())); assertThat(firstWindow).containsExactly(jane1, jane2); assertThat(firstWindow.hasNext()).isTrue(); @@ -1340,7 +1342,7 @@ void scrollByPredicateKeyset() { repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); Window firstWindow = repository.findBy(QUser.user.firstname.startsWith("J"), - q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")).scroll(KeysetScrollPosition.initial())); + q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")).scroll(ScrollPosition.keyset())); assertThat(firstWindow).containsOnly(jane1); assertThat(firstWindow.hasNext()).isTrue(); @@ -1363,16 +1365,14 @@ void scrollByPredicateKeysetBackward() { repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); Window firstWindow = repository.findBy(QUser.user.firstname.startsWith("J"), - q -> q.limit(3).sortBy(Sort.by("firstname", "emailAddress")).scroll(KeysetScrollPosition.initial())); + q -> q.limit(3).sortBy(Sort.by("firstname", "emailAddress")).scroll(ScrollPosition.keyset())); assertThat(firstWindow).containsExactly(jane1, jane2, john1); assertThat(firstWindow.hasNext()).isTrue(); KeysetScrollPosition scrollPosition = (KeysetScrollPosition) firstWindow.positionAt(2); - KeysetScrollPosition backward = KeysetScrollPosition.of(scrollPosition.getKeys(), - KeysetScrollPosition.Direction.Backward); Window previousWindow = repository.findBy(QUser.user.firstname.startsWith("J"), - q -> q.limit(3).sortBy(Sort.by("firstname", "emailAddress")).scroll(backward)); + q -> q.limit(3).sortBy(Sort.by("firstname", "emailAddress")).scroll(scrollPosition.backward())); assertThat(previousWindow).containsExactly(jane1, jane2); @@ -1391,16 +1391,14 @@ void scrollByPartTreeKeysetBackward() { repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); Window firstWindow = repository.findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc("J", - KeysetScrollPosition.initial()); + ScrollPosition.keyset()); assertThat(firstWindow).containsExactly(jane1, jane2, john1); assertThat(firstWindow.hasNext()).isTrue(); KeysetScrollPosition scrollPosition = (KeysetScrollPosition) firstWindow.positionAt(2); - KeysetScrollPosition backward = KeysetScrollPosition.of(scrollPosition.getKeys(), - KeysetScrollPosition.Direction.Backward); Window previousWindow = repository.findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc("J", - backward); + scrollPosition.backward()); assertThat(previousWindow).containsExactly(jane1, jane2); @@ -1914,7 +1912,7 @@ void findByCollectionWithPageRequest() { flushTestUsers(); - Page userPage = repository.findByAgeIn(List.of(28, 35), (PageRequest) PageRequest.of(0, 2)); + Page userPage = repository.findByAgeIn(List.of(28, 35), PageRequest.of(0, 2)); assertThat(userPage).hasSize(2); assertThat(userPage.getTotalElements()).isEqualTo(2); From b3cb1f7a5315de040d5d9f8d20e8de2d89d12900 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 4 May 2023 08:56:28 -0500 Subject: [PATCH 375/821] Create a Testcontainers image name resolver to support CI proxy usage. Ported the solution to leveraging Docker proxy services to Spring Data JPA 3.1. Related: #2941. Original pull request: #2937. --- Jenkinsfile | 4 ++ .../support/ProxyImageNameSubstitutor.java | 71 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ProxyImageNameSubstitutor.java diff --git a/Jenkinsfile b/Jenkinsfile index 8c14e1abff..415311f002 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -32,6 +32,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { script { @@ -60,6 +61,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { script { @@ -77,6 +79,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { script { @@ -94,6 +97,7 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { script { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ProxyImageNameSubstitutor.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ProxyImageNameSubstitutor.java new file mode 100644 index 0000000000..86fea48175 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/support/ProxyImageNameSubstitutor.java @@ -0,0 +1,71 @@ +/* + * Copyright 2015-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.support; + +import java.util.List; + +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.ImageNameSubstitutor; + +/** + * An {@link ImageNameSubstitutor} only used on CI servers to leverage internal proxy solution, that needs to vary the + * prefix based on which container image is needed. + * + * @author Greg Turnquist + */ +public class ProxyImageNameSubstitutor extends ImageNameSubstitutor { + + private static final List NAMES_TO_PROXY_PREFIX = List.of("ryuk"); + + private static final List NAMES_TO_LIBRARY_PROXY_PREFIX = List.of("mysql", "postgres"); + + private static final String PROXY_PREFIX = "harbor-repo.vmware.com/dockerhub-proxy-cache/"; + + private static final String LIBRARY_PROXY_PREFIX = PROXY_PREFIX + "library/"; + + @Override + public DockerImageName apply(DockerImageName dockerImageName) { + + if (NAMES_TO_PROXY_PREFIX.stream().anyMatch(s -> dockerImageName.asCanonicalNameString().contains(s))) { + return DockerImageName.parse(applyProxyPrefix(dockerImageName.asCanonicalNameString())); + } + + if (NAMES_TO_LIBRARY_PROXY_PREFIX.stream().anyMatch(s -> dockerImageName.asCanonicalNameString().contains(s))) { + return DockerImageName.parse(applyProxyAndLibraryPrefix(dockerImageName.asCanonicalNameString())); + } + + return dockerImageName; + } + + @Override + protected String getDescription() { + return "Spring Data Proxy Image Name Substitutor"; + } + + /** + * Apply a non-library-based prefix. + */ + private static String applyProxyPrefix(String imageName) { + return PROXY_PREFIX + imageName; + } + + /** + * Apply a library based prefix. + */ + private static String applyProxyAndLibraryPrefix(String imageName) { + return LIBRARY_PROXY_PREFIX + imageName; + } +} From d2fa85ad29c3914e3ee3ecf237c02a3077bc2c62 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 10 May 2023 10:44:29 +0200 Subject: [PATCH 376/821] =?UTF-8?q?Document=20that=20`JpaSpecificationExec?= =?UTF-8?q?utor.delete(=E2=80=A6)`=20uses=20`CriteriaDelete`=20and=20hence?= =?UTF-8?q?=20`Cascades`=20are=20not=20considered.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2936 --- .../jpa/repository/JpaSpecificationExecutor.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index 8835cf1d6e..0ca25cfbe2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -15,6 +15,10 @@ */ package org.springframework.data.jpa.repository; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; + import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -89,6 +93,14 @@ public interface JpaSpecificationExecutor { /** * Deletes by the {@link Specification} and returns the number of rows deleted. + *

    + * This method uses {@link jakarta.persistence.criteria.CriteriaDelete Criteria API bulk delete} that maps directly to + * database delete operations. The persistence context is not synchronized with the result of the bulk delete. + *

    + * Please note that {@link jakarta.persistence.criteria.CriteriaQuery} in, + * {@link Specification#toPredicate(Root, CriteriaQuery, CriteriaBuilder)} will be {@literal null} because + * {@link jakarta.persistence.criteria.CriteriaBuilder#createCriteriaDelete(Class)} does not implement + * {@code CriteriaQuery}. * * @param spec the {@link Specification} to use for the existence check, must not be {@literal null}. * @return the number of entities deleted. From 878db1d51ed1074dfc25625d42c21bf773579496 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 9 May 2023 15:48:07 -0500 Subject: [PATCH 377/821] Rewrite LIKE clauses with wildcards as CONCAT functions. We support wrapping parameters (named or positional) with optional wildcards when doing LIKE patterns. This is out-of-band and requires moving the wildcards into the bindings. To stop doing this and causing race conditions, we can instead rewrite the queries using the CONCAT function. This function is standard across relational database (native queries) as well as JPA providers (Hibernate and EclipseLink). See #2939 See #2760 Original Pull Request: #2940 Superceding Pull Request: #2944 --- .../jpa/repository/query/StringQuery.java | 58 +++++++++++++------ .../query/LikeBindingUnitTests.java | 10 ---- .../QueryWithNullLikeIntegrationTests.java | 39 ++++++++++++- .../query/StringQueryUnitTests.java | 9 +-- 4 files changed, 82 insertions(+), 34 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index d52447ae36..e56185ab1e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -334,7 +334,43 @@ private static String replaceFirst(String text, String substring, String replace return text; } - return text.substring(0, index) + replacement + text.substring(index + substring.length()); + return text.substring(0, index) + potentiallyWrapWithWildcards(replacement, substring) + + text.substring(index + substring.length()); + } + + /** + * If there are any pre- or post-wildcards ({@literal %}), replace them with a {@literal CONCAT} function and proper + * wildcards as string literals. NOTE: {@literal CONCAT} appears to be a standard function across relational + * databases as well as JPA providers. + * + * @param replacement + * @param substring + * @return the replacement string properly wrapped in a {@literal CONCAT} function with wildcards applied. + * @since 3.1 + */ + private static String potentiallyWrapWithWildcards(String replacement, String substring) { + + boolean wildcards = substring.startsWith("%") || substring.endsWith("%"); + + if (!wildcards) { + return replacement; + } + + StringBuilder concatWrapper = new StringBuilder("CONCAT("); + + if (substring.startsWith("%")) { + concatWrapper.append("'%',"); + } + + concatWrapper.append(replacement); + + if (substring.endsWith("%")) { + concatWrapper.append(",'%'"); + } + + concatWrapper.append(")"); + + return concatWrapper.toString(); } @Nullable @@ -708,28 +744,12 @@ public Type getType() { } /** - * Prepares the given raw keyword according to the like type. + * Extracts the raw value properly. */ @Nullable @Override public Object prepare(@Nullable Object value) { - - Object unwrapped = PersistenceProvider.unwrapTypedParameterValue(value); - if (unwrapped == null) { - return null; - } - - switch (type) { - case STARTING_WITH: - return String.format("%s%%", unwrapped); - case ENDING_WITH: - return String.format("%%%s", unwrapped); - case CONTAINING: - return String.format("%%%s%%", unwrapped); - case LIKE: - default: - return unwrapped; - } + return PersistenceProvider.unwrapTypedParameterValue(value); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java index 555aa23d96..bcd0556bff 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java @@ -84,14 +84,4 @@ void setsUpInstanceForIndex() { assertThat(binding.hasPosition(1)).isTrue(); assertThat(binding.getType()).isEqualTo(Type.CONTAINING); } - - @Test - void augmentsValueCorrectly() { - - assertAugmentedValue(Type.CONTAINING, "%value%"); - assertAugmentedValue(Type.ENDING_WITH, "%value"); - assertAugmentedValue(Type.STARTING_WITH, "value%"); - - assertThat(new LikeParameterBinding(1, Type.CONTAINING).prepare(null)).isNull(); - } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java index 9afd65db6b..556f88836d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import jakarta.persistence.EntityManagerFactory; @@ -102,6 +102,40 @@ void customQueryWithNullMatch() { assertThat(Employees).extracting(EmployeeWithName::getName).isEmpty(); } + @Test // GH-2939 + void customQueryWithMultipleMatchInNative() { + + List Employees = repository.customQueryWithNullableParamInNative("Baggins"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test // GH-2939 + void customQueryWithSingleMatchInNative() { + + List Employees = repository.customQueryWithNullableParamInNative("Frodo"); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins"); + } + + @Test + void customQueryWithEmptyStringMatchInNative() { + + List Employees = repository.customQueryWithNullableParamInNative(""); + + assertThat(Employees).extracting(EmployeeWithName::getName).containsExactlyInAnyOrder("Frodo Baggins", + "Bilbo Baggins"); + } + + @Test // GH-2939 + void customQueryWithNullMatchInNative() { + + List Employees = repository.customQueryWithNullableParamInNative(null); + + assertThat(Employees).extracting(EmployeeWithName::getName).isEmpty(); + } + @Test void derivedQueryStartsWithSingleMatch() { @@ -235,6 +269,9 @@ public interface EmployeeWithNullLikeRepository extends JpaRepository customQueryWithNullableParam(@Nullable @Param("partialName") String partialName); + @Query(value = "select * from EmployeeWithName as e where e.name like %:partialName%", nativeQuery = true) + List customQueryWithNullableParamInNative(@Nullable @Param("partialName") String partialName); + List findByNameStartsWith(@Nullable String partialName); List findByNameEndsWith(@Nullable String partialName); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 78427ece9a..3c7ece5c4b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -65,7 +65,7 @@ void detectsPositionalLikeBindings() { assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()) - .isEqualTo("select u from User u where u.firstname like ?1 or u.lastname like ?2"); + .isEqualTo("select u from User u where u.firstname like CONCAT('%',?1,'%') or u.lastname like CONCAT('%',?2)"); List bindings = query.getParameterBindings(); assertThat(bindings).hasSize(2); @@ -87,7 +87,7 @@ void detectsNamedLikeBindings() { StringQuery query = new StringQuery("select u from User u where u.firstname like %:firstname", true); assertThat(query.hasParameterBindings()).isTrue(); - assertThat(query.getQueryString()).isEqualTo("select u from User u where u.firstname like :firstname"); + assertThat(query.getQueryString()).isEqualTo("select u from User u where u.firstname like CONCAT('%',:firstname)"); List bindings = query.getParameterBindings(); assertThat(bindings).hasSize(1); @@ -199,8 +199,9 @@ void removesLikeBindingsFromQueryIfQueryContainsSimpleBinding() { assertNamedBinding(LikeParameterBinding.class, "escapedWord", bindings.get(0)); assertNamedBinding(ParameterBinding.class, "word", bindings.get(1)); - assertThat(query.getQueryString()).isEqualTo("SELECT a FROM Article a WHERE a.overview LIKE :escapedWord ESCAPE '~'" - + " OR a.content LIKE :escapedWord ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC"); + assertThat(query.getQueryString()) + .isEqualTo("SELECT a FROM Article a WHERE a.overview LIKE CONCAT('%',:escapedWord,'%') ESCAPE '~'" + + " OR a.content LIKE CONCAT('%',:escapedWord,'%') ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC"); } @Test // DATAJPA-483 From 4d9667f2c0f60d0526bb3055e51adfd776570be4 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 11 May 2023 16:16:13 +0200 Subject: [PATCH 378/821] Adopt to changed `Sort.Order` constructor. Closes #2946 --- .../data/jpa/domain/JpaSort.java | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index e8f8b57b86..eead562429 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -15,15 +15,15 @@ */ package org.springframework.data.jpa.domain; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.PluralAttribute; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import jakarta.persistence.metamodel.Attribute; -import jakarta.persistence.metamodel.PluralAttribute; - import org.springframework.data.domain.Sort; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -364,7 +364,6 @@ public static class JpaOrder extends Order { private static final long serialVersionUID = 1L; private final boolean unsafe; - private final boolean ignoreCase; /** * Creates a new {@link JpaOrder} instance. if order is {@literal null} then order defaults to @@ -386,25 +385,24 @@ private JpaOrder(@Nullable Direction direction, String property) { * @param nullHandlingHint can be {@literal null}, will default to {@link NullHandling#NATIVE}. */ private JpaOrder(@Nullable Direction direction, String property, NullHandling nullHandlingHint) { - this(direction, property, nullHandlingHint, false, true); + this(direction, property, false, nullHandlingHint, true); } - private JpaOrder(@Nullable Direction direction, String property, NullHandling nullHandling, boolean ignoreCase, + private JpaOrder(@Nullable Direction direction, String property, boolean ignoreCase, NullHandling nullHandling, boolean unsafe) { - super(direction, property, nullHandling); - this.ignoreCase = ignoreCase; + super(direction, property, ignoreCase, nullHandling); this.unsafe = unsafe; } @Override public JpaOrder with(Direction order) { - return new JpaOrder(order, getProperty(), getNullHandling(), isIgnoreCase(), this.unsafe); + return new JpaOrder(order, getProperty(), isIgnoreCase(), getNullHandling(), this.unsafe); } @Override public JpaOrder with(NullHandling nullHandling) { - return new JpaOrder(getDirection(), getProperty(), nullHandling, isIgnoreCase(), this.unsafe); + return new JpaOrder(getDirection(), getProperty(), isIgnoreCase(), nullHandling, this.unsafe); } /** @@ -421,7 +419,7 @@ public Sort withUnsafe(String... properties) { List orders = new ArrayList<>(properties.length); for (String property : properties) { - orders.add(new JpaOrder(getDirection(), property, getNullHandling(), isIgnoreCase(), this.unsafe)); + orders.add(new JpaOrder(getDirection(), property, isIgnoreCase(), getNullHandling(), this.unsafe)); } return Sort.by(orders); @@ -429,12 +427,7 @@ public Sort withUnsafe(String... properties) { @Override public JpaOrder ignoreCase() { - return new JpaOrder(getDirection(), getProperty(), getNullHandling(), true, this.unsafe); - } - - @Override - public boolean isIgnoreCase() { - return super.isIgnoreCase() || ignoreCase; + return new JpaOrder(getDirection(), getProperty(), true, getNullHandling(), this.unsafe); } /** From 3145bc62e97b715f28b39c7dff8b80c980f3e1f3 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 12 May 2023 14:14:05 +0200 Subject: [PATCH 379/821] Prepare 3.1 GA (2023.0.0). See #2919 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 849cb35379..4cb53d08fa 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0-SNAPSHOT + 3.1.0 @@ -37,7 +37,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0-SNAPSHOT + 3.1.0 0.10.3 org.hibernate @@ -218,8 +218,8 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-libs-release + https://repo.spring.io/libs-release true From 0ef008ad4f1a8f404aa984ab8a78e4a56f42e3fa Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 12 May 2023 14:14:38 +0200 Subject: [PATCH 380/821] Release version 3.1 GA (2023.0.0). See #2919 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 4cb53d08fa..c51a626718 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index db915d7c3b..5ac1b1aea5 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0-SNAPSHOT + 3.1.0 org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a5cb2f09b5..dbc53ea5d5 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index ea9125d029..8c893392c2 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0-SNAPSHOT + 3.1.0 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml From fa29178fc38d05e67427e3b42363b0b7d81736aa Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 12 May 2023 14:18:51 +0200 Subject: [PATCH 381/821] Prepare next development iteration. See #2919 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index c51a626718..fd60bd4f29 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0 + 3.2.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 5ac1b1aea5..0c3d16ef85 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.1.0 + 3.2.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.1.0 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index dbc53ea5d5..991cd8cbf0 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 8c893392c2..50e68c943b 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.1.0 + 3.2.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.1.0 + 3.2.0-SNAPSHOT ../pom.xml From a5b764bfccf33b28dff24029fda62aad40772736 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 12 May 2023 14:18:53 +0200 Subject: [PATCH 382/821] After release cleanups. See #2919 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index fd60bd4f29..a47a8bfbf2 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.1.0 + 3.2.0-SNAPSHOT @@ -37,7 +37,7 @@ 4.5 8.0.31 42.5.0 - 3.1.0 + 3.2.0-SNAPSHOT 0.10.3 org.hibernate @@ -218,8 +218,8 @@ - spring-libs-release - https://repo.spring.io/libs-release + spring-libs-snapshot + https://repo.spring.io/libs-snapshot true From 850aaac3710e2d9c12744a538964c27e0c18ba78 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Thu, 27 Apr 2023 11:23:45 +0200 Subject: [PATCH 383/821] Remove deprecated constructors of JpaSort. Original pull request #2929 --- .../data/jpa/domain/JpaSort.java | 87 +++++-------------- ...MailMessageRepositoryIntegrationTests.java | 2 +- 2 files changed, 25 insertions(+), 64 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index eead562429..629e743cfe 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -28,6 +28,12 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + /** * Sort option for queries that wraps JPA meta-model {@link Attribute}s for sorting. * @@ -35,59 +41,14 @@ * @author Oliver Gierke * @author Christoph Strobl * @author David Madden + * @author Jens Schauder */ public class JpaSort extends Sort { private static final long serialVersionUID = 1L; - /** - * Creates a new {@link JpaSort} for the given attributes with the default sort direction. - * - * @param attributes must not be {@literal null} or empty. - * @deprecated since 2.3, use {@link JpaSort#of(Attribute...)} instead. - */ - @Deprecated - public JpaSort(Attribute... attributes) { - this(DEFAULT_DIRECTION, attributes); - } - - /** - * Creates a new {@link JpaSort} instance with the given {@link Path}s. - * - * @param paths must not be {@literal null} or empty. - * @deprecated since 2.3, use {@link JpaSort#of(Path...))} instead. - */ - @Deprecated - public JpaSort(Path... paths) { - this(DEFAULT_DIRECTION, paths); - } - - /** - * Creates a new {@link JpaSort} for the given direction and attributes. - * - * @param direction the sorting direction. - * @param attributes must not be {@literal null} or empty. - * @deprecated since 2.3, use {@link JpaSort#of(Direction, Attribute...)} instead. - */ - @Deprecated - public JpaSort(Direction direction, Attribute... attributes) { - this(direction, paths(attributes)); - } - - /** - * Creates a new {@link JpaSort} for the given direction and {@link Path}s. - * - * @param direction the sorting direction. - * @param paths must not be {@literal null} or empty. - * @deprecated since 2.3, use {@link JpaSort#of(Direction, Path...)} instead. - */ - @Deprecated - public JpaSort(Direction direction, Path... paths) { - this(direction, Arrays.asList(paths)); - } - private JpaSort(Direction direction, List> paths) { - this(Collections. emptyList(), direction, paths); + this(Collections.emptyList(), direction, paths); } private JpaSort(List orders, @Nullable Direction direction, List> paths) { @@ -104,7 +65,7 @@ private JpaSort(List orders) { * @param attributes must not be {@literal null} or empty. */ public static JpaSort of(Attribute... attributes) { - return new JpaSort(attributes); + return new JpaSort(DEFAULT_DIRECTION, Arrays.asList(paths(attributes))); } /** @@ -113,33 +74,33 @@ public static JpaSort of(Attribute... attributes) { * @param paths must not be {@literal null} or empty. */ public static JpaSort of(JpaSort.Path... paths) { - return new JpaSort(paths); + return new JpaSort(DEFAULT_DIRECTION, Arrays.asList(paths)); } /** * Creates a new {@link JpaSort} for the given direction and attributes. * - * @param direction the sorting direction. + * @param direction the sorting direction. * @param attributes must not be {@literal null} or empty. */ public static JpaSort of(Direction direction, Attribute... attributes) { - return new JpaSort(direction, attributes); + return new JpaSort(direction, Arrays.asList(paths(attributes))); } /** * Creates a new {@link JpaSort} for the given direction and {@link Path}s. * * @param direction the sorting direction. - * @param paths must not be {@literal null} or empty. + * @param paths must not be {@literal null} or empty. */ public static JpaSort of(Direction direction, Path... paths) { - return new JpaSort(direction, paths); + return new JpaSort(direction, Arrays.asList(paths)); } /** * Returns a new {@link JpaSort} with the given sorting criteria added to the current one. * - * @param direction can be {@literal null}. + * @param direction can be {@literal null}. * @param attributes must not be {@literal null}. * @return */ @@ -154,7 +115,7 @@ public JpaSort and(@Nullable Direction direction, Attribute... attributes) * Returns a new {@link JpaSort} with the given sorting criteria added to the current one. * * @param direction can be {@literal null}. - * @param paths must not be {@literal null}. + * @param paths must not be {@literal null}. * @return */ public JpaSort and(@Nullable Direction direction, Path... paths) { @@ -173,7 +134,7 @@ public JpaSort and(@Nullable Direction direction, Path... paths) { /** * Returns a new {@link JpaSort} with the given sorting criteria added to the current one. * - * @param direction can be {@literal null}. + * @param direction can be {@literal null}. * @param properties must not be {@literal null} or empty. * @return */ @@ -191,7 +152,7 @@ public JpaSort andUnsafe(@Nullable Direction direction, String... properties) { orders.add(new JpaOrder(direction, property)); } - return new JpaSort(orders, direction, Collections.> emptyList()); + return new JpaSort(orders, direction, Collections.>emptyList()); } /** @@ -262,7 +223,7 @@ public static JpaSort unsafe(String... properties) { /** * Creates new unsafe {@link JpaSort} based on given {@link Direction} and properties. * - * @param direction must not be {@literal null}. + * @param direction must not be {@literal null}. * @param properties must not be {@literal null} or empty. * @return */ @@ -278,7 +239,7 @@ public static JpaSort unsafe(Direction direction, String... properties) { /** * Creates new unsafe {@link JpaSort} based on given {@link Direction} and properties. * - * @param direction must not be {@literal null}. + * @param direction must not be {@literal null}. * @param properties must not be {@literal null} or empty. * @return */ @@ -370,7 +331,7 @@ public static class JpaOrder extends Order { * {@link Sort#DEFAULT_DIRECTION} * * @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}. - * @param property must not be {@literal null}. + * @param property must not be {@literal null}. */ private JpaOrder(@Nullable Direction direction, String property) { this(direction, property, NullHandling.NATIVE); @@ -380,8 +341,8 @@ private JpaOrder(@Nullable Direction direction, String property) { * Creates a new {@link Order} instance. if order is {@literal null} then order defaults to * {@link Sort#DEFAULT_DIRECTION}. * - * @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}. - * @param property must not be {@literal null}. + * @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}. + * @param property must not be {@literal null}. * @param nullHandlingHint can be {@literal null}, will default to {@link NullHandling#NATIVE}. */ private JpaOrder(@Nullable Direction direction, String property, NullHandling nullHandlingHint) { @@ -389,7 +350,7 @@ private JpaOrder(@Nullable Direction direction, String property, NullHandling nu } private JpaOrder(@Nullable Direction direction, String property, boolean ignoreCase, NullHandling nullHandling, - boolean unsafe) { + boolean unsafe) { super(direction, property, ignoreCase, nullHandling); this.unsafe = unsafe; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java index 9427a33762..20d78e1cec 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/MailMessageRepositoryIntegrationTests.java @@ -79,7 +79,7 @@ void shouldSortMailWithPageRequestAndJpaSortCriteriaNullsFirst() { mailMessageRepository.save(message2); Page results = mailMessageRepository.findAll(PageRequest.of(0, 20, // - new JpaSort(Direction.ASC, path(MailMessage_.mailSender).dot(MailSender_.name)))); + JpaSort.of(Direction.ASC, path(MailMessage_.mailSender).dot(MailSender_.name)))); List messages = results.getContent(); assertThat(messages).hasSize(2); From 1ac50e43cbe7b6fc5ba27f342559471fa6dcf895 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 22 May 2023 14:29:31 -0500 Subject: [PATCH 384/821] Properly implement handling sort items. See #2962 Original Pull Request: #2965 --- .../data/jpa/repository/query/Hql.g4 | 6 +--- .../repository/query/HqlQueryRenderer.java | 22 ++------------- .../query/HqlQueryRendererTests.java | 28 +++++++++++++++++++ 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index c429687b77..80af871fc6 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -139,10 +139,6 @@ values : '(' expression (',' expression)* ')' ; -projectedItem - : (expression | instantiation) alias? - ; - instantiation : NEW instantiationTarget '(' instantiationArguments ')' ; @@ -254,7 +250,7 @@ groupByClause ; orderByClause - : ORDER BY projectedItem (',' projectedItem)* + : ORDER BY sortedItem (',' sortedItem)* ; havingClause diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index f613352d6f..1adfa14216 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -457,24 +457,6 @@ public List visitValues(HqlParser.ValuesContext ctx) { return tokens; } - @Override - public List visitProjectedItem(HqlParser.ProjectedItemContext ctx) { - - List tokens = new ArrayList<>(); - - if (ctx.expression() != null) { - tokens.addAll(visit(ctx.expression())); - } else if (ctx.instantiation() != null) { - tokens.addAll(visit(ctx.instantiation())); - } - - if (ctx.alias() != null) { - tokens.addAll(visit(ctx.alias())); - } - - return tokens; - } - @Override public List visitInstantiation(HqlParser.InstantiationContext ctx) { @@ -858,8 +840,8 @@ public List visitOrderByClause(HqlParser.OrderByClauseCont tokens.add(new JpaQueryParsingToken(ctx.ORDER())); tokens.add(new JpaQueryParsingToken(ctx.BY())); - ctx.projectedItem().forEach(projectedItemContext -> { - tokens.addAll(visit(projectedItemContext)); + ctx.sortedItem().forEach(sortedItemContext -> { + tokens.addAll(visit(sortedItemContext)); NOSPACE(tokens); tokens.add(TOKEN_COMMA); }); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index c161fd5eab..672c65aba4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1430,4 +1430,32 @@ void hqlQueries() { "order by p " + // "limit 50"); } + + @Test // GH-2962 + void orderByWithNullsFirstOrLastShouldWork() { + + assertThatNoException().isThrownBy(() -> { + parseWithoutChanges(""" + select a, + case + when a.geaendertAm is null then a.erstelltAm + else a.geaendertAm end as mutationAm + from Element a + where a.erstelltDurch = :variable + order by mutationAm desc nulls first + """); + }); + + assertThatNoException().isThrownBy(() -> { + parseWithoutChanges(""" + select a, + case + when a.geaendertAm is null then a.erstelltAm + else a.geaendertAm end as mutationAm + from Element a + where a.erstelltDurch = :variable + order by mutationAm desc nulls last + """); + }); + } } From 6d20cab3563dc939df09061e65f611b610218cab Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 22 May 2023 15:20:10 -0500 Subject: [PATCH 385/821] ROUND doesn't need to be a reserved word. Because ROUND is a reserved word yet is NOT on the list of approved functions, it fails to get parsed. Simply dropping it from the list of reserved words makes it succeed in the HQL query parser. See #2964 Original Pull Request: #2966 --- .../data/jpa/repository/query/Hql.g4 | 1 - .../query/HqlQueryRendererTests.java | 27 ++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 80af871fc6..8796cf1f82 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -977,7 +977,6 @@ RANGE : R A N G E; RESPECT : R E S P E C T; RIGHT : R I G H T; ROLLUP : R O L L U P; -ROUND : R O U N D; ROW : R O W; ROWS : R O W S; SEARCH : S E A R C H; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 672c65aba4..f6343f51bd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1448,13 +1448,26 @@ void orderByWithNullsFirstOrLastShouldWork() { assertThatNoException().isThrownBy(() -> { parseWithoutChanges(""" - select a, - case - when a.geaendertAm is null then a.erstelltAm - else a.geaendertAm end as mutationAm - from Element a - where a.erstelltDurch = :variable - order by mutationAm desc nulls last + select a, + case + when a.geaendertAm is null then a.erstelltAm + else a.geaendertAm end as mutationAm + from Element a + where a.erstelltDurch = :variable + order by mutationAm desc nulls last + """); + }); + } + + @Test // GH-2964 + void roundFunctionShouldWorkLikeAnyOtherFunction() { + + assertThatNoException().isThrownBy(() -> { + parseWithoutChanges(""" + select round(count(ri) * 100 / max(ri.receipt.positions), 0) as perc + from StockOrderItem oi + right join StockReceiptItem ri + on ri.article = oi.article """); }); } From 235a6d3397622ef9f27a4e36cb56658a4decdde8 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 23 May 2023 07:40:04 -0500 Subject: [PATCH 386/821] HQL queries without a primary alias don't need Sorted properties prefixed. If a Hibernate query doesn't have a primary alias, don't attempt to prefix any Sort properties using it. See #2969 Original Pull Request #2971 --- .../repository/query/JpaQueryTransformerSupport.java | 11 +++++++++-- .../repository/query/HqlQueryTransformerTests.java | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java index acc73c772f..20b6fe920e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java @@ -12,6 +12,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.JpaSort; import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; /** * Transformational operations needed to support either {@link HqlQueryTransformer} or {@link JpqlQueryTransformer}. @@ -103,7 +104,7 @@ private void checkSortExpression(Sort.Order order) { */ private String generateOrderByArgument(@Nullable String primaryFromAlias, Sort.Order order) { - if (shouldPrefixWithAlias(order)) { + if (shouldPrefixWithAlias(order, primaryFromAlias)) { return primaryFromAlias + "." + order.getProperty(); } else { return order.getProperty(); @@ -115,9 +116,15 @@ private String generateOrderByArgument(@Nullable String primaryFromAlias, Sort.O * FROM clause's alias. * * @param order + * @param primaryFromAlias * @return boolean whether or not to apply the primary FROM clause's alias as a prefix */ - private boolean shouldPrefixWithAlias(Sort.Order order) { + private boolean shouldPrefixWithAlias(Sort.Order order, String primaryFromAlias) { + + // If there is no primary alias + if (ObjectUtils.isEmpty(primaryFromAlias)) { + return false; + } // If the Sort contains a function if (order.getProperty().contains("(")) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index 0b691a4c35..172be0489f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -966,6 +966,13 @@ void shouldHandleAliasInsideCaseStatement() { "order by newDateDue desc"); } + @Test // GH-2969 + void fromWithoutAPrimaryAliasShouldWork() { + + assertThat(createQueryFor("FROM Story WHERE enabled = true", Sort.by(Sort.Direction.DESC, "created"))) + .isEqualTo("FROM Story WHERE enabled = true order by created desc"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 93e4025a4c49710d87987260dcad588325d91ee8 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 23 May 2023 08:21:27 -0500 Subject: [PATCH 387/821] Support alternative NOT EQUALS operators in HQL. Hibernate also supports "!=" and "^=" as NOT EQUALS operators. See #2970 Original Pull Request #2972 --- .../data/jpa/repository/query/Hql.g4 | 3 ++- .../repository/query/HqlQueryRendererTests.java | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 8796cf1f82..f02e46084a 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -533,8 +533,9 @@ expressionOrPredicate ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-relational-comparisons +// NOTE: The TIP shows that "!=" is also supported. Hibernate's source code shows that "^=" is another NOT_EQUALS option as well. relationalExpression - : expression op=('=' | '>' | '>=' | '<' | '<=' | '<>' ) expression + : expression op=('=' | '>' | '>=' | '<' | '<=' | '<>' | '!=' | '^=' ) expression ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-between-predicate diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index f6343f51bd..6cb62adae3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -564,6 +564,22 @@ WHERE TYPE(e) <> Exempt """); } + @Test // GH-2970 + void alternateNotEqualsShouldAlsoWork() { + + assertQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) != Exempt + """); + + assertQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) ^= Exempt + """); + } + @Test void theRest5() { From e1fa886cb12bb270db1e7ba164c0beffe5c519a6 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 24 May 2023 14:36:46 -0500 Subject: [PATCH 388/821] Handle subqueries when used inside HQL INSERT statements. Potentialy subqueries can crop up inside INSERT statements. We need to handle this as well. NOTE: INSERT statements aren't defined in JPQL, so this problem doesn't overlap with standard JPA queries. See #2977 --- .../jpa/repository/query/HqlQueryTransformer.java | 2 ++ .../repository/query/HqlQueryTransformerTests.java | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java index 18a40573e5..6afba0f1f7 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java @@ -96,6 +96,8 @@ private static boolean isSubquery(ParserRuleContext ctx) { return true; } else if (ctx instanceof HqlParser.SelectStatementContext) { return false; + } else if (ctx instanceof HqlParser.InsertStatementContext) { + return false; } else { return isSubquery(ctx.getParent()); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index 172be0489f..e11dec1ebc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -973,6 +973,19 @@ void fromWithoutAPrimaryAliasShouldWork() { .isEqualTo("FROM Story WHERE enabled = true order by created desc"); } + @Test // GH-2977 + void isSubqueryThrowsException() { + + String query = """ + insert into MyEntity (id, col) + select max(id), col + from MyEntityStaging + group by col + """; + + assertThat(createQueryFor(query, Sort.unsorted())).isEqualToIgnoringWhitespace(query); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 9ea3f27714d29f211a8f903a43e48160ac8ffe5c Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 23 May 2023 15:14:27 -0500 Subject: [PATCH 389/821] Migrate to org.codehaus.mojo:aspectj-maven-plugin. Closes #2973. Original pull request: #2974 --- pom.xml | 4 +--- spring-data-jpa/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a47a8bfbf2..95f42845e2 100644 --- a/pom.xml +++ b/pom.xml @@ -27,9 +27,7 @@ - 16 - - + 17 4.10.1 3.0.3 6.2.1.Final diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 50e68c943b..248ac4b2fd 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -389,9 +389,9 @@ - io.starter + org.codehaus.mojo aspectj-maven-plugin - 1.12.9 + 1.14.0 org.aspectj From 8e3c52c7d2f6d16f6c53806fa4035283c4c4fdb0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 26 May 2023 10:23:51 +0200 Subject: [PATCH 390/821] Polishing. Remove unnecessary property declarations. Original pull request: #2974 See #2973. --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95f42845e2..0bc96784c3 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,6 @@ - 17 4.10.1 3.0.3 6.2.1.Final From 3817e3851a453889cfa47a319b9949b189495006 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 26 May 2023 13:34:22 -0500 Subject: [PATCH 391/821] Support Common Table Expressions in HQL parser. See #2981. --- .../data/jpa/repository/query/Hql.g4 | 30 +++- .../repository/query/HqlQueryRenderer.java | 162 +++++++++++++++++- .../query/JpaQueryParsingToken.java | 7 + .../query/HqlQueryRendererTests.java | 12 ++ 4 files changed, 203 insertions(+), 8 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index f02e46084a..674e67721a 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -51,9 +51,37 @@ selectStatement ; queryExpression - : orderedQuery (setOperator orderedQuery)* + : withClause? orderedQuery (setOperator orderedQuery)* ; +withClause + : WITH cte (',' cte)* + ; + +cte + : identifier AS (NOT? MATERIALIZED)? '(' queryExpression ')' searchClause? cycleClause? + ; + +searchClause + : SEARCH (BREADTH | DEPTH) FIRST BY searchSpecifications SET identifier + ; + +searchSpecifications + : searchSpecification (',' searchSpecification)* + ; + +searchSpecification + : identifier sortDirection? nullsPrecedence? + ; + +cycleClause + : CYCLE cteAttributes SET identifier (TO literal DEFAULT literal)? (USING identifier)? + ; + +cteAttributes + : identifier (',' identifier)* + ; + orderedQuery : (query | '(' queryExpression ')') queryOrder? ; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 1adfa14216..39d6271fe3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -59,6 +59,10 @@ public List visitQueryExpression(HqlParser.QueryExpression List tokens = new ArrayList<>(); + if (ctx.withClause() != null) { + tokens.addAll(visit(ctx.withClause())); + } + tokens.addAll(visit(ctx.orderedQuery(0))); for (int i = 1; i < ctx.orderedQuery().size(); i++) { @@ -70,6 +74,150 @@ public List visitQueryExpression(HqlParser.QueryExpression return tokens; } + @Override + public List visitWithClause(HqlParser.WithClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_WITH); + + ctx.cte().forEach(cteContext -> { + + tokens.addAll(visit(cteContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitCte(HqlParser.CteContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.identifier())); + tokens.add(TOKEN_AS); + NOSPACE(tokens); + + if (ctx.NOT() != null) { + tokens.add(TOKEN_NOT); + } + if (ctx.MATERIALIZED() != null) { + tokens.add(TOKEN_MATERIALIZED); + } + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.queryExpression())); + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.searchClause() != null) { + tokens.addAll(visit(ctx.searchClause())); + } + if (ctx.cycleClause() != null) { + tokens.addAll(visit(ctx.cycleClause())); + } + + return tokens; + } + + @Override + public List visitSearchClause(HqlParser.SearchClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SEARCH().getText())); + + if (ctx.BREADTH() != null) { + tokens.add(new JpaQueryParsingToken(ctx.BREADTH().getText())); + } else if (ctx.DEPTH() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DEPTH().getText())); + } + + tokens.add(new JpaQueryParsingToken(ctx.FIRST().getText())); + tokens.add(new JpaQueryParsingToken(ctx.BY().getText())); + tokens.addAll(visit(ctx.searchSpecifications())); + tokens.add(new JpaQueryParsingToken(ctx.SET().getText())); + tokens.addAll(visit(ctx.identifier())); + + return tokens; + } + + @Override + public List visitSearchSpecifications(HqlParser.SearchSpecificationsContext ctx) { + + List tokens = new ArrayList<>(); + + ctx.searchSpecification().forEach(searchSpecificationContext -> { + + tokens.addAll(visit(searchSpecificationContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitSearchSpecification(HqlParser.SearchSpecificationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.identifier())); + + if (ctx.sortDirection() != null) { + tokens.addAll(visit(ctx.sortDirection())); + } + + if (ctx.nullsPrecedence() != null) { + tokens.addAll(visit(ctx.nullsPrecedence())); + } + + return tokens; + } + + @Override + public List visitCycleClause(HqlParser.CycleClauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CYCLE().getText())); + tokens.addAll(visit(ctx.cteAttributes())); + tokens.add(new JpaQueryParsingToken(ctx.SET().getText())); + tokens.addAll(visit(ctx.identifier(0))); + + if (ctx.TO() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TO().getText())); + tokens.addAll(visit(ctx.literal(0))); + tokens.add(new JpaQueryParsingToken(ctx.DEFAULT().getText())); + tokens.addAll(visit(ctx.literal(1))); + } + + if (ctx.USING() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.USING().getText())); + tokens.addAll(visit(ctx.identifier(1))); + } + + return tokens; + } + + @Override + public List visitCteAttributes(HqlParser.CteAttributesContext ctx) { + + List tokens = new ArrayList<>(); + + ctx.identifier().forEach(identifierContext -> { + + tokens.addAll(visit(identifierContext)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + @Override public List visitOrderedQuery(HqlParser.OrderedQueryContext ctx) { @@ -1876,7 +2024,7 @@ public List visitNotPredicate(HqlParser.NotPredicateContex List tokens = new ArrayList<>(); - tokens.add(new JpaQueryParsingToken(ctx.NOT())); + tokens.add(TOKEN_NOT); tokens.addAll(visit(ctx.predicate())); return tokens; @@ -1919,7 +2067,7 @@ public List visitBetweenExpression(HqlParser.BetweenExpres tokens.addAll(visit(ctx.expression(0))); if (ctx.NOT() != null) { - tokens.add(new JpaQueryParsingToken(ctx.NOT())); + tokens.add(TOKEN_NOT); } tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); @@ -1939,7 +2087,7 @@ public List visitDealingWithNullExpression(HqlParser.Deali tokens.add(new JpaQueryParsingToken(ctx.IS())); if (ctx.NOT() != null) { - tokens.add(new JpaQueryParsingToken(ctx.NOT())); + tokens.add(TOKEN_NOT); } if (ctx.NULL() != null) { @@ -1962,7 +2110,7 @@ public List visitStringPatternMatching(HqlParser.StringPat tokens.addAll(visit(ctx.expression(0))); if (ctx.NOT() != null) { - tokens.add(new JpaQueryParsingToken(ctx.NOT())); + tokens.add(TOKEN_NOT); } if (ctx.LIKE() != null) { @@ -1990,7 +2138,7 @@ public List visitInExpression(HqlParser.InExpressionContex tokens.addAll(visit(ctx.expression())); if (ctx.NOT() != null) { - tokens.add(new JpaQueryParsingToken(ctx.NOT())); + tokens.add(TOKEN_NOT); } tokens.add(new JpaQueryParsingToken(ctx.IN())); @@ -2081,14 +2229,14 @@ public List visitCollectionExpression(HqlParser.Collection tokens.add(new JpaQueryParsingToken(ctx.IS())); if (ctx.NOT() != null) { - tokens.add(new JpaQueryParsingToken(ctx.NOT())); + tokens.add(TOKEN_NOT); } tokens.add(new JpaQueryParsingToken(ctx.EMPTY())); } else if (ctx.MEMBER() != null) { if (ctx.NOT() != null) { - tokens.add(new JpaQueryParsingToken(ctx.NOT())); + tokens.add(TOKEN_NOT); } tokens.add(new JpaQueryParsingToken(ctx.MEMBER())); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java index e4ca63df2e..59c409e9e3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java @@ -59,6 +59,13 @@ class JpaQueryParsingToken { public static final JpaQueryParsingToken TOKEN_DESC = new JpaQueryParsingToken("desc", false); public static final JpaQueryParsingToken TOKEN_ASC = new JpaQueryParsingToken("asc", false); + + public static final JpaQueryParsingToken TOKEN_WITH = new JpaQueryParsingToken("WITH"); + + public static final JpaQueryParsingToken TOKEN_NOT = new JpaQueryParsingToken("NOT"); + + public static final JpaQueryParsingToken TOKEN_MATERIALIZED = new JpaQueryParsingToken("materialized"); + /** * The text value of the token. */ diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 6cb62adae3..d3a9f83264 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1487,4 +1487,16 @@ select round(count(ri) * 100 / max(ri.receipt.positions), 0) as perc """); }); } + + @Test // GH-2981 + void cteWithClauseShouldWork() { + + assertQuery(""" + WITH maxId AS(select max(sr.snapshot.id) snapshotId from SnapshotReference sr + where sr.id.selectionId = ?1 and sr.enabled + group by sr.userId + ) + select sr from maxId m join SnapshotReference sr on sr.snapshot.id = m.snapshotId + """); + } } From 6c315a3bbc38f3facaa6c6c36a2575afa678ce19 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 26 May 2023 14:35:36 -0500 Subject: [PATCH 392/821] Properly handle reserved words as entity names. In JPQL and HQL, we need to properly handle reserved words that crop up as entity names (which is legal). See #2982. --- .../data/jpa/repository/query/Hql.g4 | 1 + .../data/jpa/repository/query/Jpql.g4 | 12 ++++++------ .../jpa/repository/query/JpqlQueryRenderer.java | 8 ++------ .../repository/query/HqlQueryRendererTests.java | 17 +++++++++++++++++ .../query/JpqlQueryRendererTests.java | 17 +++++++++++++++++ 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 674e67721a..488031b42c 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -713,6 +713,7 @@ reservedWord | FETCH | FILTER | FIRST + | FLOOR | FOLLOWING | FOR | FORMAT diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 00491c8415..546f49c154 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -596,12 +596,13 @@ trim_character identification_variable : IDENTIFICATION_VARIABLE - | ORDER // Gap in the spec requires supporting 'Order' as an entity name - | COUNT // Gap in the spec requires supporting 'count' as a possible name - | KEY // Gap in the sepc requires supported 'key' as a possible name - | LEFT + | f=(COUNT | INNER + | KEY + | LEFT + | ORDER | OUTER + | FLOOR) ; constructor_name @@ -682,8 +683,7 @@ collection_value_field ; entity_name - : identification_variable - | identification_variable ('.' identification_variable)* // Hibernate sometimes expands the entity name to FQDN when using named queries + : identification_variable ('.' identification_variable)* // Hibernate sometimes expands the entity name to FQDN when using named queries ; result_variable diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java index 0598e7029f..0a148f145f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -2118,12 +2118,8 @@ public List visitIdentification_variable(JpqlParser.Identi if (ctx.IDENTIFICATION_VARIABLE() != null) { return List.of(new JpaQueryParsingToken(ctx.IDENTIFICATION_VARIABLE())); - } else if (ctx.COUNT() != null) { - return List.of(new JpaQueryParsingToken(ctx.COUNT())); - } else if (ctx.ORDER() != null) { - return List.of(new JpaQueryParsingToken(ctx.ORDER())); - } else if (ctx.KEY() != null) { - return List.of(new JpaQueryParsingToken(ctx.KEY())); + } else if (ctx.f != null) { + return List.of(new JpaQueryParsingToken(ctx.f)); } else { return List.of(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index d3a9f83264..c6c69d24ef 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1499,4 +1499,21 @@ WITH maxId AS(select max(sr.snapshot.id) snapshotId from SnapshotReference sr select sr from maxId m join SnapshotReference sr on sr.snapshot.id = m.snapshotId """); } + + @Test // GH-2982 + void floorShouldBeValidEntityName() { + + assertQuery(""" + SELECT f + FROM Floor f + WHERE f.name = :name + """); + + assertQuery(""" + SELECT r + FROM Room r + JOIN r.floor f + WHERE f.name = :name + """); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index e0124b6907..14900f8458 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -914,4 +914,21 @@ void theRest38() { WHERE l.product.name = ?1 """); } + + @Test // GH-2982 + void floorShouldBeValidEntityName() { + + assertQuery(""" + SELECT f + FROM Floor f + WHERE f.name = :name + """); + + assertQuery(""" + SELECT r + FROM Room r + JOIN r.floor f + WHERE f.name = :name + """); + } } From 192ba02bcb4bff2fa55394fa31514bd89a762b21 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 4 May 2023 16:13:45 -0500 Subject: [PATCH 393/821] Close `StoredProcedureQuery` if it implements AutoCloseable. If the JPA provider's implementation of StoredProcedureQuery implements AutoCloseable, invoke the close method when completed. Resolves #2915. Original pull request: #2938 --- .../repository/query/JpaQueryExecution.java | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 90ddbaa671..428fba44b4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -332,24 +332,36 @@ protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAcce Assert.isInstanceOf(StoredProcedureJpaQuery.class, jpaQuery); StoredProcedureJpaQuery storedProcedureJpaQuery = (StoredProcedureJpaQuery) jpaQuery; + StoredProcedureQuery storedProcedure = storedProcedureJpaQuery.createQuery(accessor); - boolean returnsResultSet = storedProcedure.execute(); + try { + + boolean returnsResultSet = storedProcedure.execute(); + + if (returnsResultSet) { - if (returnsResultSet) { + if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) { + throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); + } - if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) { - throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); + if (storedProcedureJpaQuery.getQueryMethod().isCollectionQuery()) { + return storedProcedure.getResultList(); + } else { + return storedProcedure.getSingleResult(); + } } - if (storedProcedureJpaQuery.getQueryMethod().isCollectionQuery()) { - return storedProcedure.getResultList(); - } else { - return storedProcedure.getSingleResult(); + return storedProcedureJpaQuery.extractOutputValue(storedProcedure); + + } finally { + + if (storedProcedure instanceof AutoCloseable autoCloseable) { + try { + autoCloseable.close(); + } catch (Exception ignored) {} } } - - return storedProcedureJpaQuery.extractOutputValue(storedProcedure); } } From 8717db6618d41cbf9815e290ef2c22fb069176f3 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 30 May 2023 14:57:08 +0200 Subject: [PATCH 394/821] Polishing. Simplify code flow. Introduce flag to capture whether a stored procedure uses collection return types. Remove unconditionally the Optional converter as we're already on Java 17 and do not require the Java 8 guard. See #2915 Original pull request: #2938 --- .../repository/query/AbstractJpaQuery.java | 2 +- .../repository/query/JpaQueryExecution.java | 54 ++++++------------- .../support/JpaRepositoryTests.java | 3 +- 3 files changed, 19 insertions(+), 40 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 7e70d350f2..32c5f438b1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -92,7 +92,7 @@ public AbstractJpaQuery(JpaQueryMethod method, EntityManager em) { if (method.isStreamQuery()) { return new StreamExecution(); } else if (method.isProcedureQuery()) { - return new ProcedureExecution(); + return new ProcedureExecution(method.isCollectionQuery()); } else if (method.isCollectionQuery()) { return new CollectionExecution(); } else if (method.isSliceQuery()) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 428fba44b4..43d70ca8a4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -68,7 +68,7 @@ public abstract class JpaQueryExecution { conversionService.addConverter(JpaResultConverters.BlobToByteArrayConverter.INSTANCE); conversionService.removeConvertible(Collection.class, Object.class); - potentiallyRemoveOptionalConverter(conversionService); + conversionService.removeConvertible(Object.class, Optional.class); CONVERSION_SERVICE = conversionService; } @@ -197,7 +197,7 @@ static class PagedExecution extends JpaQueryExecution { @Override @SuppressWarnings("unchecked") - protected Object doExecute(final AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) { + protected Object doExecute(AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) { Query query = repositoryQuery.createQuery(accessor); @@ -324,20 +324,25 @@ protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccesso */ static class ProcedureExecution extends JpaQueryExecution { + private final boolean collectionQuery; + private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a @Procedure method without a surrounding transaction that keeps the connection open so that the ResultSet can actually be consumed; Make sure the consumer code uses @Transactional or any other way of declaring a (read-only) transaction"; + ProcedureExecution(boolean collectionQuery) { + this.collectionQuery = collectionQuery; + } + @Override protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAccessor accessor) { Assert.isInstanceOf(StoredProcedureJpaQuery.class, jpaQuery); - StoredProcedureJpaQuery storedProcedureJpaQuery = (StoredProcedureJpaQuery) jpaQuery; - - StoredProcedureQuery storedProcedure = storedProcedureJpaQuery.createQuery(accessor); + StoredProcedureJpaQuery query = (StoredProcedureJpaQuery) jpaQuery; + StoredProcedureQuery procedure = query.createQuery(accessor); try { - boolean returnsResultSet = storedProcedure.execute(); + boolean returnsResultSet = procedure.execute(); if (returnsResultSet) { @@ -345,20 +350,15 @@ protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAcce throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); } - if (storedProcedureJpaQuery.getQueryMethod().isCollectionQuery()) { - return storedProcedure.getResultList(); - } else { - return storedProcedure.getSingleResult(); - } + return collectionQuery ? procedure.getResultList() : procedure.getSingleResult(); } - return storedProcedureJpaQuery.extractOutputValue(storedProcedure); - + return query.extractOutputValue(procedure); } finally { - if (storedProcedure instanceof AutoCloseable autoCloseable) { + if (procedure instanceof AutoCloseable ac) { try { - autoCloseable.close(); + ac.close(); } catch (Exception ignored) {} } } @@ -375,10 +375,10 @@ static class StreamExecution extends JpaQueryExecution { private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed; Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction"; - private static Method streamMethod = ReflectionUtils.findMethod(Query.class, "getResultStream"); + private static final Method streamMethod = ReflectionUtils.findMethod(Query.class, "getResultStream"); @Override - protected Object doExecute(final AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { + protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) { if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) { throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); @@ -399,24 +399,4 @@ protected Object doExecute(final AbstractJpaQuery query, JpaParametersParameterA } } - /** - * Removes the converter being able to convert any object into an {@link Optional} from the given - * {@link ConversionService} in case we're running on Java 8. - * - * @param conversionService must not be {@literal null}. - */ - public static void potentiallyRemoveOptionalConverter(ConfigurableConversionService conversionService) { - - ClassLoader classLoader = JpaQueryExecution.class.getClassLoader(); - - if (ClassUtils.isPresent("java.util.Optional", classLoader)) { - - try { - - Class optionalType = ClassUtils.forName("java.util.Optional", classLoader); - conversionService.removeConvertible(Object.class, optionalType); - - } catch (ClassNotFoundException | LinkageError o_O) {} - } - } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java index e0f7a22982..586ef06687 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -59,7 +59,6 @@ class JpaRepositoryTests { @BeforeEach void setUp() { - repository = new JpaRepositoryFactory(em).getRepository(SampleEntityRepository.class); idClassRepository = new JpaRepositoryFactory(em).getRepository(SampleWithIdClassRepository.class); } From e0cfe41095008c52a776514e330f1bac44ead276 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 30 May 2023 10:48:42 -0500 Subject: [PATCH 395/821] Upgrade to Postgres 11 for integration testing. Postgres stored procedures requires some adjustments in order to upgrade to Postgres 11. See #2903 --- ...PostgresStoredProcedureIntegrationTests.java | 4 +++- ...edProcedureNullHandlingIntegrationTests.java | 4 +++- .../postgres-nullable-stored-procedures.sql | 8 ++------ .../scripts/postgres-stored-procedures.sql | 17 ++++------------- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index 10c07c1a78..96c0f5e710 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -47,6 +47,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.jpa.util.DisabledOnHibernate61; import org.springframework.data.jpa.util.DisabledOnHibernate62; import org.springframework.jdbc.datasource.init.DataSourceInitializer; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; @@ -68,6 +69,7 @@ * @author Greg Turnquist * @author Yanming Zhou */ +@DisabledOnHibernate61 // GH-2903 @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = PostgresStoredProcedureIntegrationTests.Config.class) @@ -203,7 +205,7 @@ static class Config { @Bean(initMethod = "start", destroyMethod = "stop") public PostgreSQLContainer container() { - return new PostgreSQLContainer<>("postgres:10.21") // + return new PostgreSQLContainer<>("postgres:15.3") // .withUsername("postgres"); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java index 10aa204f37..bf87de2be4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java @@ -44,6 +44,7 @@ import org.springframework.data.jpa.repository.Temporal; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.jpa.util.DisabledOnHibernate61; import org.springframework.jdbc.datasource.init.DataSourceInitializer; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; @@ -62,6 +63,7 @@ * * @author Greg Turnquist */ +@DisabledOnHibernate61 // GH-2903 @Transactional @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = PostgresStoredProcedureNullHandlingIntegrationTests.Config.class) @@ -109,7 +111,7 @@ static class Config { @Bean(initMethod = "start", destroyMethod = "stop") public PostgreSQLContainer container() { - return new PostgreSQLContainer<>("postgres:10.21") // + return new PostgreSQLContainer<>("postgres:15.3") // .withUsername("postgres"); } diff --git a/spring-data-jpa/src/test/resources/scripts/postgres-nullable-stored-procedures.sql b/spring-data-jpa/src/test/resources/scripts/postgres-nullable-stored-procedures.sql index 03a619176e..82ee419ab6 100644 --- a/spring-data-jpa/src/test/resources/scripts/postgres-nullable-stored-procedures.sql +++ b/spring-data-jpa/src/test/resources/scripts/postgres-nullable-stored-procedures.sql @@ -6,8 +6,7 @@ CREATE TABLE test_model CONSTRAINT test_model_pk PRIMARY KEY (ID) );; -CREATE OR REPLACE FUNCTION countByUuid(this_uuid uuid) - RETURNS int +CREATE OR REPLACE PROCEDURE countByUuid(IN this_uuid uuid) LANGUAGE 'plpgsql' AS $BODY$ @@ -18,13 +17,11 @@ BEGIN INTO c FROM test_model WHERE test_model.uuid = this_uuid; - RETURN c; END; $BODY$ ;; -CREATE OR REPLACE FUNCTION countByLocalDate(this_local_date DATE) - RETURNS int +CREATE OR REPLACE PROCEDURE countByLocalDate(IN this_local_date DATE) LANGUAGE 'plpgsql' AS $BODY$ @@ -35,7 +32,6 @@ BEGIN INTO c FROM test_model WHERE test_model.local_date = this_local_date; - RETURN c; END; $BODY$ ;; diff --git a/spring-data-jpa/src/test/resources/scripts/postgres-stored-procedures.sql b/spring-data-jpa/src/test/resources/scripts/postgres-stored-procedures.sql index 9d8226ee75..59097c8515 100644 --- a/spring-data-jpa/src/test/resources/scripts/postgres-stored-procedures.sql +++ b/spring-data-jpa/src/test/resources/scripts/postgres-stored-procedures.sql @@ -8,38 +8,29 @@ CREATE TABLE employee INSERT INTO employee (ID, NAME) VALUES (3, 'Fanny');; INSERT INTO employee (ID, NAME) VALUES (4, 'Gabriel');; -CREATE OR REPLACE FUNCTION get_employees() - RETURNS refcursor +CREATE OR REPLACE PROCEDURE get_employees(OUT ref refcursor) LANGUAGE 'plpgsql' AS $BODY$ -DECLARE - ref refcursor; BEGIN OPEN ref FOR SELECT * FROM employee; - RETURN ref; END; $BODY$;; -CREATE OR REPLACE FUNCTION get_employees_count() - RETURNS integer +CREATE OR REPLACE PROCEDURE get_employees_count(OUT results integer) LANGUAGE 'plpgsql' AS $BODY$ BEGIN - RETURN (SELECT COUNT(*) FROM employee); + results = (SELECT COUNT(*) FROM employee); END; $BODY$;; -CREATE OR REPLACE FUNCTION get_single_employee() - RETURNS refcursor +CREATE OR REPLACE PROCEDURE get_single_employee(OUT ref refcursor) LANGUAGE 'plpgsql' AS $BODY$ -DECLARE - ref refcursor; BEGIN OPEN ref FOR SELECT * FROM employee WHERE employee.ID = 3; - RETURN ref; END; $BODY$;; From 38ced42d6a6b3fb8714c9d70a59e0526b57679fc Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 15 May 2023 14:12:19 -0500 Subject: [PATCH 396/821] Resolve handling of ESCAPE clause with LIKE queries on Hibernate. The HQL parser has to handle parameters in addition to character values in order to support SpEL. Closes #2954 Original Pull Request: #2956. --- .../data/jpa/repository/query/Hql.g4 | 2 +- .../jpa/repository/query/HqlQueryRenderer.java | 2 +- .../repository/query/JpqlQueryRenderer.java | 6 ++++++ .../EclipseLinkUserRepositoryFinderTests.java | 18 ++++++++++++++++++ .../repository/UserRepositoryFinderTests.java | 7 +------ .../jpa/repository/sample/UserRepository.java | 2 +- 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 488031b42c..731bb16c7d 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -579,7 +579,7 @@ dealingWithNullExpression // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-like-predicate stringPatternMatching - : expression NOT? (LIKE | ILIKE) expression (ESCAPE character)? + : expression NOT? (LIKE | ILIKE) expression (ESCAPE expression)? ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-elements-indices diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 39d6271fe3..5446173ca2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -2124,7 +2124,7 @@ public List visitStringPatternMatching(HqlParser.StringPat if (ctx.ESCAPE() != null) { tokens.add(new JpaQueryParsingToken(ctx.ESCAPE())); - tokens.addAll(visit(ctx.character())); + tokens.addAll(visit(ctx.expression(2))); } return tokens; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java index 0a148f145f..b5a41fe998 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -1206,6 +1206,12 @@ public List visitLike_expression(JpqlParser.Like_expressio tokens.add(new JpaQueryParsingToken(ctx.LIKE())); tokens.addAll(visit(ctx.pattern_value())); + if (ctx.ESCAPE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ESCAPE())); + tokens.addAll(visit(ctx.escape_character())); + } + return tokens; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java index 0caf735d4e..1b6bc2a7bf 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java @@ -34,4 +34,22 @@ void executesNotInQueryCorrectly() {} @Disabled @Override void executesInKeywordForPageCorrectly() {} + + @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") + @Override + void escapingInLikeSpels() { + super.escapingInLikeSpels(); + } + + @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") + @Override + void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { + super.escapingInLikeSpelsInThePresenceOfEscapeCharacters(); + } + + @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") + @Override + void escapingInLikeSpelsInThePresenceOfEscapedWildcards() { + super.escapingInLikeSpelsInThePresenceOfEscapedWildcards(); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 6fa3e2b00f..bccc417ea0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -25,7 +25,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -235,7 +234,6 @@ void parametersForContainsGetProperlyEscaped() { .isEmpty(); } - @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1519 void escapingInLikeSpels() { @@ -246,7 +244,6 @@ void escapingInLikeSpels() { assertThat(userRepository.findContainingEscaped("att_")).containsExactly(extra); } - @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { @@ -256,7 +253,6 @@ void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { assertThat(userRepository.findContainingEscaped("att\\x")).containsExactly(withEscapeCharacter); } - @Disabled("Can't get ESCAPE clause working with Hibernate") @Test // DATAJPA-1522 void escapingInLikeSpelsInThePresenceOfEscapedWildcards() { @@ -288,8 +284,7 @@ void executesQueryWithProjectionContainingReferenceToPluralAttribute() { List rolesAndFirstnameBy = userRepository.findRolesAndFirstnameBy(); - assertThat(rolesAndFirstnameBy) - .isNotNull(); + assertThat(rolesAndFirstnameBy).isNotNull(); for (RolesAndFirstname rolesAndFirstname : rolesAndFirstnameBy) { assertThat(rolesAndFirstname.getFirstname()).isNotNull(); 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 5eb7490398..821b119a73 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 @@ -625,7 +625,7 @@ List findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity List findByNamedQueryWithConstructorExpression(); // DATAJPA-1519 - @Query("select u from User u where u.lastname like '%?#{escape([0])}%' escape ?#{escapeCharacter()}") + @Query("select u from User u where u.lastname like %?#{escape([0])}% escape ?#{escapeCharacter()}") List findContainingEscaped(String namePart); // DATAJPA-1303 From 97255cfabb05c2f92dbcbff048dc15dc5b2a7d61 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 17 May 2023 15:47:19 -0500 Subject: [PATCH 397/821] Resolve handling of ESCAPE clause with LIKE queries on EclipseLink. Migrate tests to H2 to verify LIKE with ESCAPE works properly on EclipseLink with Spring Data JPA. Resolves #2955 Original Pull Request: #2956. --- pom.xml | 1 + spring-data-jpa/pom.xml | 7 ++++ .../data/jpa/repository/query/Hql.g4 | 2 +- .../repository/query/HqlQueryRenderer.java | 6 +++- .../EclipseLinkUserRepositoryFinderTests.java | 20 ++--------- .../repository/UserRepositoryFinderTests.java | 3 +- .../namespace-application-context-h2.xml | 31 ++++++++++++++++ .../src/test/resources/eclipselink-h2.xml | 21 +++++++++++ .../src/test/resources/infrastructure-h2.xml | 35 +++++++++++++++++++ .../src/test/resources/scripts/h2-init.sql | 1 + .../scripts/h2-stored-procedures.sql | 9 +++++ 11 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 spring-data-jpa/src/test/resources/config/namespace-application-context-h2.xml create mode 100644 spring-data-jpa/src/test/resources/eclipselink-h2.xml create mode 100644 spring-data-jpa/src/test/resources/infrastructure-h2.xml create mode 100644 spring-data-jpa/src/test/resources/scripts/h2-init.sql create mode 100644 spring-data-jpa/src/test/resources/scripts/h2-stored-procedures.sql diff --git a/pom.xml b/pom.xml index 0bc96784c3..f8e44fb758 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ 3.0.3 6.2.1.Final 2.7.1 +

    2.1.214

    4.5 8.0.31 42.5.0 diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 248ac4b2fd..7145881fb0 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -100,6 +100,13 @@ test + + com.h2database + h2 + ${h2} + test + + com.mysql diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 731bb16c7d..b702bad0c1 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -579,7 +579,7 @@ dealingWithNullExpression // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-like-predicate stringPatternMatching - : expression NOT? (LIKE | ILIKE) expression (ESCAPE expression)? + : expression NOT? (LIKE | ILIKE) expression (ESCAPE (character|parameter))? ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-elements-indices diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 5446173ca2..17458b20e6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -2124,7 +2124,11 @@ public List visitStringPatternMatching(HqlParser.StringPat if (ctx.ESCAPE() != null) { tokens.add(new JpaQueryParsingToken(ctx.ESCAPE())); - tokens.addAll(visit(ctx.expression(2))); + if (ctx.character() != null) { + tokens.addAll(visit(ctx.character())); + } else if (ctx.parameter() != null) { + tokens.addAll(visit(ctx.parameter())); + } } return tokens; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java index 1b6bc2a7bf..473331252a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkUserRepositoryFinderTests.java @@ -23,8 +23,9 @@ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477. * * @author Oliver Gierke + * @author Greg Turnquist */ -@ContextConfiguration("classpath:eclipselink.xml") +@ContextConfiguration("classpath:eclipselink-h2.xml") class EclipseLinkUserRepositoryFinderTests extends UserRepositoryFinderTests { @Disabled @@ -35,21 +36,4 @@ void executesNotInQueryCorrectly() {} @Override void executesInKeywordForPageCorrectly() {} - @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") - @Override - void escapingInLikeSpels() { - super.escapingInLikeSpels(); - } - - @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") - @Override - void escapingInLikeSpelsInThePresenceOfEscapeCharacters() { - super.escapingInLikeSpelsInThePresenceOfEscapeCharacters(); - } - - @Disabled("Can't get ESCAPE clause working with EclipseLink. See #2955") - @Override - void escapingInLikeSpelsInThePresenceOfEscapedWildcards() { - super.escapingInLikeSpelsInThePresenceOfEscapedWildcards(); - } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index bccc417ea0..4b6c4bb9eb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -52,10 +52,11 @@ * * @author Oliver Gierke * @author Krzysztof Krason + * @author Greg Turnquist * @see QueryLookupStrategy */ @ExtendWith(SpringExtension.class) -@ContextConfiguration(locations = "classpath:config/namespace-application-context.xml") +@ContextConfiguration(locations = "classpath:config/namespace-application-context-h2.xml") @Transactional class UserRepositoryFinderTests { diff --git a/spring-data-jpa/src/test/resources/config/namespace-application-context-h2.xml b/spring-data-jpa/src/test/resources/config/namespace-application-context-h2.xml new file mode 100644 index 0000000000..9cb3eab275 --- /dev/null +++ b/spring-data-jpa/src/test/resources/config/namespace-application-context-h2.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/spring-data-jpa/src/test/resources/eclipselink-h2.xml b/spring-data-jpa/src/test/resources/eclipselink-h2.xml new file mode 100644 index 0000000000..71d4ab6372 --- /dev/null +++ b/spring-data-jpa/src/test/resources/eclipselink-h2.xml @@ -0,0 +1,21 @@ + + + + + + + + org.h2.Driver + jdbc:h2:mem:hades + sa + + create-tables + false + SEVERE + + + diff --git a/spring-data-jpa/src/test/resources/infrastructure-h2.xml b/spring-data-jpa/src/test/resources/infrastructure-h2.xml new file mode 100644 index 0000000000..723454c015 --- /dev/null +++ b/spring-data-jpa/src/test/resources/infrastructure-h2.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-data-jpa/src/test/resources/scripts/h2-init.sql b/spring-data-jpa/src/test/resources/scripts/h2-init.sql new file mode 100644 index 0000000000..1c8a0e7976 --- /dev/null +++ b/spring-data-jpa/src/test/resources/scripts/h2-init.sql @@ -0,0 +1 @@ +; \ No newline at end of file diff --git a/spring-data-jpa/src/test/resources/scripts/h2-stored-procedures.sql b/spring-data-jpa/src/test/resources/scripts/h2-stored-procedures.sql new file mode 100644 index 0000000000..1023dc1a39 --- /dev/null +++ b/spring-data-jpa/src/test/resources/scripts/h2-stored-procedures.sql @@ -0,0 +1,9 @@ +/; +DROP alias IF EXISTS plus1inout +/; +CREATE alias plus1inout AS $$ +Integer plus1inout(Integer arg) { + return arg + 1; +} +$$ +/; From 28de27107d7cfab44b20529696652d269c8f6a96 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 31 May 2023 15:52:16 -0500 Subject: [PATCH 398/821] Verify that projections work with subqueries. Interface-based projections used to NOT work when the query had a subquery in it. But with the new query parser, it already handles it. So this simply captures the test providing that corner case. See #2008 Original Pull Request: #420 --- .../jpa/repository/UserRepositoryFinderTests.java | 11 +++++++++++ .../data/jpa/repository/sample/UserRepository.java | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 4b6c4bb9eb..5ded8dd6bb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -315,4 +315,15 @@ void rejectsStreamExecutionIfNoSurroundingTransactionActive() { void executesNamedQueryWithConstructorExpression() { userRepository.findByNamedQueryWithConstructorExpression(); } + + @Test // DATAJPA-1713, GH-2008 + public void selectProjectionWithSubselect() { + + List dtos = userRepository.findProjectionBySubselect(); + + assertThat(dtos).flatExtracting(UserRepository.NameOnly::getFirstname) // + .containsExactly("Dave", "Carter", "Oliver August"); + assertThat(dtos).flatExtracting(UserRepository.NameOnly::getLastname) // + .containsExactly("Matthews", "Beauford", "Matthews"); + } } 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 821b119a73..a1bd087119 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 @@ -718,6 +718,10 @@ List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter nativeQuery = true) int mergeNativeStatement(); + // DATAJPA-1713, GH-2008 + @Query("select u from User u where u.firstname >= (select Min(u0.firstname) from User u0)") + List findProjectionBySubselect(); + interface RolesAndFirstname { String getFirstname(); From 03cb819e24b572568429acbd1a275af7c4c0c943 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Fri, 2 Jun 2023 16:08:43 +0800 Subject: [PATCH 399/821] Add missing null check for metadata. 1. null check on variable `metadata` is missing in method `existsById` 2. `CrudMethodMetadata::getComment` should be `@Nullable` Closes #2991 Original pull request: #2995 --- .../data/jpa/repository/support/CrudMethodMetadata.java | 2 ++ .../support/CrudMethodMetadataPostProcessor.java | 3 ++- .../data/jpa/repository/support/SimpleJpaRepository.java | 9 +-------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java index 359df17cbb..0cf59bca75 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java @@ -33,6 +33,7 @@ * @author Mark Paluch * @author Jens Schauder * @author Greg Turnquist + * @author Yanming Zhou */ public interface CrudMethodMetadata { @@ -66,6 +67,7 @@ public interface CrudMethodMetadata { * @return * @since 3.0 */ + @Nullable String getComment(); /** diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 3128fed675..4932f7f1fb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -56,6 +56,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Jens Schauder + * @author Yanming Zhou */ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, BeanClassLoaderAware { @@ -181,7 +182,7 @@ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { private final @Nullable LockModeType lockModeType; private final org.springframework.data.jpa.repository.support.QueryHints queryHints; private final org.springframework.data.jpa.repository.support.QueryHints queryHintsForCount; - private final String comment; + private final @Nullable String comment; private final Optional entityGraph; private final Method method; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index e537c9537d..f48d9c3bcd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -370,14 +370,7 @@ public boolean existsById(ID id) { TypedQuery query = em.createQuery(existsQuery, Long.class); - Map hints = new HashMap<>(); - getQueryHints().withFetchGraphs(em).forEach(hints::put); - - if (metadata.getComment() != null && provider.getCommentHintKey() != null) { - hints.put(provider.getCommentHintKey(), provider.getCommentHintValue(metadata.getComment())); - } - - hints.forEach(query::setHint); + applyQueryHints(query); if (!entityInformation.hasCompositeId()) { query.setParameter(idAttributeNames.iterator().next(), id); From 48cb33805bf56fb2b39019a02609ca3e7e4429ac Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 2 Jun 2023 14:35:31 +0200 Subject: [PATCH 400/821] Polishing. Reuse comment hint retrieval. Reorder methods. Inline methods and use fluent API where possible. See #2991 Original pull request: #2995 --- .../support/SimpleJpaRepository.java | 92 +++++++++++-------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index f48d9c3bcd..e9a2d4b204 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -38,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -230,9 +231,11 @@ public void deleteAllByIdInBatch(Iterable ids) { entityInformation.getIdAttribute().getName()); Query query = em.createQuery(queryString); - /** + + /* * Some JPA providers require {@code ids} to be a {@link Collection} so we must convert if it's not already. */ + if (Collection.class.isInstance(ids)) { query.setParameter("ids", ids); } else { @@ -304,33 +307,11 @@ public Optional findById(ID id) { } LockModeType type = metadata.getLockModeType(); - - Map hints = new HashMap<>(); - - getQueryHints().withFetchGraphs(em).forEach(hints::put); - - if (metadata != null && metadata.getComment() != null && provider.getCommentHintKey() != null) { - hints.put(provider.getCommentHintKey(), provider.getCommentHintValue(metadata.getComment())); - } + Map hints = getHints(); return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints)); } - /** - * Returns {@link QueryHints} with the query hints based on the current {@link CrudMethodMetadata} and potential - * {@link EntityGraph} information. - */ - protected QueryHints getQueryHints() { - return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata); - } - - /** - * Returns {@link QueryHints} with the query hints on the current {@link CrudMethodMetadata} for count queries. - */ - protected QueryHints getQueryHintsForCount() { - return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata).forCounts(); - } - @Deprecated @Override public T getOne(ID id) { @@ -437,7 +418,7 @@ public List findAll(Sort sort) { @Override public Page findAll(Pageable pageable) { - if (isUnpaged(pageable)) { + if (pageable.isUnpaged()) { return new PageImpl<>(findAll()); } @@ -463,7 +444,7 @@ public List findAll(Specification spec) { public Page findAll(Specification spec, Pageable pageable) { TypedQuery query = getQuery(spec, pageable); - return isUnpaged(pageable) ? new PageImpl<>(query.getResultList()) + return pageable.isUnpaged() ? new PageImpl<>(query.getResultList()) : readPage(query, getDomainClass(), pageable, spec); } @@ -475,9 +456,12 @@ public List findAll(Specification spec, Sort sort) { @Override public boolean exists(Specification spec) { - CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(Integer.class); - cq.select(this.em.getCriteriaBuilder().literal(1)); + CriteriaQuery cq = this.em.getCriteriaBuilder() // + .createQuery(Integer.class) // + .select(this.em.getCriteriaBuilder().literal(1)); + applySpecificationToCriteria(spec, getDomainClass(), cq); + TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); return query.setMaxResults(1).getResultList().size() == 1; } @@ -565,9 +549,12 @@ public long count(Example example) { public boolean exists(Example example) { Specification spec = new ExampleSpecification<>(example, this.escapeCharacter); - CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery(Integer.class); - cq.select(this.em.getCriteriaBuilder().literal(1)); + CriteriaQuery cq = this.em.getCriteriaBuilder() // + .createQuery(Integer.class) // + .select(this.em.getCriteriaBuilder().literal(1)); + applySpecificationToCriteria(spec, example.getProbeType(), cq); + TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); return query.setMaxResults(1).getResultList().size() == 1; } @@ -590,7 +577,7 @@ public Page findAll(Example example, Pageable pageable) { Class probeType = example.getProbeType(); TypedQuery query = getQuery(new ExampleSpecification<>(example, escapeCharacter), probeType, pageable); - return isUnpaged(pageable) ? new PageImpl<>(query.getResultList()) : readPage(query, probeType, pageable, spec); + return pageable.isUnpaged() ? new PageImpl<>(query.getResultList()) : readPage(query, probeType, pageable, spec); } @Override @@ -804,6 +791,21 @@ protected TypedQuery getCountQuery(@Nullable Specification TypedQuery applyRepositoryMethodMetadataForCount(TypedQuery query) { @@ -878,9 +877,26 @@ private void applyQueryHintsForCount(Query query) { } getQueryHintsForCount().forEach(query::setHint); + applyComment(metadata, query::setHint); + } + + private Map getHints() { + + Map hints = new HashMap<>(); + + getQueryHints().withFetchGraphs(em).forEach(hints::put); + + if (metadata != null) { + applyComment(metadata, hints::put); + } + + return hints; + } + + private void applyComment(CrudMethodMetadata metadata, BiConsumer consumer) { if (metadata.getComment() != null && provider.getCommentHintKey() != null) { - query.setHint(provider.getCommentHintKey(), provider.getCommentHintValue(metadata.getComment())); + consumer.accept(provider.getCommentHintKey(), provider.getCommentHintValue(this.metadata.getComment())); } } @@ -903,10 +919,6 @@ private static long executeCountQuery(TypedQuery query) { return total; } - private static boolean isUnpaged(Pageable pageable) { - return pageable.isUnpaged(); - } - /** * Specification that gives access to the {@link Parameter} instance used to bind the ids for * {@link SimpleJpaRepository#findAllById(Iterable)}. Workaround for OpenJPA not binding collections to in-clauses From 8cae8cdf24503d9a5e70b7dd663a4b36cb8f7f4e Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 2 Jun 2023 13:29:09 -0500 Subject: [PATCH 401/821] Handle SIGN function properly in HQL and JPQL queries. See #2994. --- .../org/springframework/data/jpa/repository/query/Hql.g4 | 1 - .../org/springframework/data/jpa/repository/query/Jpql.g4 | 3 ++- .../data/jpa/repository/query/HqlQueryRendererTests.java | 5 +++++ .../data/jpa/repository/query/JpqlQueryRendererTests.java | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index b702bad0c1..1ca0c466b4 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -1013,7 +1013,6 @@ SEARCH : S E A R C H; SECOND : S E C O N D; SELECT : S E L E C T; SET : S E T; -SIGN : S I G N; SIZE : S I Z E; SOME : S O M E; SUBSTRING : S U B S T R I N G; diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 546f49c154..b7a636f7c3 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -602,7 +602,8 @@ identification_variable | LEFT | ORDER | OUTER - | FLOOR) + | FLOOR + | SIGN) ; constructor_name diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index c6c69d24ef..fb5a5ed923 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1516,4 +1516,9 @@ void floorShouldBeValidEntityName() { WHERE f.name = :name """); } + + @Test // GH-2994 + void queryWithSignShouldWork() { + assertQuery("select t.sign from TestEntity t"); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index 14900f8458..3a7d1598e7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -931,4 +931,9 @@ void floorShouldBeValidEntityName() { WHERE f.name = :name """); } + + @Test // GH-2994 + void queryWithSignShouldWork() { + assertQuery("select t.sign from TestEntity t"); + } } From 90de69354af5c00e3896e815a4e6fca4c6f6e4ad Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 6 Jun 2023 10:02:38 +0200 Subject: [PATCH 402/821] Use snapshot and milestone repositories instead of libs-snapshot and libs-milestone. Closes #2998 --- README.adoc | 4 ++-- pom.xml | 24 +++++++++--------------- src/main/asciidoc/preface.adoc | 5 ++--- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/README.adoc b/README.adoc index b3ef9abdf8..bf8882d648 100644 --- a/README.adoc +++ b/README.adoc @@ -119,9 +119,9 @@ If you'd rather like the latest snapshots of the upcoming major version, use our - spring-libs-snapshot + spring-snapshot Spring Snapshot Repository - https://repo.spring.io/libs-snapshot + https://repo.spring.io/snapshot ---- diff --git a/pom.xml b/pom.xml index f8e44fb758..91916af9b9 100644 --- a/pom.xml +++ b/pom.xml @@ -216,25 +216,19 @@ - spring-libs-snapshot - https://repo.spring.io/libs-snapshot + spring-snapshot + https://repo.spring.io/snapshot true + + false + + + + spring-milestone + https://repo.spring.io/milestone - - - spring-libs-milestone - https://repo.spring.io/libs-milestone - - - spring-libs-snapshot - https://repo.spring.io/libs-snapshot - - true - - - diff --git a/src/main/asciidoc/preface.adoc b/src/main/asciidoc/preface.adoc index 22a0e3e852..8b30257482 100644 --- a/src/main/asciidoc/preface.adoc +++ b/src/main/asciidoc/preface.adoc @@ -8,6 +8,5 @@ Spring Data JPA provides repository support for the Jakarta Persistence API (JPA * Version control: https://github.com/spring-projects/spring-data-jpa * Bugtracker: https://github.com/spring-projects/spring-data-jpa/issues -* Release repository: https://repo.spring.io/libs-release -* Milestone repository: https://repo.spring.io/libs-milestone -* Snapshot repository: https://repo.spring.io/libs-snapshot +* Milestone repository: https://repo.spring.io/milestone +* Snapshot repository: https://repo.spring.io/snapshot From 2e489cb4fca1b2414e489936be4d3f2a55bdde79 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 8 Jun 2023 09:47:40 +0200 Subject: [PATCH 403/821] Avoid duplicate sort orders through Keyset scrolling. We now avoid adding sort properties if they are already specified by Sort. Closes #2996 --- .../query/KeysetScrollSpecification.java | 16 +++- .../KeysetScrollSpecificationUnitTests.java | 77 +++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecificationUnitTests.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java index ee4979a637..42846ccc62 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java @@ -22,6 +22,8 @@ import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.springframework.data.domain.KeysetScrollPosition; @@ -61,11 +63,21 @@ public static Sort createSort(KeysetScrollPosition position, Sort sort, JpaEntit KeysetScrollDelegate delegate = KeysetScrollDelegate.of(position.getDirection()); + Collection sortById; Sort sortToUse; if (entity.hasCompositeId()) { - sortToUse = sort.and(Sort.by(entity.getIdAttributeNames().toArray(new String[0]))); + sortById = new ArrayList<>(entity.getIdAttributeNames()); } else { - sortToUse = sort.and(Sort.by(entity.getRequiredIdAttribute().getName())); + sortById = new ArrayList<>(1); + sortById.add(entity.getRequiredIdAttribute().getName()); + } + + sort.forEach(it -> sortById.remove(it.getProperty())); + + if (sortById.isEmpty()) { + sortToUse = sort; + } else { + sortToUse = sort.and(Sort.by(sortById.toArray(new String[0]))); } return delegate.getSortOrders(sortToUse); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecificationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecificationUnitTests.java new file mode 100644 index 0000000000..09362c5c5d --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecificationUnitTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.data.domain.ScrollPosition; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.data.jpa.domain.sample.SampleWithIdClass; +import org.springframework.data.jpa.domain.sample.User; +import org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.annotation.Transactional; + +/** + * Unit tests for {@link KeysetScrollSpecification}. + * + * @author Mark Paluch + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration({ "classpath:infrastructure.xml" }) +@Transactional +class KeysetScrollSpecificationUnitTests { + + @PersistenceContext EntityManager em; + + @Test // GH-2996 + void shouldAddIdentifierToSort() { + + Sort sort = KeysetScrollSpecification.createSort(ScrollPosition.keyset(), Sort.by("firstname"), + new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel(), + em.getEntityManagerFactory().getPersistenceUnitUtil())); + + assertThat(sort).extracting(Order::getProperty).containsExactly("firstname", "id"); + } + + @Test // GH-2996 + void shouldAddCompositeIdentifierToSort() { + + Sort sort = KeysetScrollSpecification.createSort(ScrollPosition.keyset(), Sort.by("first", "firstname"), + new JpaMetamodelEntityInformation<>(SampleWithIdClass.class, em.getMetamodel(), + em.getEntityManagerFactory().getPersistenceUnitUtil())); + + assertThat(sort).extracting(Order::getProperty).containsExactly("first", "firstname", "second"); + } + + @Test // GH-2996 + void shouldSkipExistingIdentifiersInSort() { + + Sort sort = KeysetScrollSpecification.createSort(ScrollPosition.keyset(), Sort.by("id", "firstname"), + new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel(), + em.getEntityManagerFactory().getPersistenceUnitUtil())); + + assertThat(sort).extracting(Order::getProperty).containsExactly("id", "firstname"); + } + +} From 80916d4b2539147839e1aaa253a5e96b701e5011 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 7 Jun 2023 10:55:04 -0500 Subject: [PATCH 404/821] Add section to reference docs highlighting other possibilities. Give users that have too complex of a query a list of where to go should Spring Data JPA not offer what they need in query support. See #3005 Original Pull Request: #3006 --- src/main/asciidoc/jpa.adoc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc index 723989c40f..e8178c49df 100644 --- a/src/main/asciidoc/jpa.adoc +++ b/src/main/asciidoc/jpa.adoc @@ -685,6 +685,22 @@ The escape character used can be configured by setting the `escapeCharacter` of Note that the method `escape(String)` available in the SpEL context will only escape the SQL and JPQL standard wildcards `_` and `%`. If the underlying database or the JPA implementation supports additional wildcards these will not get escaped. +[[jpa.query.other-methods]] +=== Other Methods + +Spring Data JPA offers many ways to build queries. +But sometimes, your query may simply be too complicated for the techniques offered. +In that situation, consider: + +* If you haven't already, simply write the query yourself using <>. +* If that doesn't fit your needs, consider implementing a <>. This lets you register a method in your repository while leaving the implementation completely up to you. This gives you the ability to: +** Talk directly to the `EntityManager` (writing pure HQL/JPQL/EQL/native SQL or using the *Criteria API*) +** Leverage Spring Framework's `JdbcTemplate` (native SQL) +** Use another 3rd-party database toolkit. +* Another option is putting your query inside the database and then using either Spring Data JPA's <> or if it's a database function using the <> and invoking it with a `CALL`. + +These tactics may be most effective when you need maximum control of your query, while still letting Spring Data JPA provide resource management. + [[jpa.modifying-queries]] === Modifying Queries From a9f18365d13940377b958e0ae0b9a1e847a2d696 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 23 May 2023 12:14:40 -0500 Subject: [PATCH 405/821] Test against Java 21 on CI. To make this code work with Java 21, delombok the test suite. Original Pull Request: #2993 --- Jenkinsfile | 2 +- ci/pipeline.properties | 4 +- .../data/envers/sample/AbstractEntity.java | 4 +- .../data/envers/sample/Country.java | 10 +- .../data/envers/sample/License.java | 27 ++++-- .../jpa/domain/sample/AbstractMappedType.java | 9 +- .../jpa/domain/sample/EmployeeWithName.java | 30 ++++-- .../data/jpa/domain/sample/Item.java | 33 +++++-- .../SampleWithIdClassIncludingEntity.java | 71 ++++++++++++-- .../domain/sample/UserWithOptionalField.java | 30 ++++-- .../jpa/repository/UserRepositoryTests.java | 38 ++++++-- .../MySqlStoredProcedureIntegrationTests.java | 56 +++++++++-- ...stgresStoredProcedureIntegrationTests.java | 54 +++++++++-- ...ProcedureNullHandlingIntegrationTests.java | 48 ++++++++-- .../ProjectionJoinIntegrationTests.java | 72 ++++++++++++-- .../ProjectionsIntegrationTests.java | 93 ++++++++++++++++--- ...rIndexedQueryParameterSetterUnitTests.java | 27 +++++- ...odelEntityInformationIntegrationTests.java | 49 +++++++++- ...PersistableEntityInformationUnitTests.java | 11 ++- ...QuerydslJpaPredicateExecutorUnitTests.java | 19 +++- 20 files changed, 575 insertions(+), 112 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 415311f002..3759ab7607 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -48,7 +48,7 @@ pipeline { when { beforeAgent(true) allOf { - branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP") + branch(pattern: "issue/gh-java21|main|(\\d\\.\\d\\.x)", comparator: "REGEXP") not { triggeredBy 'UpstreamCause' } } } diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 2f45263dbc..026fdcb58b 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,10 +1,10 @@ # Java versions java.main.tag=17.0.6_10-jdk-focal -java.next.tag=20-jdk-jammy +java.next.tag=21-jdk-bullseye # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} -docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag} +docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/openjdk:${java.next.tag} # Supported versions of MongoDB docker.mongodb.4.4.version=4.4.18 diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java index caf5355dec..231bc18842 100644 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/AbstractEntity.java @@ -15,15 +15,13 @@ */ package org.springframework.data.envers.sample; -import lombok.EqualsAndHashCode; - import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; @MappedSuperclass -@EqualsAndHashCode abstract class AbstractEntity { public @Id @GeneratedValue Long id; + } diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java index f974349488..932b1a99c2 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/Country.java @@ -17,11 +17,10 @@ import jakarta.persistence.Entity; -import lombok.ToString; -import org.hibernate.envers.Audited; - import java.time.Instant; +import org.hibernate.envers.Audited; + /** * Sample domain class. * @@ -31,7 +30,6 @@ */ @Audited @Entity -@ToString public class Country extends AbstractEntity { public String code; @@ -39,4 +37,8 @@ public class Country extends AbstractEntity { public Instant timestamp; public String name; + + public String toString() { + return "Country(code=" + this.code + ", timestamp=" + this.timestamp + ", name=" + this.name + ")"; + } } diff --git a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java index 32ccae2967..efae0b6e86 100755 --- a/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java +++ b/spring-data-envers/src/test/java/org/springframework/data/envers/sample/License.java @@ -15,12 +15,13 @@ */ package org.springframework.data.envers.sample; -import java.util.Set; - import jakarta.persistence.Entity; import jakarta.persistence.ManyToMany; import jakarta.persistence.Version; +import java.util.Objects; +import java.util.Set; + import org.hibernate.envers.Audited; /** @@ -32,10 +33,24 @@ @Entity public class License extends AbstractEntity { - @Version - public Integer version; + @Version public Integer version; public String name; - @ManyToMany - public Set laender; + @ManyToMany public Set laender; + + @Override + public boolean equals(Object o) { + + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + License license = (License) o; + return Objects.equals(version, license.version) && Objects.equals(name, license.name); + } + + @Override + public int hashCode() { + return Objects.hash(version, name); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java index 0cf8abfe72..2de5e01cc5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/AbstractMappedType.java @@ -15,8 +15,6 @@ */ package org.springframework.data.jpa.domain.sample; -import lombok.Getter; - import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; @@ -29,7 +27,8 @@ @MappedSuperclass public abstract class AbstractMappedType { - @Id @GeneratedValue @Getter Long id; + @Id + @GeneratedValue Long id; @Version Long version; private String attribute1; @@ -38,4 +37,8 @@ public abstract class AbstractMappedType { AbstractMappedType(String attribute1) { this.attribute1 = attribute1; } + + public Long getId() { + return this.id; + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java index ddcb19e0c5..bf0e74772e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/EmployeeWithName.java @@ -18,20 +18,16 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; -import lombok.AccessLevel; -import lombok.Data; -import lombok.NoArgsConstructor; /** * @author Greg Turnquist */ @Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@Data public class EmployeeWithName { @Id - @GeneratedValue private Integer id; + @GeneratedValue // + private Integer id; private String name; public EmployeeWithName(String name) { @@ -39,4 +35,26 @@ public EmployeeWithName(String name) { this(); this.name = name; } + + protected EmployeeWithName() {} + + public Integer getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public void setId(Integer id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public String toString() { + return "EmployeeWithName(id=" + this.getId() + ", name=" + this.getName() + ")"; + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java index 38c6b83b04..664395f9f3 100755 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Item.java @@ -21,8 +21,8 @@ import jakarta.persistence.IdClass; import jakarta.persistence.JoinColumn; import jakarta.persistence.Table; -import lombok.EqualsAndHashCode; -import lombok.ToString; + +import java.util.Objects; /** * @author Mark Paluch @@ -32,13 +32,13 @@ @Entity @Table @IdClass(ItemId.class) -@EqualsAndHashCode -@ToString public class Item { - @Id @Column(columnDefinition = "INT") private Integer id; + @Id + @Column(columnDefinition = "INT") private Integer id; - @Id @JoinColumn(name = "manufacturer_id", columnDefinition = "INT") private Integer manufacturerId; + @Id + @JoinColumn(name = "manufacturer_id", columnDefinition = "INT") private Integer manufacturerId; private String name; @@ -72,4 +72,25 @@ public void setName(String name) { this.name = name; } + @Override + public boolean equals(Object o) { + + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Item item = (Item) o; + return Objects.equals(id, item.id) && Objects.equals(manufacturerId, item.manufacturerId) + && Objects.equals(name, item.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, manufacturerId, name); + } + + public String toString() { + return "Item(id=" + this.getId() + ", manufacturerId=" + this.getManufacturerId() + ", name=" + this.getName() + + ")"; + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java index 4dc08b50cf..0d28b4cfd9 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/SampleWithIdClassIncludingEntity.java @@ -1,14 +1,12 @@ package org.springframework.data.jpa.domain.sample; -import lombok.Data; - -import java.io.Serializable; - import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.IdClass; import jakarta.persistence.ManyToOne; +import java.io.Serializable; + /** * Sample class for integration testing * {@link org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation}. @@ -17,24 +15,81 @@ */ @Entity @IdClass(SampleWithIdClassIncludingEntity.SampleWithIdClassPK.class) -@Data public class SampleWithIdClassIncludingEntity { @Id Long first; - @ManyToOne @Id OtherEntity second; + @ManyToOne + @Id OtherEntity second; + + public SampleWithIdClassIncludingEntity() {} + + public Long getFirst() { + return this.first; + } + + public OtherEntity getSecond() { + return this.second; + } + + public void setFirst(Long first) { + this.first = first; + } + + public void setSecond(OtherEntity second) { + this.second = second; + } + + public String toString() { + return "SampleWithIdClassIncludingEntity(first=" + this.getFirst() + ", second=" + this.getSecond() + ")"; + } - @Data @SuppressWarnings("serial") public static class SampleWithIdClassPK implements Serializable { Long first; Long second; + + public SampleWithIdClassPK() {} + + public Long getFirst() { + return this.first; + } + + public Long getSecond() { + return this.second; + } + + public void setFirst(Long first) { + this.first = first; + } + + public void setSecond(Long second) { + this.second = second; + } + + public String toString() { + return "SampleWithIdClassIncludingEntity.SampleWithIdClassPK(first=" + this.getFirst() + ", second=" + + this.getSecond() + ")"; + } } @Entity - @Data public static class OtherEntity { @Id Long otherId; + + public OtherEntity() {} + + public Long getOtherId() { + return this.otherId; + } + + public void setOtherId(Long otherId) { + this.otherId = otherId; + } + + public String toString() { + return "SampleWithIdClassIncludingEntity.OtherEntity(otherId=" + this.getOtherId() + ")"; + } } /** diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java index f38eeae33f..9cc8d67b15 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java @@ -15,24 +15,22 @@ */ package org.springframework.data.jpa.domain.sample; -import lombok.Data; - -import java.util.Optional; - import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import java.util.Optional; + import org.springframework.lang.Nullable; /** * @author Greg Turnquist */ @Entity -@Data public class UserWithOptionalField { - @Id @GeneratedValue private Long id; + @Id + @GeneratedValue private Long id; private String name; private String role; @@ -57,4 +55,24 @@ public Optional getRole() { public void setRole(Optional role) { this.role = role.orElse(null); } + + public Long getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public String toString() { + return "UserWithOptionalField(id=" + this.getId() + ", name=" + this.getName() + ", role=" + this.getRole() + ")"; + } } 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 844e2ec9fa..c8404af3c6 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 @@ -31,7 +31,6 @@ import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; -import lombok.Data; import java.util.ArrayList; import java.util.Arrays; @@ -56,8 +55,6 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.*; -import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; -import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; @@ -1300,9 +1297,8 @@ void scrollByExampleKeysetBackward() { q -> q.limit(4).sortBy(Sort.by("firstname", "emailAddress")).scroll(ScrollPosition.keyset())); KeysetScrollPosition scrollPosition = (KeysetScrollPosition) firstWindow.positionAt(2); - Window previousWindow = repository.findBy(example, - q -> q.limit(1).sortBy(Sort.by("firstname", "emailAddress")) - .scroll(ScrollPosition.backward(scrollPosition.getKeys()))); + Window previousWindow = repository.findBy(example, q -> q.limit(1) + .sortBy(Sort.by("firstname", "emailAddress")).scroll(ScrollPosition.backward(scrollPosition.getKeys()))); assertThat(previousWindow).containsOnly(jane2); assertThat(previousWindow.hasNext()).isTrue(); @@ -2477,9 +2473,22 @@ void findByFluentExampleWithSortedInterfaceBasedProjection() { @Test // GH-2294 void fluentExamplesWithClassBasedDtosNotYetSupported() { - @Data class UserDto { String firstname; + + public UserDto() {} + + public String getFirstname() { + return this.firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String toString() { + return "UserDto(firstname=" + this.getFirstname() + ")"; + } } assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> { @@ -2709,9 +2718,22 @@ void findByFluentSpecificationWithSortedInterfaceBasedProjection() { @Test // GH-2274 void fluentSpecificationWithClassBasedDtosNotYetSupported() { - @Data class UserDto { String firstname; + + public UserDto() {} + + public String getFirstname() { + return this.firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String toString() { + return "UserDto(firstname=" + this.getFirstname() + ")"; + } } assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java index cf4ce08c03..025e3424bc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/MySqlStoredProcedureIntegrationTests.java @@ -16,18 +16,16 @@ package org.springframework.data.jpa.repository.procedures; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import jakarta.persistence.Entity; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.NamedStoredProcedureQuery; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; import java.util.List; +import java.util.Objects; import java.util.Properties; import javax.sql.DataSource; @@ -146,17 +144,59 @@ void testEntityListFromNamedProcedure() { new Employee(4, "Gabriel")); } - @Data @Entity - @AllArgsConstructor - @NoArgsConstructor @NamedStoredProcedureQuery(name = "get_employees_mysql", procedureName = "get_employees", resultClasses = Employee.class) public static class Employee { @Id - @GeneratedValue private Integer id; + @GeneratedValue // + private Integer id; private String name; + + public Employee(Integer id, String name) { + + this.id = id; + this.name = name; + } + + public Employee() {} + + public Integer getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public void setId(Integer id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Employee employee = (Employee) o; + return Objects.equals(id, employee.id) && Objects.equals(name, employee.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + public String toString() { + return "MySqlStoredProcedureIntegrationTests.Employee(id=" + this.getId() + ", name=" + this.getName() + ")"; + } } @Transactional diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java index 96c0f5e710..4b2a9c39dc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureIntegrationTests.java @@ -25,12 +25,10 @@ import jakarta.persistence.NamedStoredProcedureQuery; import jakarta.persistence.ParameterMode; import jakarta.persistence.StoredProcedureParameter; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; import java.math.BigDecimal; import java.util.List; +import java.util.Objects; import java.util.Properties; import javax.sql.DataSource; @@ -152,10 +150,7 @@ void testEntityListFromNamedProcedure() { new Employee(4, "Gabriel")); } - @Data @Entity - @AllArgsConstructor - @NoArgsConstructor @NamedStoredProcedureQuery( // name = "get_employees_postgres", // procedureName = "get_employees", // @@ -164,8 +159,53 @@ void testEntityListFromNamedProcedure() { public static class Employee { @Id - @GeneratedValue private Integer id; + @GeneratedValue // + private Integer id; private String name; + + public Employee(Integer id, String name) { + + this.id = id; + this.name = name; + } + + public Employee() {} + + public Integer getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public void setId(Integer id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Employee employee = (Employee) o; + return Objects.equals(id, employee.id) && Objects.equals(name, employee.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + public String toString() { + return "PostgresStoredProcedureIntegrationTests.Employee(id=" + this.getId() + ", name=" + this.getName() + ")"; + } } @Transactional diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java index bf87de2be4..1739dbed9e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/procedures/PostgresStoredProcedureNullHandlingIntegrationTests.java @@ -20,10 +20,6 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; import java.util.Date; import java.util.Properties; @@ -81,16 +77,52 @@ void invokingNullOnTemporalStoredProcedureParameterShouldWork() { repository.countLocalDate(null); } - @Data - @AllArgsConstructor - @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity class TestModel { @Id - @GeneratedValue(strategy = GenerationType.AUTO) private long id; + @GeneratedValue(strategy = GenerationType.AUTO) // + private long id; private UUID uuid; private Date date; + + public TestModel(long id, UUID uuid, Date date) { + + this.id = id; + this.uuid = uuid; + this.date = date; + } + + protected TestModel() {} + + public long getId() { + return this.id; + } + + public UUID getUuid() { + return this.uuid; + } + + public Date getDate() { + return this.date; + } + + public void setId(long id) { + this.id = id; + } + + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + public void setDate(Date date) { + this.date = date; + } + + public String toString() { + return "PostgresStoredProcedureNullHandlingIntegrationTests.TestModel(id=" + this.getId() + ", uuid=" + + this.getUuid() + ", date=" + this.getDate() + ")"; + } } @Transactional diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java index d100cb3f0f..08be64dd89 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionJoinIntegrationTests.java @@ -17,8 +17,6 @@ import static org.assertj.core.api.Assertions.*; -import lombok.Data; - import jakarta.persistence.Access; import jakarta.persistence.AccessType; import jakarta.persistence.CascadeType; @@ -31,7 +29,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.test.context.ContextConfiguration; @@ -59,7 +56,6 @@ void findByIdPerformsAnOuterJoin() { assertThat(projection.getAddress()).isNull(); } - @Data public static class UserProjection { private final int id; @@ -69,6 +65,19 @@ public UserProjection(int id, Address address) { this.id = id; this.address = address; } + + public int getId() { + return this.id; + } + + public Address getAddress() { + return this.address; + } + + public String toString() { + return "ProjectionJoinIntegrationTests.UserProjection(id=" + this.getId() + ", address=" + this.getAddress() + + ")"; + } } public interface UserRepository extends CrudRepository { @@ -76,21 +85,68 @@ public interface UserRepository extends CrudRepository { T findById(int id, Class projectionClass); } - @Data @Table(name = "ProjectionJoinIntegrationTests_User") @Entity static class User { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Access(value = AccessType.PROPERTY) int id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Access(value = AccessType.PROPERTY) int id; @OneToOne(cascade = CascadeType.ALL) Address address; + + public User() {} + + public int getId() { + return this.id; + } + + public Address getAddress() { + return this.address; + } + + public void setId(int id) { + this.id = id; + } + + public void setAddress(Address address) { + this.address = address; + } + + public String toString() { + return "ProjectionJoinIntegrationTests.User(id=" + this.getId() + ", address=" + this.getAddress() + ")"; + } } - @Data @Table(name = "ProjectionJoinIntegrationTests_Address") @Entity static class Address { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Access(value = AccessType.PROPERTY) int id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Access(value = AccessType.PROPERTY) // + int id; String streetName; + + public Address() {} + + public int getId() { + return this.id; + } + + public String getStreetName() { + return this.streetName; + } + + public void setId(int id) { + this.id = id; + } + + public void setStreetName(String streetName) { + this.streetName = streetName; + } + + public String toString() { + return "ProjectionJoinIntegrationTests.Address(id=" + this.getId() + ", streetName=" + this.getStreetName() + ")"; + } } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java index 0572cef1cf..66b4f99267 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/projections/ProjectionsIntegrationTests.java @@ -17,12 +17,6 @@ import static org.assertj.core.api.Assertions.*; -import lombok.Data; - -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.EntityManagerFactory; @@ -31,12 +25,16 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + import javax.sql.DataSource; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.data.jpa.repository.JpaRepository; @@ -94,28 +92,99 @@ private SubEntity createSubEntity(int index) { return entity; } - @Data @Entity(name = "Dummy") @Table(name = "DummyEntity") static class DummyEntityWithCollection { - @GeneratedValue @Id Long id; + @GeneratedValue + @Id Long id; String name; - @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "subs") List subs = new ArrayList<>(); + @OneToMany(cascade = CascadeType.ALL) + @JoinColumn(name = "subs") List subs = new ArrayList<>(); String otherAttribute; + + public DummyEntityWithCollection() {} + + public Long getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public List getSubs() { + return this.subs; + } + + public String getOtherAttribute() { + return this.otherAttribute; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setSubs(List subs) { + this.subs = subs; + } + + public void setOtherAttribute(String otherAttribute) { + this.otherAttribute = otherAttribute; + } + + public String toString() { + return "ProjectionsIntegrationTests.DummyEntityWithCollection(id=" + this.getId() + ", name=" + this.getName() + + ", subs=" + this.getSubs() + ", otherAttribute=" + this.getOtherAttribute() + ")"; + } } - @Data @Entity @Table(name = "SubEntity") static class SubEntity { - @GeneratedValue @Id Long id; + @GeneratedValue + @Id Long id; String name; String otherAttribute; + + public SubEntity() {} + + public Long getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public String getOtherAttribute() { + return this.otherAttribute; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setOtherAttribute(String otherAttribute) { + this.otherAttribute = otherAttribute; + } + + public String toString() { + return "ProjectionsIntegrationTests.SubEntity(id=" + this.getId() + ", name=" + this.getName() + + ", otherAttribute=" + this.getOtherAttribute() + ")"; + } } interface DummyEntityProjection { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java index 171322146e..47a063bad4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedOrIndexedQueryParameterSetterUnitTests.java @@ -25,7 +25,6 @@ import jakarta.persistence.Query; import jakarta.persistence.TemporalType; import jakarta.persistence.criteria.ParameterExpression; -import lombok.Value; import java.util.Arrays; import java.util.Collections; @@ -216,15 +215,33 @@ private static Query mockExceptionThrowingQueryWithNamedParameters() { return query; } - @Value - private static class ParameterImpl implements Parameter { + private static final class ParameterImpl implements Parameter { - String name; - Integer position; + private final String name; + private final Integer position; + + public ParameterImpl(String name, Integer position) { + + this.name = name; + this.position = position; + } @Override public Class getParameterType() { return Object.class; } + + public String getName() { + return this.name; + } + + public Integer getPosition() { + return this.position; + } + + public String toString() { + return "NamedOrIndexedQueryParameterSetterUnitTests.ParameterImpl(name=" + this.getName() + ", position=" + + this.getPosition() + ")"; + } } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java index 1545a21ae8..b8b2ea739f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformationIntegrationTests.java @@ -19,7 +19,6 @@ import static org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.*; import jakarta.persistence.*; -import lombok.Data; import java.io.Serializable; import java.sql.Timestamp; @@ -359,18 +358,62 @@ public static class EntityWithIdClass { @Id String id2; } - @Data public static class EntityWithIdClassPK implements Serializable { String id1; String id2; + + public EntityWithIdClassPK() {} + + public String getId1() { + return this.id1; + } + + public String getId2() { + return this.id2; + } + + public void setId1(String id1) { + this.id1 = id1; + } + + public void setId2(String id2) { + this.id2 = id2; + } + + public String toString() { + return "JpaMetamodelEntityInformationIntegrationTests.EntityWithIdClassPK(id1=" + this.getId1() + ", id2=" + + this.getId2() + ")"; + } } - @Data public static class EntityWithNestedIdClassPK implements Serializable { Long id; EntityWithIdClassPK reference; + + public EntityWithNestedIdClassPK() {} + + public Long getId() { + return this.id; + } + + public EntityWithIdClassPK getReference() { + return this.reference; + } + + public void setId(Long id) { + this.id = id; + } + + public void setReference(EntityWithIdClassPK reference) { + this.reference = reference; + } + + public String toString() { + return "JpaMetamodelEntityInformationIntegrationTests.EntityWithNestedIdClassPK(id=" + this.getId() + + ", reference=" + this.getReference() + ")"; + } } @Entity diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java index db6684898b..9d7f93c3ee 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformationUnitTests.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; @@ -24,7 +24,6 @@ import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.Metamodel; import jakarta.persistence.metamodel.Type; -import lombok.Getter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -87,11 +86,15 @@ void usesPersistableMethodsForIsNewAndGetId() { @SuppressWarnings("serial") class Foo implements Persistable { - @Getter Long id; + Long id; @Override public boolean isNew() { return id != null; } + + public Long getId() { + return this.id; + } } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java index a0eb72430a..00df593ef8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java @@ -15,12 +15,10 @@ */ package org.springframework.data.jpa.repository.support; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.*; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; -import lombok.Data; import java.sql.Date; import java.time.LocalDate; @@ -440,9 +438,22 @@ void existsByFluentPredicate() { @Test // GH-2294 void fluentExamplesWithClassBasedDtosNotYetSupported() { - @Data class UserDto { String firstname; + + public UserDto() {} + + public String getFirstname() { + return this.firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String toString() { + return "UserDto(firstname=" + this.getFirstname() + ")"; + } } assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> predicateExecutor From ec44947b35f68b77ebb2a6a73b7baa0b663121ec Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 22 May 2023 16:34:21 -0500 Subject: [PATCH 406/821] Properly handle Sort's that start with a join alias. JOIN clauses can have aliases as well, despite not using an AS reserved word. The HQL query parser needs to handle this. See #2960, #1066, #664 Original Pull Request: 2967 --- .../repository/query/HqlQueryTransformer.java | 24 +++++++++++++++++++ .../query/JpaQueryTransformerSupport.java | 7 +++++- .../query/JpqlQueryTransformer.java | 10 ++++++++ .../query/HqlQueryTransformerTests.java | 24 +++++++++++++++++-- .../query/JpqlQueryTransformerTests.java | 24 +++++++++++++++++-- 5 files changed, 84 insertions(+), 5 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java index 6afba0f1f7..7d6be749dc 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryTransformer.java @@ -282,6 +282,30 @@ public List visitJoin(HqlParser.JoinContext ctx) { return tokens; } + @Override + public List visitJoinPath(HqlParser.JoinPathContext ctx) { + + List tokens = super.visitJoinPath(ctx); + + if (ctx.variable() != null) { + transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + } + + return tokens; + } + + @Override + public List visitJoinSubquery(HqlParser.JoinSubqueryContext ctx) { + + List tokens = super.visitJoinSubquery(ctx); + + if (ctx.variable() != null) { + transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + } + + return tokens; + } + @Override public List visitAlias(HqlParser.AliasContext ctx) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java index 20b6fe920e..bce349e871 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java @@ -131,11 +131,16 @@ private boolean shouldPrefixWithAlias(Sort.Order order, String primaryFromAlias) return false; } - // If the Sort references an alias + // If the Sort references an alias directly if (projectionAliases.contains(order.getProperty())) { return false; } + // If the Sort property starts with an alias + if (projectionAliases.stream().anyMatch(alias -> order.getProperty().startsWith(alias))) { + return false; + } + return true; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java index a89abcb687..a9aeec36af 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformer.java @@ -219,6 +219,16 @@ public List visitRange_variable_declaration(JpqlParser.Ran return tokens; } + @Override + public List visitJoin(JpqlParser.JoinContext ctx) { + + List tokens = super.visitJoin(ctx); + + transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + + return tokens; + } + @Override public List visitConstructor_expression(JpqlParser.Constructor_expressionContext ctx) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index e11dec1ebc..460d8f456f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -245,12 +245,12 @@ AND LOWER(COALESCE(vehicle.make, '')) LIKE :query) """)).isEqualTo("o"); } - @Test // DATAJPA-252 + @Test // DATAJPA-252, GH-664, GH-1066, GH-2960 void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { String query = "select p from Person p left join p.address address"; Sort sort = Sort.by("address.city"); - assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by address.city asc"); } @Test // DATAJPA-252 @@ -986,6 +986,26 @@ select max(id), col assertThat(createQueryFor(query, Sort.unsorted())).isEqualToIgnoringWhitespace(query); } + @Test // GH-664, GH-1066, GH-2960 + void sortingRecognizesJoinAliases() { + + String query = "select p from Customer c join c.productOrder p where p.delayed = true"; + + assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("lastName")))).isEqualToIgnoringWhitespace(""" + select p from Customer c + join c.productOrder p + where p.delayed = true + order by c.lastName desc + """); + + assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("p.lineItems")))).isEqualToIgnoringWhitespace(""" + select p from Customer c + join c.productOrder p + where p.delayed = true + order by p.lineItems desc + """); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java index bd2b6a4696..2559c37edd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryTransformerTests.java @@ -235,12 +235,12 @@ AND LOWER(COALESCE(vehicle.make, '')) LIKE :query) """)).isEqualTo("o"); } - @Test // DATAJPA-252 + @Test // DATAJPA-252, GH-664, GH-1066, GH-2960 void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { String query = "select p from Person p left join p.address address"; Sort sort = Sort.by("address.city"); - assertThat(createQueryFor(query, sort)).endsWith("order by p.address.city asc"); + assertThat(createQueryFor(query, sort)).endsWith("order by address.city asc"); } @Test // DATAJPA-252 @@ -742,6 +742,26 @@ where exists ( """, relationshipName, joinAlias, joinAlias)); } + @Test // GH-664, GH-1066, GH-2960 + void sortingRecognizesJoinAliases() { + + String query = "select p from Customer c join c.productOrder p where p.delayed = true"; + + assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("lastName")))).isEqualToIgnoringWhitespace(""" + select p from Customer c + join c.productOrder p + where p.delayed = true + order by c.lastName desc + """); + + assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("p.lineItems")))).isEqualToIgnoringWhitespace(""" + select p from Customer c + join c.productOrder p + where p.delayed = true + order by p.lineItems desc + """); + } + static Stream queriesWithReservedWordsAsIdentifiers() { return Stream.of( // From be58b75d93d03702e35e498d9311b3e8b6ebe5c8 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 13 Jun 2023 08:54:57 +0200 Subject: [PATCH 407/821] Upgrade to Maven Wrapper 3.9.2. See #3019 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index b54a804bd3..929909e554 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Thu Apr 06 16:16:24 CEST 2023 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip +#Tue Jun 13 08:54:57 CEST 2023 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip From de9301886e1352965d7110183ba702cd05a96ca0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 13 Jun 2023 09:25:51 +0200 Subject: [PATCH 408/821] Retain scroll direction in Keyset position function of the correct item. Closes #2999 --- .../jpa/repository/query/ScrollDelegate.java | 6 ++--- .../jpa/repository/UserRepositoryTests.java | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java index 10681ac698..eb244a0209 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java @@ -80,14 +80,14 @@ private static Window createWindow(Sort sort, int limit, Direction direct JpaEntityInformation entity, List result) { KeysetScrollDelegate delegate = KeysetScrollDelegate.of(direction); - List resultsToUse = delegate.postProcessResults(result); + List resultsToUse = delegate.getResultWindow(delegate.postProcessResults(result), limit); IntFunction positionFunction = value -> { - T object = result.get(value); + T object = resultsToUse.get(value); Map keys = entity.getKeyset(sort.stream().map(Order::getProperty).toList(), object); - return ScrollPosition.forward(keys); + return ScrollPosition.of(keys, direction); }; return Window.from(delegate.getResultWindow(resultsToUse, limit), positionFunction, hasMoreElements(result, limit)); 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 c8404af3c6..0874aaaea0 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 @@ -57,6 +57,7 @@ import org.springframework.data.domain.*; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; +import org.springframework.data.domain.ExampleMatcher.*; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.sample.Address; import org.springframework.data.jpa.domain.sample.QUser; @@ -1304,6 +1305,31 @@ void scrollByExampleKeysetBackward() { assertThat(previousWindow.hasNext()).isTrue(); } + @Test // GH-2999 + void scrollInitiallyByExampleKeysetBackward() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Example example = Example.of(new User("J", null, null), + matching().withMatcher("firstname", GenericPropertyMatcher::startsWith).withIgnorePaths("age", "createdAt", + "dateOfBirth")); + + Window firstWindow = repository.findBy(example, + q -> q.limit(2).sortBy(Sort.by("firstname", "emailAddress")).scroll(ScrollPosition.keyset().backward())); + + assertThat(firstWindow).containsExactly(john1, john2); + + Window previousWindow = repository.findBy(example, + q -> q.limit(2).sortBy(Sort.by("firstname", "emailAddress")).scroll(firstWindow.positionAt(0))); + + assertThat(previousWindow).containsExactly(jane1, jane2); + } + @Test // GH-2878 void scrollByPredicateOffset() { From a4c1d4ea3000933f88e05b8fe4108865ba625815 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 13 Jun 2023 09:28:45 +0200 Subject: [PATCH 409/821] Polishing. Move spring-instrument into test dependencies to eagerly resolve the dependency instead of using plugin-repositories. See #2998 --- pom.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 91916af9b9..65312f53bb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -116,6 +117,14 @@ + + + org.springframework + spring-instrument + ${spring} + test + + From 6699263152406a58bcaab48085c9b4b47704e876 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 13 Jun 2023 10:10:17 +0200 Subject: [PATCH 410/821] Compile tests with parameter names. Disable Eclipselink stored procedure tests as they infer stored procedure variable names from the method calls. Closes #3020 --- pom.xml | 16 --------------- spring-data-jpa/pom.xml | 18 +---------------- ...seLinkStoredProcedureIntegrationTests.java | 20 +++++++++++++++++++ 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/pom.xml b/pom.xml index 65312f53bb..340ef8999b 100644 --- a/pom.xml +++ b/pom.xml @@ -204,22 +204,6 @@ - - maven-compiler-plugin - - - java-test-compile - test-compile - - testCompile - - - false - - - - - diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 7145881fb0..b21b03c313 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -162,7 +162,7 @@ - + ${hibernate.groupId}.orm hibernate-jpamodelgen @@ -379,22 +379,6 @@ - - maven-compiler-plugin - - - java-test-compile - test-compile - - testCompile - - - false - - - - - org.codehaus.mojo aspectj-maven-plugin diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java index 94dca00327..4af04742b6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/EclipseLinkStoredProcedureIntegrationTests.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.springframework.context.annotation.ImportResource; import org.springframework.test.context.ContextConfiguration; @@ -29,4 +31,22 @@ class EclipseLinkStoredProcedureIntegrationTests extends StoredProcedureIntegrat @ImportResource({ "classpath:infrastructure.xml", "classpath:eclipselink.xml" }) static class TestConfig extends Config {} + + @Override + @Test + @Disabled("EclipseLink parameter name inference breaks the method calls") + void shouldExecuteAdHocProcedureWith1InputAnd1OutputParameter() { + } + + @Override + @Test + @Disabled("EclipseLink parameter name inference breaks the method calls") + void shouldExecuteAdHocProcedureWith1InputAndNoOutputParameter() { + } + + @Override + @Test + @Disabled("EclipseLink parameter name inference breaks the method calls") + void shouldExecuteAdHocProcedureWith1InputAnd1OutputParameterWithUpdate() { + } } From 29e16a145da354ed2fb4a2b51603baf0fa28945a Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 14 Jun 2023 11:58:33 -0500 Subject: [PATCH 411/821] Properly handle CAST functions on HQL. See #3024 --- .../data/jpa/repository/query/Hql.g4 | 11 +++++- .../repository/query/HqlQueryRenderer.java | 37 +++++++++++++++++-- .../query/HqlQueryRendererTests.java | 5 +++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 1ca0c466b4..679c6293bc 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -492,9 +492,18 @@ frameEnd // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-functions castFunction - : CAST '(' expression AS identifier ')' + : CAST '(' expression AS castTarget ')' ; +castTarget + : castTargetType ('(' INTEGER_LITERAL (',' INTEGER_LITERAL)? ')')? + ; + +castTargetType + returns [String fullTargetName] + : (i=identifier { $fullTargetName = _localctx.i.getText(); }) ('.' c=identifier { $fullTargetName += ("." + _localctx.c.getText() ); })* + ; + extractFunction : EXTRACT '(' expression FROM expression ')' | dateTimeFunction '(' expression ')' diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 17458b20e6..3ad3d4e24d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -106,7 +106,7 @@ public List visitCte(HqlParser.CteContext ctx) { if (ctx.MATERIALIZED() != null) { tokens.add(TOKEN_MATERIALIZED); } - + tokens.add(TOKEN_OPEN_PAREN); tokens.addAll(visit(ctx.queryExpression())); tokens.add(TOKEN_CLOSE_PAREN); @@ -1790,16 +1790,47 @@ public List visitCastFunction(HqlParser.CastFunctionContex List tokens = new ArrayList<>(); - tokens.add(new JpaQueryParsingToken(ctx.CAST())); + tokens.add(new JpaQueryParsingToken(ctx.CAST(), false)); tokens.add(TOKEN_OPEN_PAREN); tokens.addAll(visit(ctx.expression())); tokens.add(new JpaQueryParsingToken(ctx.AS())); - tokens.addAll(visit(ctx.identifier())); + tokens.addAll(visit(ctx.castTarget())); + NOSPACE(tokens); tokens.add(TOKEN_CLOSE_PAREN); return tokens; } + @Override + public List visitCastTarget(HqlParser.CastTargetContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.castTargetType())); + + if (ctx.INTEGER_LITERAL() != null && !ctx.INTEGER_LITERAL().isEmpty()) { + + tokens.add(TOKEN_OPEN_PAREN); + + ctx.INTEGER_LITERAL().forEach(terminalNode -> { + + tokens.add(new JpaQueryParsingToken(terminalNode)); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + NOSPACE(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitCastTargetType(HqlParser.CastTargetTypeContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.fullTargetName)); + } + @Override public List visitExtractFunction(HqlParser.ExtractFunctionContext ctx) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index fb5a5ed923..a1e62a7e7a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1521,4 +1521,9 @@ void floorShouldBeValidEntityName() { void queryWithSignShouldWork() { assertQuery("select t.sign from TestEntity t"); } + + @Test // GH-3024 + void castFunctionWithFqdnShouldWork() { + assertQuery("SELECT o FROM Order o WHERE CAST(:userId AS java.util.UUID) IS NULL OR o.user.id = :userId"); + } } From a37e01d0f6233255b1548b2b119611347d730c09 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 14 Jun 2023 14:20:57 -0500 Subject: [PATCH 412/821] Handle dateTimeField property with HQL. Hibernate supports varisous duration literals. They also support a couple formats for binary literals. See #3025 --- .../data/jpa/repository/query/Hql.g4 | 29 ++++++- .../repository/query/HqlQueryRenderer.java | 76 +++++++++++++++++++ .../query/JpaQueryParsingToken.java | 1 + .../query/HqlQueryRendererTests.java | 13 ++++ 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 679c6293bc..e5ad3cfe82 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -299,6 +299,7 @@ literal | stringLiteral | numericLiteral | dateTimeLiteral + | binaryLiteral ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-boolean-literals @@ -337,10 +338,24 @@ dateTimeLiteral ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-duration-literals -// TBD +datetimeField + : YEAR + | MONTH + | DAY + | WEEK + | QUARTER + | HOUR + | MINUTE + | SECOND + | NANOSECOND + | EPOCH + ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-binary-literals -// TBD +binaryLiteral + : BINARY_LITERAL + | '{' HEXLITERAL (',' HEXLITERAL)* '}' + ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-enum-literals // TBD @@ -362,6 +377,8 @@ expression | primaryExpression # PlainPrimaryExpression | op=('+' | '-') numericLiteral # SignedNumericLiteral | op=('+' | '-') expression # SignedExpression + | expression datetimeField # ToDurationExpression + | expression BY datetimeField # FromDurationExpression | expression op=('*' | '/') expression # MultiplicationExpression | expression op=('+' | '-') expression # AdditionExpression | expression '||' expression # HqlConcatenationExpression @@ -607,7 +624,6 @@ inList ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-exists-predicate -// TBD existsExpression : EXISTS (ELEMENTS | INDICES) '(' simplePath ')' | EXISTS expression @@ -1058,13 +1074,18 @@ YEAR : Y E A R; fragment INTEGER_NUMBER : ('0' .. '9')+ ; fragment FLOAT_NUMBER : INTEGER_NUMBER+ '.'? INTEGER_NUMBER* (E [+-]? INTEGER_NUMBER)? ; +fragment HEX_DIGIT : [0-9a-fA-F]; + CHARACTER : '\'' (~ ('\'' | '\\' )) '\'' ; STRINGLITERAL : '\'' ('\'' '\'' | ~('\'' | '\\'))* '\'' ; JAVASTRINGLITERAL : '"' ( ('\\' [btnfr"']) | ~('"'))* '"'; INTEGER_LITERAL : INTEGER_NUMBER (L | B I)? ; FLOAT_LITERAL : FLOAT_NUMBER (D | F | B D)?; -HEXLITERAL : '0' X ('0' .. '9' | A | B | C | D | E)+ ; +HEXLITERAL : '0' X HEX_DIGIT+ ; +BINARY_LITERAL : [xX] '\'' HEX_DIGIT+ '\'' + | [xX] '"' HEX_DIGIT+ '"' + ; IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 3ad3d4e24d..0affea51af 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -1047,6 +1047,8 @@ public List visitLiteral(HqlParser.LiteralContext ctx) { return visit(ctx.numericLiteral()); } else if (ctx.dateTimeLiteral() != null) { return visit(ctx.dateTimeLiteral()); + } else if (ctx.binaryLiteral() != null) { + return visit(ctx.binaryLiteral()); } else { return List.of(); } @@ -1135,6 +1137,56 @@ public List visitDateTimeLiteral(HqlParser.DateTimeLiteral return tokens; } + @Override + public List visitDatetimeField(HqlParser.DatetimeFieldContext ctx) { + + if (ctx.YEAR() != null) { + return List.of(new JpaQueryParsingToken(ctx.YEAR())); + } else if (ctx.MONTH() != null) { + return List.of(new JpaQueryParsingToken(ctx.MONTH())); + } else if (ctx.DAY() != null) { + return List.of(new JpaQueryParsingToken(ctx.DAY())); + } else if (ctx.WEEK() != null) { + return List.of(new JpaQueryParsingToken(ctx.WEEK())); + } else if (ctx.QUARTER() != null) { + return List.of(new JpaQueryParsingToken(ctx.QUARTER())); + } else if (ctx.HOUR() != null) { + return List.of(new JpaQueryParsingToken(ctx.HOUR())); + } else if (ctx.MINUTE() != null) { + return List.of(new JpaQueryParsingToken(ctx.MINUTE())); + } else if (ctx.SECOND() != null) { + return List.of(new JpaQueryParsingToken(ctx.SECOND())); + } else if (ctx.NANOSECOND() != null) { + return List.of(new JpaQueryParsingToken(ctx.NANOSECOND())); + } else if (ctx.EPOCH() != null) { + return List.of(new JpaQueryParsingToken(ctx.EPOCH())); + } else { + return List.of(); + } + } + + @Override + public List visitBinaryLiteral(HqlParser.BinaryLiteralContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.BINARY_LITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.BINARY_LITERAL())); + } else if (ctx.HEXLITERAL() != null) { + + tokens.add(TOKEN_OPEN_BRACE); + ctx.HEXLITERAL().forEach(terminalNode -> { + tokens.add(new JpaQueryParsingToken(terminalNode)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + tokens.add(TOKEN_CLOSE_BRACE); + } + + return tokens; + } + @Override public List visitPlainPrimaryExpression(HqlParser.PlainPrimaryExpressionContext ctx) { return visit(ctx.primaryExpression()); @@ -1177,6 +1229,7 @@ public List visitGroupedExpression(HqlParser.GroupedExpres tokens.add(TOKEN_OPEN_PAREN); tokens.addAll(visit(ctx.expression())); + NOSPACE(tokens); tokens.add(TOKEN_CLOSE_PAREN); return tokens; @@ -1242,6 +1295,29 @@ public List visitSignedExpression(HqlParser.SignedExpressi return tokens; } + @Override + public List visitToDurationExpression(HqlParser.ToDurationExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression())); + tokens.addAll(visit(ctx.datetimeField())); + + return tokens; + } + + @Override + public List visitFromDurationExpression(HqlParser.FromDurationExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.expression())); + tokens.add(new JpaQueryParsingToken(ctx.BY())); + tokens.addAll(visit(ctx.datetimeField())); + + return tokens; + } + @Override public List visitCaseExpression(HqlParser.CaseExpressionContext ctx) { return visit(ctx.caseList()); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java index 59c409e9e3..00717de3d3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java @@ -48,6 +48,7 @@ class JpaQueryParsingToken { public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET = new JpaQueryParsingToken("]"); public static final JpaQueryParsingToken TOKEN_COLON = new JpaQueryParsingToken(":", false); public static final JpaQueryParsingToken TOKEN_QUESTION_MARK = new JpaQueryParsingToken("?", false); + public static final JpaQueryParsingToken TOKEN_OPEN_BRACE = new JpaQueryParsingToken("{", false); public static final JpaQueryParsingToken TOKEN_CLOSE_BRACE = new JpaQueryParsingToken("}"); public static final JpaQueryParsingToken TOKEN_CLOSE_SQUARE_BRACKET_BRACE = new JpaQueryParsingToken("]}"); public static final JpaQueryParsingToken TOKEN_CLOSE_PAREN_BRACE = new JpaQueryParsingToken(")}"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index a1e62a7e7a..f37e341adc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1526,4 +1526,17 @@ void queryWithSignShouldWork() { void castFunctionWithFqdnShouldWork() { assertQuery("SELECT o FROM Order o WHERE CAST(:userId AS java.util.UUID) IS NULL OR o.user.id = :userId"); } + + @Test // GH-3025 + void durationLiteralsShouldWork() { + assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE (ce.endDate - ce.startDate) > 5 MINUTE"); + } + + @Test // GH-3025 + void binaryLiteralsShouldWork() { + + assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.value = {0xDE, 0xAD, 0xBE, 0xEF}"); + assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.value = X'DEADBEEF'"); + assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.value = x'deadbeef'"); + } } From 3409ee08e5c2c910e5c2c278ed717c4652c7ffbe Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Fri, 17 Mar 2023 11:16:43 +0800 Subject: [PATCH 413/821] Remove synthetic attribute from SharedEntityManager. AOT no longer needs this bean to be synthetic to work properly. And this attribute hampers BeanPostProcessors from being applied. See #2730 Original Pull Request: #2866 --- .../config/JpaRepositoryConfigExtension.java | 1 - .../JpaRepositoryConfigExtensionUnitTests.java | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index dc47c51c96..0660920eb1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -217,7 +217,6 @@ private String registerSharedEntityMangerIfNotAlreadyRegistered(BeanDefinitionRe AbstractBeanDefinition entityManager = getEntityManagerBeanDefinitionFor(config, null); entityManager.setRole(BeanDefinition.ROLE_SUPPORT); - entityManager.setSynthetic(true); entityManager.setPrimary(false); entityManager.setAutowireCandidate(false); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java index 65388887f9..32c5fffae2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtensionUnitTests.java @@ -31,6 +31,7 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -48,6 +49,7 @@ * @author Oliver Gierke * @author Mark Paluch * @author Jens Schauder + * @author Yanming Zhou */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -147,6 +149,20 @@ void exposesJpaAotProcessor() { .isEqualTo(JpaRepositoryConfigExtension.JpaRepositoryRegistrationAotProcessor.class); } + @Test // GH-2730 + void shouldNotRegisterEntityManagerAsSynthetic() { + + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + + RepositoryConfigurationExtension extension = new JpaRepositoryConfigExtension(); + extension.registerBeansForRoot(factory, configSource); + + AbstractBeanDefinition bd = (AbstractBeanDefinition) factory.getBeanDefinition("jpaSharedEM_" + + configSource.getAttribute("entityManagerFactoryRef").orElse("entityManagerFactory")); + + assertThat(bd.isSynthetic()).isEqualTo(false); + } + private void assertOnlyOnePersistenceAnnotationBeanPostProcessorRegistered(DefaultListableBeanFactory factory, String expectedBeanName) { From 3b5adcf6d0b91c222a3eec66a3c66154af4ffbb1 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 15 Jun 2023 15:10:28 +0200 Subject: [PATCH 414/821] Polishing. See #2998 --- pom.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 340ef8999b..67859bcdad 100644 --- a/pom.xml +++ b/pom.xml @@ -122,7 +122,7 @@ org.springframework spring-instrument ${spring} - test + provided @@ -224,4 +224,11 @@ + + + spring-milestone + https://repo.spring.io/milestone + + + From 93bc3080414a3b1e91b42036f57b6750652e6155 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Tue, 13 Jun 2023 16:49:06 +0800 Subject: [PATCH 415/821] Polishing. delegate.getResultWindow() the first time it's called in this method already produces a limit-sized result set, captured in resultsToUse. Running it through the same getResultWindow a second time in unnecessary. Original Pull Request: #3021 --- .../data/jpa/repository/query/ScrollDelegate.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java index eb244a0209..b3e7ad5d2f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ScrollDelegate.java @@ -35,6 +35,7 @@ * Delegate to run {@link ScrollPosition scroll queries} and create result {@link Window}. * * @author Mark Paluch + * @author Yanming Zhou * @since 3.1 */ public class ScrollDelegate { @@ -90,7 +91,7 @@ private static Window createWindow(Sort sort, int limit, Direction direct return ScrollPosition.of(keys, direction); }; - return Window.from(delegate.getResultWindow(resultsToUse, limit), positionFunction, hasMoreElements(result, limit)); + return Window.from(resultsToUse, positionFunction, hasMoreElements(result, limit)); } private static Window createWindow(List result, int limit, From fca9f09ce6b81de9d8e7b2b6f4dbe245b75c4cd1 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 15 Jun 2023 13:50:43 -0500 Subject: [PATCH 416/821] Handle VALUE function properly in JPQL queries. See #3028 --- .../org/springframework/data/jpa/repository/query/Jpql.g4 | 3 ++- .../data/jpa/repository/query/HqlQueryRendererTests.java | 5 +++++ .../data/jpa/repository/query/JpqlQueryRendererTests.java | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index b7a636f7c3..9f09273e91 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -603,7 +603,8 @@ identification_variable | ORDER | OUTER | FLOOR - | SIGN) + | SIGN + | VALUE) ; constructor_name diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index f37e341adc..319084421f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1522,6 +1522,11 @@ void queryWithSignShouldWork() { assertQuery("select t.sign from TestEntity t"); } + @Test // GH-3028 + void queryWithValueShouldWork() { + assertQuery("select t.value from TestEntity t"); + } + @Test // GH-3024 void castFunctionWithFqdnShouldWork() { assertQuery("SELECT o FROM Order o WHERE CAST(:userId AS java.util.UUID) IS NULL OR o.user.id = :userId"); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index 3a7d1598e7..989e9319f7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -936,4 +936,9 @@ void floorShouldBeValidEntityName() { void queryWithSignShouldWork() { assertQuery("select t.sign from TestEntity t"); } + + @Test // GH-3028 + void queryWithValueShouldWork() { + assertQuery("select t.value from TestEntity t"); + } } From e8d08ddd2707117ac386a4327ea8e7521ca0d9cd Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 15 Jun 2023 14:35:58 -0500 Subject: [PATCH 417/821] Upgrade to Hibernate 6.2.4.Final. See #3029 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 67859bcdad..1019e554c5 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 4.10.1 3.0.3 - 6.2.1.Final + 6.2.4.Final 2.7.1

    2.1.214

    4.5 From 57a6f726b49b34609f47ffdb83e9054a164ce9a9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 26 Jun 2023 09:24:36 +0200 Subject: [PATCH 418/821] Backoff JSqlParserQueryEnhancer if query type is not a supported query type. We now backoff from enhancing queries if the query type is not supported (e.g. TRUNCATE). Closes #3038 --- .../query/JSqlParserQueryEnhancer.java | 13 +++++++------ .../query/JSqlParserQueryEnhancerUnitTests.java | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 6 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 cd76876ac7..7536a5eb91 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 @@ -96,7 +96,7 @@ private ParsedType detectParsedType() { } else if (statement instanceof Merge) { return ParsedType.MERGE; } else { - return ParsedType.SELECT; + return ParsedType.OTHER; } } catch (JSQLParserException e) { throw new IllegalArgumentException("The query you provided is not a valid SQL Query!", e); @@ -314,10 +314,10 @@ private String detectAlias(String query) { Select selectStatement = parseSelectStatement(query); /* - 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 + * 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 */ if (!(selectStatement.getSelectBody() instanceof PlainSelect)) { return null; @@ -516,10 +516,11 @@ public DeclaredQuery getQuery() { *
  • {@code ParsedType.SELECT}: means the top level statement is {@link Select}
  • *
  • {@code ParsedType.INSERT}: means the top level statement is {@link Insert}
  • *
  • {@code ParsedType.MERGE}: means the top level statement is {@link Merge}
  • + *
  • {@code ParsedType.OTHER}: means the top level statement is a different top-level type
  • * */ enum ParsedType { - DELETE, UPDATE, SELECT, INSERT, MERGE; + DELETE, UPDATE, SELECT, INSERT, MERGE, OTHER; } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java index 5d97802439..2f84a4c1e1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancerUnitTests.java @@ -191,6 +191,23 @@ void multipleWithStatementsWorks() { assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); } + @Test // GH-3038 + void truncateStatementShouldWork() { + + StringQuery stringQuery = new StringQuery("TRUNCATE TABLE foo", true); + QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); + + assertThat(stringQuery.getAlias()).isNull(); + assertThat(stringQuery.getProjection()).isEmpty(); + assertThat(stringQuery.hasConstructorExpression()).isFalse(); + + assertThat(queryEnhancer.applySorting(Sort.by("day").descending())).isEqualTo("TRUNCATE TABLE foo"); + assertThat(queryEnhancer.getJoinAliases()).isEmpty(); + assertThat(queryEnhancer.detectAlias()).isNull(); + assertThat(queryEnhancer.getProjection()).isEmpty(); + assertThat(queryEnhancer.hasConstructorExpression()).isFalse(); + } + @ParameterizedTest // GH-2641 @MethodSource("mergeStatementWorksSource") void mergeStatementWorksWithJSqlParser(String query, String alias) { From 13a4661bc0d07acb6b107c2ee5ca9b57504f0a64 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 26 Jun 2023 09:38:33 +0200 Subject: [PATCH 419/821] Reuse parsed SQL statement for alias and projection detection. Closes #3039 --- .../query/JSqlParserQueryEnhancer.java | 65 +++++++++---------- 1 file changed, 31 insertions(+), 34 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 7536a5eb91..03a92fed5e 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 @@ -64,6 +64,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { private final DeclaredQuery query; + private final Statement statement; private final ParsedType parsedType; /** @@ -72,7 +73,13 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { public JSqlParserQueryEnhancer(DeclaredQuery query) { this.query = query; - this.parsedType = detectParsedType(); + try { + this.statement = CCJSqlParserUtil.parse(this.query.getQueryString()); + } catch (JSQLParserException e) { + throw new IllegalArgumentException("The query is not a valid SQL Query", e); + } + + this.parsedType = detectParsedType(statement); } /** @@ -80,26 +87,20 @@ public JSqlParserQueryEnhancer(DeclaredQuery query) { * * @return the parsed type */ - private ParsedType detectParsedType() { - - try { - Statement statement = CCJSqlParserUtil.parse(this.query.getQueryString()); - - if (statement instanceof Insert) { - return ParsedType.INSERT; - } else if (statement instanceof Update) { - return ParsedType.UPDATE; - } else if (statement instanceof Delete) { - return ParsedType.DELETE; - } else if (statement instanceof Select) { - return ParsedType.SELECT; - } else if (statement instanceof Merge) { - return ParsedType.MERGE; - } else { - return ParsedType.OTHER; - } - } catch (JSQLParserException e) { - throw new IllegalArgumentException("The query you provided is not a valid SQL Query!", e); + private static ParsedType detectParsedType(Statement statement) { + + if (statement instanceof Insert) { + return ParsedType.INSERT; + } else if (statement instanceof Update) { + return ParsedType.UPDATE; + } else if (statement instanceof Delete) { + return ParsedType.DELETE; + } else if (statement instanceof Select) { + return ParsedType.SELECT; + } else if (statement instanceof Merge) { + return ParsedType.MERGE; + } else { + return ParsedType.OTHER; } } @@ -127,9 +128,8 @@ public String applySorting(Sort sort, @Nullable String alias) { PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); - final Set joinAliases = getJoinAliases(selectBody); - - final Set selectionAliases = getSelectionAliases(selectBody); + Set joinAliases = getJoinAliases(selectBody); + Set selectionAliases = getSelectionAliases(selectBody); List orderByElements = sort.stream() // .map(order -> getOrderClause(joinAliases, selectionAliases, alias, order)) // @@ -203,7 +203,7 @@ Set getSelectionAliases() { return new HashSet<>(); } - Select selectStatement = parseSelectStatement(this.query.getQueryString()); + Select selectStatement = (Select) statement; PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); return this.getSelectionAliases(selectBody); } @@ -220,7 +220,7 @@ private Set getJoinAliases(String query) { return new HashSet<>(); } - Select selectStatement = parseSelectStatement(query); + Select selectStatement = (Select) statement; if (selectStatement.getSelectBody()instanceof PlainSelect selectBody) { return getJoinAliases(selectBody); } @@ -306,12 +306,12 @@ private String detectAlias(String query) { if (ParsedType.MERGE.equals(this.parsedType)) { - Merge mergeStatement = parseSelectStatement(query, Merge.class); + Merge mergeStatement = (Merge) statement; return detectAlias(mergeStatement); } else if (ParsedType.SELECT.equals(this.parsedType)) { - Select selectStatement = parseSelectStatement(query); + Select selectStatement = (Select) statement; /* * For all the other types ({@link ValuesStatement} and {@link SetOperationList}) it does not make sense to provide @@ -319,11 +319,10 @@ private String detectAlias(String query) { * ValuesStatement has no alias * SetOperation can have multiple alias for each operation item */ - if (!(selectStatement.getSelectBody() instanceof PlainSelect)) { + if (!(selectStatement.getSelectBody()instanceof PlainSelect selectBody)) { return null; } - PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); return detectAlias(selectBody); } @@ -375,12 +374,10 @@ public String createCountQueryFor(@Nullable String countProjection) { /* We only support count queries for {@link PlainSelect}. */ - if (!(selectStatement.getSelectBody() instanceof PlainSelect)) { + if (!(selectStatement.getSelectBody()instanceof PlainSelect selectBody)) { return this.query.getQueryString(); } - PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody(); - // remove order by selectBody.setOrderByElements(null); @@ -436,7 +433,7 @@ public String getProjection() { Assert.hasText(query.getQueryString(), "Query must not be null or empty"); - Select selectStatement = parseSelectStatement(query.getQueryString()); + Select selectStatement = (Select) statement; if (selectStatement.getSelectBody() instanceof ValuesStatement) { return ""; From 37e88e9aa58fcbe1647821cbdb71d7a1443dfa05 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 26 Jun 2023 10:33:58 +0200 Subject: [PATCH 420/821] Correctly apply OffsetScrollPosition to derived queries. We now apply the scroll position correctly regardless of whether the query is limited. Previously, we applied the position only if the query was limited. Closes #3015 --- .../jpa/repository/query/PartTreeJpaQuery.java | 8 ++++---- .../jpa/repository/UserRepositoryTests.java | 17 ++++++++++++++++- .../jpa/repository/sample/UserRepository.java | 5 +++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 78cfde34a5..b1ccc6943a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -255,11 +255,11 @@ public Query createQuery(JpaParametersParameterAccessor accessor) { @SuppressWarnings("ConstantConditions") private Query restrictMaxResultsIfNecessary(Query query, @Nullable ScrollPosition scrollPosition) { - if (tree.isLimiting()) { + if (scrollPosition instanceof OffsetScrollPosition offset) { + query.setFirstResult(Math.toIntExact(offset.getOffset())); + } - if (scrollPosition instanceof OffsetScrollPosition offset) { - query.setFirstResult(Math.toIntExact(offset.getOffset())); - } + if (tree.isLimiting()) { if (query.getMaxResults() != Integer.MAX_VALUE) { /* 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 0874aaaea0..00a1750a59 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 @@ -57,7 +57,6 @@ import org.springframework.data.domain.*; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; -import org.springframework.data.domain.ExampleMatcher.*; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.sample.Address; import org.springframework.data.jpa.domain.sample.QUser; @@ -1428,6 +1427,22 @@ void scrollByPartTreeKeysetBackward() { assertThat(previousWindow.hasNext()).isFalse(); } + @Test // GH-3015 + void shouldApplyOffsetScrollPosition() { + + User jane1 = new User("Jane", "Doe", "jane@doe1.com"); + User jane2 = new User("Jane", "Doe", "jane@doe2.com"); + User john1 = new User("John", "Doe", "john@doe1.com"); + User john2 = new User("John", "Doe", "john@doe2.com"); + + repository.saveAllAndFlush(Arrays.asList(john1, john2, jane1, jane2)); + + Window atOffset3 = repository.findByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc("J", + ScrollPosition.offset(3)); + + assertThat(atOffset3).containsExactly(john2); + } + @Test // DATAJPA-491 void sortByNestedAssociationPropertyWithSortInPageable() { 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 a1bd087119..fad09d8a23 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 @@ -26,6 +26,7 @@ import java.util.Set; import java.util.stream.Stream; +import org.springframework.data.domain.OffsetScrollPosition; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -155,6 +156,8 @@ public interface UserRepository extends JpaRepository, JpaSpecifi Window findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(String firstname, ScrollPosition position); + Window findByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(String firstname, ScrollPosition position); + List findByFirstnameNotIn(Collection firstnames); // DATAJPA-292 @@ -722,6 +725,8 @@ List findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter @Query("select u from User u where u.firstname >= (select Min(u0.firstname) from User u0)") List findProjectionBySubselect(); + Window findBy(OffsetScrollPosition position); + interface RolesAndFirstname { String getFirstname(); From 37d031e98c85297e63513648ee6f55a08812dd0b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 26 Jun 2023 12:07:39 -0500 Subject: [PATCH 421/821] Handle backslash characters for LIKE ESCAPE clause. Hibernate HQL supports using a `\\` as the ESCAPE character so we need to support it in our custom parser. See #3040 --- .../org/springframework/data/jpa/repository/query/Hql.g4 | 4 ++-- .../data/jpa/repository/query/HqlQueryRenderer.java | 5 +++-- .../data/jpa/repository/query/HqlQueryRendererTests.java | 5 +++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index e5ad3cfe82..7516949148 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -605,7 +605,7 @@ dealingWithNullExpression // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-like-predicate stringPatternMatching - : expression NOT? (LIKE | ILIKE) expression (ESCAPE (character|parameter))? + : expression NOT? (LIKE | ILIKE) expression (ESCAPE (stringLiteral|parameter))? ; // https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-elements-indices @@ -1078,7 +1078,7 @@ fragment HEX_DIGIT : [0-9a-fA-F]; CHARACTER : '\'' (~ ('\'' | '\\' )) '\'' ; -STRINGLITERAL : '\'' ('\'' '\'' | ~('\'' | '\\'))* '\'' ; +STRINGLITERAL : '\'' ('\'' '\'' | ~('\''))* '\'' ; JAVASTRINGLITERAL : '"' ( ('\\' [btnfr"']) | ~('"'))* '"'; INTEGER_LITERAL : INTEGER_NUMBER (L | B I)? ; FLOAT_LITERAL : FLOAT_NUMBER (D | F | B D)?; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 0affea51af..e705664a7f 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -2231,8 +2231,9 @@ public List visitStringPatternMatching(HqlParser.StringPat if (ctx.ESCAPE() != null) { tokens.add(new JpaQueryParsingToken(ctx.ESCAPE())); - if (ctx.character() != null) { - tokens.addAll(visit(ctx.character())); + + if (ctx.stringLiteral() != null) { + tokens.addAll(visit(ctx.stringLiteral())); } else if (ctx.parameter() != null) { tokens.addAll(visit(ctx.parameter())); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 319084421f..fcd151679e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1544,4 +1544,9 @@ void binaryLiteralsShouldWork() { assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.value = X'DEADBEEF'"); assertQuery("SELECT ce.id FROM CalendarEvent ce WHERE ce.value = x'deadbeef'"); } + + @Test // GH-3040 + void escapeClauseShouldWork() { + assertQuery("select t.name from SomeDbo t where t.name LIKE :name escape '\\\\'"); + } } From aeec5b86058e1a6070da5feff0d5bb2df5722199 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 28 Jun 2023 09:56:26 -0500 Subject: [PATCH 422/821] Polishing. --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3759ab7607..415311f002 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -48,7 +48,7 @@ pipeline { when { beforeAgent(true) allOf { - branch(pattern: "issue/gh-java21|main|(\\d\\.\\d\\.x)", comparator: "REGEXP") + branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP") not { triggeredBy 'UpstreamCause' } } } From a98fb84e1e106214e786bf9a705270db2fd28464 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 3 Jul 2023 09:49:39 +0200 Subject: [PATCH 423/821] Upgrade to Maven Wrapper 3.9.3. See #3049 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 929909e554..ab824459af 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Tue Jun 13 08:54:57 CEST 2023 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip +#Mon Jul 03 09:49:39 CEST 2023 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip From 30c32f7452463ac29905cf9e481248eaf8084958 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 3 Jul 2023 09:50:15 +0200 Subject: [PATCH 424/821] Update CI properties. See #2950 --- ci/pipeline.properties | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 026fdcb58b..736b1df179 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,21 +1,21 @@ # Java versions -java.main.tag=17.0.6_10-jdk-focal -java.next.tag=21-jdk-bullseye +java.main.tag=17.0.7_7-jdk-focal +java.next.tag=20-jdk-jammy # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} -docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/openjdk:${java.next.tag} +docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag} # Supported versions of MongoDB -docker.mongodb.4.4.version=4.4.18 -docker.mongodb.5.0.version=5.0.14 -docker.mongodb.6.0.version=6.0.4 +docker.mongodb.4.4.version=4.4.22 +docker.mongodb.5.0.version=5.0.18 +docker.mongodb.6.0.version=6.0.7 # Supported versions of Redis -docker.redis.6.version=6.2.10 +docker.redis.6.version=6.2.12 # Supported versions of Cassandra -docker.cassandra.3.version=3.11.14 +docker.cassandra.3.version=3.11.15 # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home From 133ec27b74e58dce4aa59b201bd84fe9aeb30a6b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 5 Jul 2023 11:25:02 +0200 Subject: [PATCH 425/821] Add support for `Limit`. Closes #3052 --- .../jpa/repository/query/JpaParameters.java | 11 ++++++++-- .../data/jpa/repository/query/NamedQuery.java | 2 +- .../jpa/repository/query/ParameterBinder.java | 2 +- .../repository/UserRepositoryFinderTests.java | 21 +++++++++++++++++++ .../query/JpaQueryMethodUnitTests.java | 14 ++++++------- .../jpa/repository/sample/UserRepository.java | 3 +++ 6 files changed, 42 insertions(+), 11 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index 428c0ec18d..b41144a3f4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -15,12 +15,12 @@ */ package org.springframework.data.jpa.repository.query; +import jakarta.persistence.TemporalType; + import java.lang.reflect.Method; import java.util.Date; import java.util.List; -import jakarta.persistence.TemporalType; - import org.springframework.core.MethodParameter; import org.springframework.data.jpa.repository.Temporal; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; @@ -60,6 +60,13 @@ protected JpaParameters createFrom(List parameters) { return new JpaParameters(parameters); } + /** + * @return {@code true} if the method signature declares Limit or Pageable parameters. + */ + public boolean hasLimitingParameters() { + return hasLimitParameter() || hasPageableParameter(); + } + /** * Custom {@link Parameter} implementation adding parameters of type {@link Temporal} to the special ones. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index e78c9c2531..404a0673a6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -79,7 +79,7 @@ private NamedQuery(JpaQueryMethod method, EntityManager em) { this.declaredQuery = DeclaredQuery.of(queryString, false); - boolean weNeedToCreateCountQuery = !namedCountQueryIsPresent && method.getParameters().hasPageableParameter(); + boolean weNeedToCreateCountQuery = !namedCountQueryIsPresent && method.getParameters().hasLimitingParameters(); boolean cantExtractQuery = !extractor.canExtractQuery(); if (weNeedToCreateCountQuery && cantExtractQuery) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 9298308d64..4c30d2fd46 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -96,7 +96,7 @@ Query bindAndPrepare(Query query, QueryParameterSetter.QueryMetadata metadata, bind(query, metadata, accessor); - if (!useJpaForPaging || !parameters.hasPageableParameter() || accessor.getPageable().isUnpaged()) { + if (!useJpaForPaging || !parameters.hasLimitingParameters() || accessor.getPageable().isUnpaged()) { return query; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 5ded8dd6bb..00dd6c2dd0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -29,11 +29,14 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Limit; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Window; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; @@ -53,6 +56,7 @@ * @author Oliver Gierke * @author Krzysztof Krason * @author Greg Turnquist + * @author Mark Paluch * @see QueryLookupStrategy */ @ExtendWith(SpringExtension.class) @@ -221,6 +225,23 @@ void executesQueryToSliceWithUnpaged() { assertThat(slice.hasNext()).isFalse(); } + @Test // DATAJPA-94 + void executesQueryWithLimitAndScrollPosition() { + + Window first = userRepository.findByLastnameOrderByFirstname(Limit.of(1), // + ScrollPosition.offset(), // + "Matthews" // + ); + + Window next = userRepository.findByLastnameOrderByFirstname(Limit.of(1), // + ScrollPosition.offset(1), // + "Matthews" // + ); + + assertThat(first).containsExactly(dave); + assertThat(next).containsExactly(oliver); + } + @Test // DATAJPA-830 void executesMethodWithNotContainingOnStringCorrectly() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 3854b4998b..91b92738d5 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -15,13 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import jakarta.persistence.LockModeType; import jakarta.persistence.QueryHint; @@ -159,6 +155,8 @@ void returnsQueryIfAvailable() throws Exception { @Test void rejectsInvalidReturntypeOnPagebleFinder() { + when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + assertThatIllegalStateException() .isThrownBy(() -> new JpaQueryMethod(invalidReturnType, metadata, factory, extractor)); } @@ -166,6 +164,8 @@ void rejectsInvalidReturntypeOnPagebleFinder() { @Test void rejectsPageableAndSortInFinderMethod() { + when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + assertThatIllegalStateException() .isThrownBy(() -> new JpaQueryMethod(pageableAndSort, metadata, factory, extractor)); } 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 fad09d8a23..1b2870316d 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 @@ -26,6 +26,7 @@ import java.util.Set; import java.util.stream.Stream; +import org.springframework.data.domain.Limit; import org.springframework.data.domain.OffsetScrollPosition; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -226,6 +227,8 @@ Window findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(S Page findByLastnameIgnoringCase(Pageable pageable, String lastname); + Window findByLastnameOrderByFirstname(Limit limit, ScrollPosition scrollPosition, String lastname); + List findByLastnameIgnoringCaseLike(String lastname); List findByLastnameAndFirstnameAllIgnoringCase(String lastname, String firstname); From 36accbbb0c1f0598f0b2a40f62bccadd613927b4 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 5 Jul 2023 12:41:40 -0500 Subject: [PATCH 426/821] Sort properties shouldn't overlap with aliases. When using an alias in a query, verify using "." as a proper separator against both alias projections as the primary alias to ensure that a Sort property doesn't overlap with an alias. See #3054 --- .../query/JpaQueryTransformerSupport.java | 7 +++++- .../query/HqlQueryTransformerTests.java | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java index bce349e871..b791c4771e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java @@ -131,13 +131,18 @@ private boolean shouldPrefixWithAlias(Sort.Order order, String primaryFromAlias) return false; } + // If the Sort starts with the primary alias + if (order.getProperty().startsWith(primaryFromAlias + ".")) { + return false; + } + // If the Sort references an alias directly if (projectionAliases.contains(order.getProperty())) { return false; } // If the Sort property starts with an alias - if (projectionAliases.stream().anyMatch(alias -> order.getProperty().startsWith(alias))) { + if (projectionAliases.stream().anyMatch(alias -> order.getProperty().startsWith(alias + "."))) { return false; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java index 460d8f456f..a5d1cd7c30 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryTransformerTests.java @@ -1006,6 +1006,31 @@ void sortingRecognizesJoinAliases() { """); } + @Test // GH-3054 + void aliasesShouldNotOverlapWithSortProperties() { + + assertThat( + createQueryFor("select e from Employee e where e.name = :name", Sort.by(Sort.Order.desc("evaluationDate")))) + .isEqualToIgnoringWhitespace( + "select e from Employee e where e.name = :name order by e.evaluationDate desc"); + + assertThat(createQueryFor("select e from Employee e join training t where e.name = :name", + Sort.by(Sort.Order.desc("trainingDueDate")))).isEqualToIgnoringWhitespace( + "select e from Employee e join training t where e.name = :name order by e.trainingDueDate desc"); + + assertThat(createQueryFor("select e from Employee e join training t where e.name = :name", + Sort.by(Sort.Order.desc("t.trainingDueDate")))).isEqualToIgnoringWhitespace( + "select e from Employee e join training t where e.name = :name order by t.trainingDueDate desc"); + + assertThat(createQueryFor("SELECT t3 FROM Test3 t3 JOIN t3.test2 t2 JOIN t2.test1 test WHERE test.id = :test1Id", + Sort.by(Sort.Order.desc("testDuplicateColumnName")))).isEqualToIgnoringWhitespace( + "SELECT t3 FROM Test3 t3 JOIN t3.test2 t2 JOIN t2.test1 test WHERE test.id = :test1Id order by t3.testDuplicateColumnName desc"); + + assertThat(createQueryFor("SELECT t3 FROM Test3 t3 JOIN t3.test2 x WHERE x.id = :test2Id", + Sort.by(Sort.Order.desc("t3.testDuplicateColumnName")))).isEqualToIgnoringWhitespace( + "SELECT t3 FROM Test3 t3 JOIN t3.test2 x WHERE x.id = :test2Id order by t3.testDuplicateColumnName desc"); + } + private void assertCountQuery(String originalQuery, String countQuery) { assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); } From 71258cca889ed7dc8f3eeae30c1fdcabaa085e84 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 6 Jul 2023 17:00:31 -0500 Subject: [PATCH 427/821] Restore testing againt Java 21 on CI. Our release tools pushed out an update to our pipeline.properties. Because Java 21 hasn't been updated (yet) there, it overwrote our testing against Java 21. This should reinstate things until we can roll this out everywhere. See #2993, #2950 --- ci/pipeline.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 736b1df179..8a33425f94 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,10 +1,10 @@ # Java versions java.main.tag=17.0.7_7-jdk-focal -java.next.tag=20-jdk-jammy +java.next.tag=21-jdk-bullseye # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} -docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag} +docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/openjdk:${java.next.tag} # Supported versions of MongoDB docker.mongodb.4.4.version=4.4.22 From 7627b23c39f7bd02c2189b51e3c9751c439c2d2c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 10 Jul 2023 14:11:03 +0200 Subject: [PATCH 428/821] Upgrade to H2 2.2.220. Also upgrade Postgres and MySQL drivers. Closes #3059 --- pom.xml | 6 +++--- spring-data-envers/pom.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 1019e554c5..e5097fe7d2 100644 --- a/pom.xml +++ b/pom.xml @@ -32,10 +32,10 @@ 3.0.3 6.2.4.Final 2.7.1 -

    2.1.214

    +

    2.2.220

    4.5 - 8.0.31 - 42.5.0 + 8.0.33 + 42.6.0 3.2.0-SNAPSHOT 0.10.3 diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 0c3d16ef85..f239d6394b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -79,7 +79,7 @@ com.h2database h2 - 2.1.212 + ${h2} test From ab34ee61a7865e2c22fd6a589030ee441125f1d1 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 10 Jul 2023 08:45:32 -0500 Subject: [PATCH 429/821] TYPE should be a valid attribute of an entity for a custom query. See #3062, #3056. --- .../org/springframework/data/jpa/repository/query/Jpql.g4 | 1 + .../data/jpa/repository/query/HqlQueryRendererTests.java | 7 +++++++ .../data/jpa/repository/query/JpqlQueryRendererTests.java | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 9f09273e91..13d6a24a79 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -604,6 +604,7 @@ identification_variable | OUTER | FLOOR | SIGN + | TYPE | VALUE) ; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index fcd151679e..48592afbf8 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1549,4 +1549,11 @@ void binaryLiteralsShouldWork() { void escapeClauseShouldWork() { assertQuery("select t.name from SomeDbo t where t.name LIKE :name escape '\\\\'"); } + + @Test // GH-3062, GH-3056 + void typeShouldBeAValidParameter() { + + assertQuery("select e from Employee e where e.type = :_type"); + assertQuery("select te from TestEntity te where te.type = :type"); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index 989e9319f7..f736c635be 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -941,4 +941,11 @@ void queryWithSignShouldWork() { void queryWithValueShouldWork() { assertQuery("select t.value from TestEntity t"); } + + @Test // GH-3062, GH-3056 + void typeShouldBeAValidParameter() { + + assertQuery("select e from Employee e where e.type = :_type"); + assertQuery("select te from TestEntity te where te.type = :type"); + } } From d1043efc365dee0251385c89f0b78f0fd7ef7119 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 11 Jul 2023 10:32:14 -0500 Subject: [PATCH 430/821] Handle != as NOT EQUALS for JPQL. See #3061. --- .../data/jpa/repository/query/Jpql.g4 | 17 ++++++++++------- .../query/JpqlQueryRendererTests.java | 5 +++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 13d6a24a79..e17a919f4a 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -164,7 +164,7 @@ update_clause ; update_item - : (identification_variable '.')? (single_valued_embeddable_object_field '.')* (state_field | single_valued_object_field) '=' new_value + : (identification_variable '.')? (single_valued_embeddable_object_field '.')* (state_field | single_valued_object_field) EQUAL new_value ; new_value @@ -377,21 +377,21 @@ all_or_any_expression comparison_expression : string_expression comparison_operator (string_expression | all_or_any_expression) - | boolean_expression op=('=' | '<>') (boolean_expression | all_or_any_expression) - | enum_expression op=('=' | '<>') (enum_expression | all_or_any_expression) + | boolean_expression op=(EQUAL | NOT_EQUAL) (boolean_expression | all_or_any_expression) + | enum_expression op=(EQUAL | NOT_EQUAL) (enum_expression | all_or_any_expression) | datetime_expression comparison_operator (datetime_expression | all_or_any_expression) - | entity_expression op=('=' | '<>') (entity_expression | all_or_any_expression) + | entity_expression op=(EQUAL | NOT_EQUAL) (entity_expression | all_or_any_expression) | arithmetic_expression comparison_operator (arithmetic_expression | all_or_any_expression) - | entity_type_expression op=('=' | '<>') entity_type_expression + | entity_type_expression op=(EQUAL | NOT_EQUAL) entity_type_expression ; comparison_operator - : op='=' + : op=EQUAL | op='>' | op='>=' | op='<' | op='<=' - | op='<>' + | op=NOT_EQUAL ; arithmetic_expression @@ -841,6 +841,9 @@ VALUE : V A L U E; WHEN : W H E N; WHERE : W H E R E; +EQUAL : '=' ; +NOT_EQUAL : '<>' | '!=' ; + CHARACTER : '\'' (~ ('\'' | '\\')) '\'' ; IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index f736c635be..368b1379ab 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -948,4 +948,9 @@ void typeShouldBeAValidParameter() { assertQuery("select e from Employee e where e.type = :_type"); assertQuery("select te from TestEntity te where te.type = :type"); } + + @Test // GH-3061 + void alternateNotEqualsOperatorShouldWork() { + assertQuery("select e from Employee e where e.firstName != :name"); + } } From 1824f49babfb6a45bec8edbb9e03c01bb1205bfa Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Jul 2023 14:52:12 +0200 Subject: [PATCH 431/821] Prepare 3.2 M1 (2023.1.0). See #2950 --- pom.xml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index e5097fe7d2..80d318cf76 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 @@ -24,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-SNAPSHOT + 3.2.0-M1 @@ -36,7 +35,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-SNAPSHOT + 3.2.0-M1 0.10.3 org.hibernate @@ -208,16 +207,6 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - spring-milestone https://repo.spring.io/milestone From 36c678853d34e797e9b18b9e9b3c6fdf403c3b06 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Jul 2023 14:53:18 +0200 Subject: [PATCH 432/821] Release version 3.2 M1 (2023.1.0). See #2950 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 80d318cf76..7b00b71abe 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M1 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index f239d6394b..6c14975fed 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-SNAPSHOT + 3.2.0-M1 org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M1 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 991cd8cbf0..57710229c2 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M1 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index b21b03c313..bf78cb1c96 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-SNAPSHOT + 3.2.0-M1 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M1 ../pom.xml From 239dc25986df4c2a162c2101ba5c4621f76fda1f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Jul 2023 14:57:10 +0200 Subject: [PATCH 433/821] Prepare next development iteration. See #2950 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 7b00b71abe..80d318cf76 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M1 + 3.2.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 6c14975fed..f239d6394b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-M1 + 3.2.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.2.0-M1 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 57710229c2..991cd8cbf0 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M1 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index bf78cb1c96..b21b03c313 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-M1 + 3.2.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M1 + 3.2.0-SNAPSHOT ../pom.xml From 2d5df9673334bb5f60a9779265ecc7c193a2d7b4 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 14 Jul 2023 14:57:12 +0200 Subject: [PATCH 434/821] After release cleanups. See #2950 --- pom.xml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 80d318cf76..f3f62584cc 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-M1 + 3.2.0-SNAPSHOT @@ -35,7 +35,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-M1 + 3.2.0-SNAPSHOT 0.10.3 org.hibernate @@ -207,6 +207,16 @@ + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + spring-milestone https://repo.spring.io/milestone From b91fd82cb2d5864012148070c3fcc9c65c706aeb Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 20 Jul 2023 11:57:48 +0200 Subject: [PATCH 435/821] Consider Tuples in Keyset extraction. We now consider Tuple values when the query uses tuples for e.g. interface projections when extracting keyset values. Closes #3077 --- .../JpaMetamodelEntityInformation.java | 28 +++++++++++++++---- .../repository/UserRepositoryFinderTests.java | 14 ++++++++++ .../jpa/repository/sample/UserRepository.java | 2 ++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index dbf7285a00..362bf21c8a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -17,6 +17,7 @@ import jakarta.persistence.IdClass; import jakarta.persistence.PersistenceUnitUtil; +import jakarta.persistence.Tuple; import jakarta.persistence.metamodel.Attribute; import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.IdentifiableType; @@ -34,6 +35,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import org.springframework.beans.BeanWrapper; import org.springframework.core.annotation.AnnotationUtils; @@ -154,6 +156,11 @@ public ID getId(T entity) { // If it's a simple type, then immediately delegate to the provider if (idMetadata.hasSimpleId()) { + + if (entity instanceof Tuple t) { + return (ID) t.get(idMetadata.getSimpleIdAttribute().getName()); + } + return (ID) persistenceUnitUtil.getIdentifier(entity); } @@ -225,27 +232,38 @@ public boolean isNew(T entity) { @Override public Map getKeyset(Iterable propertyPaths, T entity) { - // TODO: Proxy handling requires more elaborate refactoring, see - // https://github.com/spring-projects/spring-data-jpa/issues/2784 - BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity); + Function getter = getPropertyValueFunction(entity); Map keyset = new LinkedHashMap<>(); if (hasCompositeId()) { for (String idAttributeName : getIdAttributeNames()) { - keyset.put(idAttributeName, entityWrapper.getPropertyValue(idAttributeName)); + keyset.put(idAttributeName, getter.apply(idAttributeName)); } } else { keyset.put(getIdAttribute().getName(), getId(entity)); } for (String propertyPath : propertyPaths) { - keyset.put(propertyPath, entityWrapper.getPropertyValue(propertyPath)); + keyset.put(propertyPath, getter.apply(propertyPath)); } return keyset; } + private Function getPropertyValueFunction(Object entity) { + + if (entity instanceof Tuple t) { + return t::get; + } + + // TODO: Proxy handling requires more elaborate refactoring, see + // https://github.com/spring-projects/spring-data-jpa/issues/2784 + BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity); + + return entityWrapper::getPropertyValue; + } + /** * Simple value object to encapsulate id specific metadata. * diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 00dd6c2dd0..b5e72eb4a3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -43,6 +43,7 @@ import org.springframework.data.jpa.repository.sample.RoleRepository; import org.springframework.data.jpa.repository.sample.UserRepository; import org.springframework.data.jpa.repository.sample.UserRepository.IdOnly; +import org.springframework.data.jpa.repository.sample.UserRepository.NameOnly; import org.springframework.data.jpa.repository.sample.UserRepository.RolesAndFirstname; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.test.context.ContextConfiguration; @@ -242,6 +243,19 @@ void executesQueryWithLimitAndScrollPosition() { assertThat(next).containsExactly(oliver); } + @Test // GH-3077 + void shouldProjectWithKeysetScrolling() { + + Window first = userRepository.findTop1ByLastnameOrderByFirstname(ScrollPosition.keyset(), // + "Matthews"); + + Window next = userRepository.findTop1ByLastnameOrderByFirstname(first.positionAt(0), // + "Matthews"); + + assertThat(first.getContent()).extracting(NameOnly::getFirstname).containsOnly(dave.getFirstname()); + assertThat(next.getContent()).extracting(NameOnly::getFirstname).containsOnly(oliver.getFirstname()); + } + @Test // DATAJPA-830 void executesMethodWithNotContainingOnStringCorrectly() { 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 1b2870316d..b79680a577 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 @@ -229,6 +229,8 @@ Window findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(S Window findByLastnameOrderByFirstname(Limit limit, ScrollPosition scrollPosition, String lastname); + Window findTop1ByLastnameOrderByFirstname(ScrollPosition scrollPosition, String lastname); + List findByLastnameIgnoringCaseLike(String lastname); List findByLastnameAndFirstnameAllIgnoringCase(String lastname, String firstname); From 454ce52ee67f4cb53474840f9aecfb46ab121881 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 27 Jun 2023 12:23:09 -0500 Subject: [PATCH 436/821] Implement Antora-based reference docs. See #3080 --- .github/workflows/deploy-docs.yml | 33 + .gitignore | 4 + pom.xml | 99 +- spring-data-jpa-distribution/pom.xml | 4 - src/main/antora/antora-playbook.yml | 37 + src/main/antora/antora.yml | 12 + src/main/antora/modules/ROOT/nav.adoc | 17 + .../modules/ROOT/pages}/envers.adoc | 4 +- .../modules/ROOT/pages}/faq.adoc | 1 + .../modules/ROOT/pages}/glossary.adoc | 2 + src/main/antora/modules/ROOT/pages/index.adoc | 24 + src/main/antora/modules/ROOT/pages/jpa.adoc | 6 + .../modules/ROOT/pages/jpa/auditing.adoc | 77 + .../ROOT/pages/jpa/entity-persistence.adoc | 55 + .../modules/ROOT/pages/jpa/introduction.adoc | 126 ++ .../pages/jpa/jpd-misc-cdi-integration.adoc | 62 + .../modules/ROOT/pages/jpa/locking.adoc | 32 + .../modules/ROOT/pages/jpa/misc-context.adoc | 27 + .../jpa/misc-merging-persistence-units.adoc | 41 + .../ROOT/pages/jpa}/query-by-example.adoc | 6 +- .../modules/ROOT/pages/jpa/query-methods.adoc | 781 +++++++++ .../ROOT/pages/jpa/specifications.adoc | 99 ++ .../ROOT/pages/jpa/stored-procedures.adoc | 97 ++ .../modules/ROOT/pages/jpa/transactions.adoc | 91 + src/main/asciidoc/images/epub-cover.png | Bin 50064 -> 0 bytes src/main/asciidoc/images/epub-cover.svg | 10 - src/main/asciidoc/index.adoc | 43 - src/main/asciidoc/jpa.adoc | 1491 ----------------- src/main/asciidoc/preface.adoc | 12 - ...epository-projections-dto-limitations.adoc | 1 - .../resources/antora-resources/antora.yml | 12 + 31 files changed, 1739 insertions(+), 1567 deletions(-) create mode 100644 .github/workflows/deploy-docs.yml create mode 100644 src/main/antora/antora-playbook.yml create mode 100644 src/main/antora/antora.yml create mode 100644 src/main/antora/modules/ROOT/nav.adoc rename src/main/{asciidoc => antora/modules/ROOT/pages}/envers.adoc (96%) rename src/main/{asciidoc => antora/modules/ROOT/pages}/faq.adoc (97%) rename src/main/{asciidoc => antora/modules/ROOT/pages}/glossary.adoc (96%) create mode 100644 src/main/antora/modules/ROOT/pages/index.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/auditing.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/introduction.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/locking.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc rename src/main/{asciidoc => antora/modules/ROOT/pages/jpa}/query-by-example.adoc (91%) create mode 100644 src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/specifications.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/transactions.adoc delete mode 100644 src/main/asciidoc/images/epub-cover.png delete mode 100644 src/main/asciidoc/images/epub-cover.svg delete mode 100644 src/main/asciidoc/index.adoc delete mode 100644 src/main/asciidoc/jpa.adoc delete mode 100644 src/main/asciidoc/preface.adoc delete mode 100644 src/main/asciidoc/repository-projections-dto-limitations.adoc create mode 100644 src/main/resources/antora-resources/antora.yml diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000000..1435fc2171 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,33 @@ +name: Deploy Docs +on: + push: + branches-ignore: [ gh-pages ] + tags: '**' + repository_dispatch: + types: request-build-reference # legacy + #schedule: + #- cron: '0 10 * * *' # Once per day at 10am UTC + workflow_dispatch: +permissions: + actions: write +jobs: + build: + runs-on: ubuntu-latest + # FIXME enable when pushed to spring-projects + # if: github.repository_owner == 'spring-projects' + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: docs-build + fetch-depth: 1 + - name: Dispatch (partial build) + if: github.ref_type == 'branch' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }} + - name: Dispatch (full build) + if: github.ref_type == 'tag' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) diff --git a/.gitignore b/.gitignore index 1a71d533d0..7cdfa7a0f4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ target/ .sonar4clipse *.sonar4clipseExternals .DS_Store +node_modules +package-lock.json +package.json +node \ No newline at end of file diff --git a/pom.xml b/pom.xml index f3f62584cc..4ddfbc8ec6 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,16 @@ reuseReports + + v18.12.1 + 8.19.2 + 3.2.0-alpha.2 + 1.0.0-alpha.1 + 1.0.0-alpha.3 + 1.0.0-beta.3 + 1.4.0 + 1.0.0-alpha.9 + @@ -101,7 +111,93 @@ 4.0.0 - + + docs + + + + com.github.eirslett + frontend-maven-plugin + 1.12.1 + + + install-antora + + install-node-and-npm + + initialize + + ${node.version} + ${npm.version} + + + + npm install antora + + npm + + initialize + + install @antora/cli@${antora.version} @antora/site-generator-default@${antora.version} @antora/atlas-extension@${antora-atlas.version} @antora/collector-extension@${antora-collector.version} @asciidoctor/tabs@${asciidoctor-tabs.version} @springio/antora-extensions@${spring-antora-extensions.version} @springio/asciidoctor-extensions@${spring-asciidoctor-extensions.version} + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + antora + + exec + + compile + + + node/node + + node_modules/.bin/antora + src/main/antora/antora-playbook.yml + --to-dir=target/site + + ${project.parent.basedir} + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + + node + false + + + node_modules + false + + + build + false + + + + + + + + src/main/resources + true + + + + @@ -202,7 +298,6 @@ - diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 991cd8cbf0..95ed749af3 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -29,10 +29,6 @@ org.apache.maven.plugins maven-assembly-plugin - - org.asciidoctor - asciidoctor-maven-plugin - diff --git a/src/main/antora/antora-playbook.yml b/src/main/antora/antora-playbook.yml new file mode 100644 index 0000000000..643dd938eb --- /dev/null +++ b/src/main/antora/antora-playbook.yml @@ -0,0 +1,37 @@ +# PACKAGES antora@3.2.0-alpha.2 @antora/atlas-extension:1.0.0-alpha.1 @antora/collector-extension@1.0.0-alpha.3 @springio/antora-extensions@1.1.0-alpha.2 @asciidoctor/tabs@1.0.0-alpha.12 @opendevise/antora-release-line-extension@1.0.0-alpha.2 +# +# The purpose of this Antora playbook is to build the docs in the current branch. +antora: + extensions: + - '@antora/collector-extension' + - require: '@springio/antora-extensions/root-component-extension' + root_component_name: 'data-jpa' +site: + title: Spring Data JPA + url: https://docs.spring.io/spring-data-jpa/reference/ +content: + sources: + - url: ./../../.. + branches: HEAD + start_path: src/main/antora + worktrees: true +asciidoc: + attributes: + page-pagination: '' + hide-uri-scheme: '@' + tabs-sync-option: '@' + chomp: 'all' + extensions: + - '@asciidoctor/tabs' + - '@springio/asciidoctor-extensions' + sourcemap: true +urls: + latest_version_segment: '' +runtime: + log: + failure_level: warn + format: pretty +ui: + bundle: + url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.3.3/ui-bundle.zip + snapshot: true \ No newline at end of file diff --git a/src/main/antora/antora.yml b/src/main/antora/antora.yml new file mode 100644 index 0000000000..edf3154af3 --- /dev/null +++ b/src/main/antora/antora.yml @@ -0,0 +1,12 @@ +name: data-jpa +version: true +title: Spring Data JPA +nav: + - modules/ROOT/nav.adoc +ext: + collector: + - run: + command: mvnw -Pdocs resources:resources + local: true + scan: + dir: target/classes/antora-resources diff --git a/src/main/antora/modules/ROOT/nav.adoc b/src/main/antora/modules/ROOT/nav.adoc new file mode 100644 index 0000000000..204efcda6f --- /dev/null +++ b/src/main/antora/modules/ROOT/nav.adoc @@ -0,0 +1,17 @@ +* xref:index.adoc[Overview] +* xref:jpa.adoc[] +** xref:jpa/introduction.adoc[] +** xref:jpa/entity-persistence.adoc[] +** xref:jpa/query-methods.adoc[] +** xref:jpa/stored-procedures.adoc[] +** xref:jpa/specifications.adoc[] +** xref:jpa/query-by-example.adoc[] +** xref:jpa/transactions.adoc[] +** xref:jpa/locking.adoc[] +** xref:jpa/auditing.adoc[] +** xref:jpa/misc-context.adoc[] +** xref:jpa/misc-merging-persistence-units.adoc[] +** xref:jpa/jpd-misc-cdi-integration.adoc[] +* xref:envers.adoc[] +* xref:faq.adoc[] +* xref:glossary.adoc[] diff --git a/src/main/asciidoc/envers.adoc b/src/main/antora/modules/ROOT/pages/envers.adoc similarity index 96% rename from src/main/asciidoc/envers.adoc rename to src/main/antora/modules/ROOT/pages/envers.adoc index d1c46be1dd..df8589135a 100644 --- a/src/main/asciidoc/envers.adoc +++ b/src/main/antora/modules/ROOT/pages/envers.adoc @@ -189,7 +189,7 @@ class EnversIntegrationTests { } } ---- -<1> This references the application context configuration presented earlier (in the <> section). +<1> This references the application context configuration presented earlier (in the xref:envers.adoc#envers.configuration[Configuration] section). ==== [[envers.resources]] @@ -201,4 +201,4 @@ You should also check out the {spring-data-commons-javadoc-base}/org/springframe You can ask questions at https://stackoverflow.com/questions/tagged/spring-data-envers[Stackoverflow by using the `spring-data-envers` tag]. -The https://github.com/spring-projects/spring-data-envers[source code and issue tracker for Spring Data Envers is hosted at GitHub]. +The https://github.com/spring-projects/spring-data-jpa[source code and issue tracker for Spring Data Envers is hosted at GitHub] (as a module of Spring Data JPA). diff --git a/src/main/asciidoc/faq.adoc b/src/main/antora/modules/ROOT/pages/faq.adoc similarity index 97% rename from src/main/asciidoc/faq.adoc rename to src/main/antora/modules/ROOT/pages/faq.adoc index 5a2f672738..7954f35158 100644 --- a/src/main/asciidoc/faq.adoc +++ b/src/main/antora/modules/ROOT/pages/faq.adoc @@ -1,5 +1,6 @@ [[faq]] [appendix] +[[frequently-asked-questions]] = Frequently Asked Questions [[faq.common]] diff --git a/src/main/asciidoc/glossary.adoc b/src/main/antora/modules/ROOT/pages/glossary.adoc similarity index 96% rename from src/main/asciidoc/glossary.adoc rename to src/main/antora/modules/ROOT/pages/glossary.adoc index b0810fe1cf..c74c09e214 100644 --- a/src/main/asciidoc/glossary.adoc +++ b/src/main/antora/modules/ROOT/pages/glossary.adoc @@ -1,6 +1,8 @@ [[glossary]] [appendix, glossary] +[[glossary]] = Glossary +:page-section-summary-toc: 1 AOP :: Aspect oriented programming diff --git a/src/main/antora/modules/ROOT/pages/index.adoc b/src/main/antora/modules/ROOT/pages/index.adoc new file mode 100644 index 0000000000..064d46877f --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/index.adoc @@ -0,0 +1,24 @@ +[[spring-data-jpa-reference-documentation]] += Spring Data JPA +Oliver Gierke; Thomas Darimont; Christoph Strobl; Mark Paluch; Jay Bryant; Greg Turnquist +:revnumber: {version} +:revdate: {localdate} +:feature-scroll: true + +(C) 2008-2023 The original authors. + +NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. + +[[preface]] +== Preface +:page-section-summary-toc: 1 + +Spring Data JPA provides repository support for the Jakarta Persistence API (JPA). It eases development of applications that need to access JPA data sources. + +[[project]] +== Project Metadata + +* Version control: https://github.com/spring-projects/spring-data-jpa +* Bugtracker: https://github.com/spring-projects/spring-data-jpa/issues +* Milestone repository: https://repo.spring.io/milestone +* Snapshot repository: https://repo.spring.io/snapshot \ No newline at end of file diff --git a/src/main/antora/modules/ROOT/pages/jpa.adoc b/src/main/antora/modules/ROOT/pages/jpa.adoc new file mode 100644 index 0000000000..eae798bbc7 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa.adoc @@ -0,0 +1,6 @@ +[[jpa.repositories]] += JPA Repositories +:page-section-summary-toc: 1 + +This chapter points out the specialties for repository support for JPA. This builds on the core repository support explained in {spring-data-commons-docs-url}/repositories.html[Working with Spring Data Repositories]. Make sure you have a sound understanding of the basic concepts explained there. + diff --git a/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc b/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc new file mode 100644 index 0000000000..20aeaa4c7c --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc @@ -0,0 +1,77 @@ +[[jpa.auditing]] += JPA Auditing + +Spring Data JPA provides auditing based upon the foundation provided by {spring-data-commons-docs-url}/auditing.html[Spring Data Common's Auditing support]. + + +There is also a convenience base class, `AbstractAuditable`, which you can extend to avoid the need to manually implement the interface methods. Doing so increases the coupling of your domain classes to Spring Data, which might be something you want to avoid. Usually, the annotation-based way of defining auditing metadata is preferred as it is less invasive and more flexible. + + +[[jpa.auditing.configuration]] +== General Auditing Configuration + +Spring Data JPA ships with an entity listener that can be used to trigger the capturing of auditing information. First, you must register the `AuditingEntityListener` to be used for all entities in your persistence contexts inside your `orm.xml` file, as shown in the following example: + +.Auditing configuration orm.xml +==== +[source, xml] +---- + + + + + + + +---- +==== + +You can also enable the `AuditingEntityListener` on a per-entity basis by using the `@EntityListeners` annotation, as follows: + +==== +[source, java] +---- +@Entity +@EntityListeners(AuditingEntityListener.class) +public class MyEntity { + +} +---- +==== + +NOTE: The auditing feature requires `spring-aspects.jar` to be on the classpath. + +With `orm.xml` suitably modified and `spring-aspects.jar` on the classpath, activating auditing functionality is a matter of adding the Spring Data JPA `auditing` namespace element to your configuration, as follows: + +.Activating auditing using XML configuration +==== +[source, xml] +---- + +---- +==== + +As of Spring Data JPA 1.5, you can enable auditing by annotating a configuration class with the `@EnableJpaAuditing` annotation. You must still modify the `orm.xml` file and have `spring-aspects.jar` on the classpath. The following example shows how to use the `@EnableJpaAuditing` annotation: + +.Activating auditing with Java configuration +==== +[source, java] +---- +@Configuration +@EnableJpaAuditing +class Config { + + @Bean + public AuditorAware auditorProvider() { + return new AuditorAwareImpl(); + } +} +---- +==== + +If you expose a bean of type `AuditorAware` to the `ApplicationContext`, the auditing infrastructure automatically picks it up and uses it to determine the current user to be set on domain types. If you have multiple implementations registered in the `ApplicationContext`, you can select the one to be used by explicitly setting the `auditorAwareRef` attribute of `@EnableJpaAuditing`. + +// FIXME: does this need to exist? +// [[jpa.misc]] +// = Miscellaneous Considerations + diff --git a/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc b/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc new file mode 100644 index 0000000000..16ad24607f --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc @@ -0,0 +1,55 @@ +[[jpa.entity-persistence]] += Persisting Entities + +This section describes how to persist (save) entities with Spring Data JPA. + +[[jpa.entity-persistence.saving-entites]] +== Saving Entities + +Saving an entity can be performed with the `CrudRepository.save(…)` method. It persists or merges the given entity by using the underlying JPA `EntityManager`. If the entity has not yet been persisted, Spring Data JPA saves the entity with a call to the `entityManager.persist(…)` method. Otherwise, it calls the `entityManager.merge(…)` method. + +[[jpa.entity-persistence.saving-entites.strategies]] +=== Entity State-detection Strategies +Spring Data JPA offers the following strategies to detect whether an entity is new or not: + +1. Version-Property and Id-Property inspection (*default*): + By default Spring Data JPA inspects first if there is a Version-property of non-primitive type. + If there is, the entity is considered new if the value of that property is `null`. + Without such a Version-property Spring Data JPA inspects the identifier property of the given entity. + If the identifier property is `null`, then the entity is assumed to be new. + Otherwise, it is assumed to be not new. +2. Implementing `Persistable`: If an entity implements `Persistable`, Spring Data JPA delegates the new detection to the `isNew(…)` method of the entity. See the link:$$https://docs.spring.io/spring-data/data-commons/docs/current/api/index.html?org/springframework/data/domain/Persistable.html$$[JavaDoc] for details. +3. Implementing `EntityInformation`: You can customize the `EntityInformation` abstraction used in the `SimpleJpaRepository` implementation by creating a subclass of `JpaRepositoryFactory` and overriding the `getEntityInformation(…)` method accordingly. You then have to register the custom implementation of `JpaRepositoryFactory` as a Spring bean. Note that this should be rarely necessary. See the link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/index.html?org/springframework/data/jpa/repository/support/JpaRepositoryFactory.html$$[JavaDoc] for details. + +Option 1 is not an option for entities that use manually assigned identifiers and no version attribute as with those the identifier will always be non-`null`. +A common pattern in that scenario is to use a common base class with a transient flag defaulting to indicate a new instance and using JPA lifecycle callbacks to flip that flag on persistence operations: + +.A base class for entities with manually assigned identifiers +==== +[source, java] +---- +@MappedSuperclass +public abstract class AbstractEntity implements Persistable { + + @Transient + private boolean isNew = true; <1> + + @Override + public boolean isNew() { + return isNew; <2> + } + + @PrePersist <3> + @PostLoad + void markNotNew() { + this.isNew = false; + } + + // More code… +} +---- +<1> Declare a flag to hold the new state. Transient so that it's not persisted to the database. +<2> Return the flag in the implementation of `Persistable.isNew()` so that Spring Data repositories know whether to call `EntityManager.persist()` or `….merge()`. +<3> Declare a method using JPA entity callbacks so that the flag is switched to indicate an existing entity after a repository call to `save(…)` or an instance creation by the persistence provider. +==== + diff --git a/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc b/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc new file mode 100644 index 0000000000..d1f66fd30a --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc @@ -0,0 +1,126 @@ +[[jpa.introduction]] += Introduction + +This section describes the basics of configuring Spring Data JPA through either: + +* "`xref:jpa/introduction.adoc#jpa.namespace[Spring Namespace]`" (XML configuration) +* "`xref:jpa/introduction.adoc#jpa.java-config[Annotation-based Configuration]`" (Java configuration) + +[[jpa.java-config]] +== Annotation-based Configuration +The Spring Data JPA repositories support can be activated through both JavaConfig as well as a custom XML namespace, as shown in the following example: + +.Spring Data JPA repositories using JavaConfig +==== +[source, java] +---- +@Configuration +@EnableJpaRepositories +@EnableTransactionManagement +class ApplicationConfig { + + @Bean + public DataSource dataSource() { + + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); + return builder.setType(EmbeddedDatabaseType.HSQL).build(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + vendorAdapter.setGenerateDdl(true); + + LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); + factory.setJpaVendorAdapter(vendorAdapter); + factory.setPackagesToScan("com.acme.domain"); + factory.setDataSource(dataSource()); + return factory; + } + + @Bean + public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + + JpaTransactionManager txManager = new JpaTransactionManager(); + txManager.setEntityManagerFactory(entityManagerFactory); + return txManager; + } +} +---- +==== +NOTE: You must create `LocalContainerEntityManagerFactoryBean` and not `EntityManagerFactory` directly, since the former also participates in exception translation mechanisms in addition to creating `EntityManagerFactory`. + +The preceding configuration class sets up an embedded HSQL database by using the `EmbeddedDatabaseBuilder` API of `spring-jdbc`. Spring Data then sets up an `EntityManagerFactory` and uses Hibernate as the sample persistence provider. The last infrastructure component declared here is the `JpaTransactionManager`. Finally, the example activates Spring Data JPA repositories by using the `@EnableJpaRepositories` annotation, which essentially carries the same attributes as the XML namespace. If no base package is configured, it uses the one in which the configuration class resides. + +[[jpa.namespace]] +== Spring Namespace + +The JPA module of Spring Data contains a custom namespace that allows defining repository beans. It also contains certain features and element attributes that are special to JPA. Generally, the JPA repositories can be set up by using the `repositories` element, as shown in the following example: + +.Setting up JPA repositories by using the namespace +==== +[source, xml] +---- + + + + + + +---- +==== + +TIP: Which is better, JavaConfig or XML? XML is how Spring was configured long ago. In today's era of fast-growing Java, record types, annotations, and more, new projects typically use as much pure Java as possible. While there is no immediate plan to remove XML support, some of the newest features MAY not be available through XML. + +Using the `repositories` element looks up Spring Data repositories as described in {spring-data-commons-docs-url}/repositories/create-instances.html[Creating Repository Instances]. Beyond that, it activates persistence exception translation for all beans annotated with `@Repository`, to let exceptions being thrown by the JPA persistence providers be converted into Spring's `DataAccessException` hierarchy. + +[[jpa.namespace.custom-namespace-attributes]] +=== Custom Namespace Attributes +Beyond the default attributes of the `repositories` element, the JPA namespace offers additional attributes to let you gain more detailed control over the setup of the repositories: + +.Custom JPA-specific attributes of the `repositories` element +[options = "autowidth"] +|=============== +|`entity-manager-factory-ref`|Explicitly wire the `EntityManagerFactory` to be used with the repositories being detected by the `repositories` element. Usually used if multiple `EntityManagerFactory` beans are used within the application. If not configured, Spring Data automatically looks up the `EntityManagerFactory` bean with the name `entityManagerFactory` in the `ApplicationContext`. +|`transaction-manager-ref`|Explicitly wire the `PlatformTransactionManager` to be used with the repositories being detected by the `repositories` element. Usually only necessary if multiple transaction managers or `EntityManagerFactory` beans have been configured. Default to a single defined `PlatformTransactionManager` inside the current `ApplicationContext`. +|=============== + +NOTE: Spring Data JPA requires a `PlatformTransactionManager` bean named `transactionManager` to be present if no explicit `transaction-manager-ref` is defined. + +[[jpa.bootstrap-mode]] +== Bootstrap Mode + +By default, Spring Data JPA repositories are default Spring beans. +They are singleton scoped and eagerly initialized. +During startup, they already interact with the JPA `EntityManager` for verification and metadata analysis purposes. +Spring Framework supports the initialization of the JPA `EntityManagerFactory` in a background thread because that process usually takes up a significant amount of startup time in a Spring application. +To make use of that background initialization effectively, we need to make sure that JPA repositories are initialized as late as possible. + +As of Spring Data JPA 2.1 you can now configure a `BootstrapMode` (either via the `@EnableJpaRepositories` annotation or the XML namespace) that takes the following values: + +* `DEFAULT` (default) -- Repositories are instantiated eagerly unless explicitly annotated with `@Lazy`. +The lazification only has effect if no client bean needs an instance of the repository as that will require the initialization of the repository bean. +* `LAZY` -- Implicitly declares all repository beans lazy and also causes lazy initialization proxies to be created to be injected into client beans. +That means, that repositories will not get instantiated if the client bean is simply storing the instance in a field and not making use of the repository during initialization. +Repository instances will be initialized and verified upon first interaction with the repository. +* `DEFERRED` -- Fundamentally the same mode of operation as `LAZY`, but triggering repository initialization in response to an `ContextRefreshedEvent` so that repositories are verified before the application has completely started. + +[[jpa.bootstrap-mode.recommendations]] +=== Recommendations + +If you're not using asynchronous JPA bootstrap stick with the default bootstrap mode. + +In case you bootstrap JPA asynchronously, `DEFERRED` is a reasonable default as it will make sure the Spring Data JPA bootstrap only waits for the `EntityManagerFactory` setup if that itself takes longer than initializing all other application components. +Still, it makes sure that repositories are properly initialized and validated before the application signals it's up. + +`LAZY` is a decent choice for testing scenarios and local development. +Once you are pretty sure that repositories can properly bootstrap, or in cases where you are testing other parts of the application, running verification for all repositories might unnecessarily increase the startup time. +The same applies to local development in which you only access parts of the application that might need to have a single repository initialized. + diff --git a/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc b/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc new file mode 100644 index 0000000000..98ba2bb2be --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc @@ -0,0 +1,62 @@ +[[jpd.misc.cdi-integration]] += CDI Integration + +Instances of the repository interfaces are usually created by a container, for which Spring is the most natural choice when working with Spring Data. Spring offers sophisticated support for creating bean instances, as documented in {spring-data-commons-docs-url}/repositories/create-instances.html[Creating Repository Instances]. As of version 1.1.0, Spring Data JPA ships with a custom CDI extension that allows using the repository abstraction in CDI environments. The extension is part of the JAR. To activate it, include the Spring Data JPA JAR on your classpath. + +You can now set up the infrastructure by implementing a CDI Producer for the `EntityManagerFactory` and `EntityManager`, as shown in the following example: + +[source, java] +---- +class EntityManagerFactoryProducer { + + @Produces + @ApplicationScoped + public EntityManagerFactory createEntityManagerFactory() { + return Persistence.createEntityManagerFactory("my-persistence-unit"); + } + + public void close(@Disposes EntityManagerFactory entityManagerFactory) { + entityManagerFactory.close(); + } + + @Produces + @RequestScoped + public EntityManager createEntityManager(EntityManagerFactory entityManagerFactory) { + return entityManagerFactory.createEntityManager(); + } + + public void close(@Disposes EntityManager entityManager) { + entityManager.close(); + } +} +---- + +The necessary setup can vary depending on the JavaEE environment. You may need to do nothing more than redeclare a `EntityManager` as a CDI bean, as follows: + +[source, java] +---- +class CdiConfig { + + @Produces + @RequestScoped + @PersistenceContext + public EntityManager entityManager; +} +---- + +In the preceding example, the container has to be capable of creating JPA `EntityManagers` itself. All the configuration does is re-export the JPA `EntityManager` as a CDI bean. + +The Spring Data JPA CDI extension picks up all available `EntityManager` instances as CDI beans and creates a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container. Thus, obtaining an instance of a Spring Data repository is a matter of declaring an `@Injected` property, as shown in the following example: + +[source, java] +---- +class RepositoryClient { + + @Inject + PersonRepository repository; + + public void businessMethod() { + List people = repository.findAll(); + } +} +---- diff --git a/src/main/antora/modules/ROOT/pages/jpa/locking.adoc b/src/main/antora/modules/ROOT/pages/jpa/locking.adoc new file mode 100644 index 0000000000..43e178f939 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/locking.adoc @@ -0,0 +1,32 @@ +[[locking]] += Locking + +To specify the lock mode to be used, you can use the `@Lock` annotation on query methods, as shown in the following example: + +.Defining lock metadata on query methods +==== +[source, java] +---- +interface UserRepository extends Repository { + + // Plain query method + @Lock(LockModeType.READ) + List findByLastname(String lastname); +} +---- +==== + +This method declaration causes the query being triggered to be equipped with a `LockModeType` of `READ`. You can also define locking for CRUD methods by redeclaring them in your repository interface and adding the `@Lock` annotation, as shown in the following example: + +.Defining lock metadata on CRUD methods +==== +[source, java] +---- +interface UserRepository extends Repository { + + // Redeclaration of a CRUD method + @Lock(LockModeType.READ) + List findAll(); +} +---- +==== diff --git a/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc b/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc new file mode 100644 index 0000000000..7cdb5feff4 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc @@ -0,0 +1,27 @@ +[[jpa.misc.jpa-context]] += Using `JpaContext` in Custom Implementations + +When working with multiple `EntityManager` instances and <>, you need to wire the correct `EntityManager` into the repository implementation class. You can do so by explicitly naming the `EntityManager` in the `@PersistenceContext` annotation or, if the `EntityManager` is `@Autowired`, by using `@Qualifier`. + +As of Spring Data JPA 1.9, Spring Data JPA includes a class called `JpaContext` that lets you obtain the `EntityManager` by managed domain class, assuming it is managed by only one of the `EntityManager` instances in the application. The following example shows how to use `JpaContext` in a custom repository: + +.Using `JpaContext` in a custom repository implementation +==== +[source, java] +---- +class UserRepositoryImpl implements UserRepositoryCustom { + + private final EntityManager em; + + @Autowired + public UserRepositoryImpl(JpaContext context) { + this.em = context.getEntityManagerByManagedType(User.class); + } + + … +} +---- +==== + +The advantage of this approach is that, if the domain type gets assigned to a different persistence unit, the repository does not have to be touched to alter the reference to the persistence unit. + diff --git a/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc b/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc new file mode 100644 index 0000000000..35ef016c2f --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc @@ -0,0 +1,41 @@ +[[jpa.misc.merging-persistence-units]] += Merging persistence units + +Spring supports having multiple persistence units. Sometimes, however, you might want to modularize your application but still make sure that all these modules run inside a single persistence unit. To enable that behavior, Spring Data JPA offers a `PersistenceUnitManager` implementation that automatically merges persistence units based on their name, as shown in the following example: + +.Using MergingPersistenceUnitmanager +==== +[source, xml] +---- + + + + + +---- +==== + +[[jpa.misc.entity-scanning]] +== Classpath Scanning for @Entity Classes and JPA Mapping Files + +A plain JPA setup requires all annotation-mapped entity classes to be listed in `orm.xml`. The same applies to XML mapping files. Spring Data JPA provides a `ClasspathScanningPersistenceUnitPostProcessor` that gets a base package configured and optionally takes a mapping filename pattern. It then scans the given package for classes annotated with `@Entity` or `@MappedSuperclass`, loads the configuration files that match the filename pattern, and hands them to the JPA configuration. The post-processor must be configured as follows: + +.Using ClasspathScanningPersistenceUnitPostProcessor +==== +[source, xml] +---- + + + + + + + + + + +---- +==== + +NOTE: As of Spring 3.1, a package to scan can be configured on the `LocalContainerEntityManagerFactoryBean` directly to enable classpath scanning for entity classes. See the link:{springJavadocUrl}org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setPackagesToScan(java.lang.String...)$$[JavaDoc] for details. + diff --git a/src/main/asciidoc/query-by-example.adoc b/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc similarity index 91% rename from src/main/asciidoc/query-by-example.adoc rename to src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc index c67368c6e5..2a7c661db4 100644 --- a/src/main/asciidoc/query-by-example.adoc +++ b/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc @@ -1,6 +1,8 @@ -[[query-by-example.running]] -== Running an Example += Query by Example + +Spring Data JPA leverages {spring-data-commons-docs-url}/query-by-example.html[Spring Data Commons support for Query by Example]. +[[query-by-example.running]] In Spring Data JPA, you can use Query by Example with Repositories, as shown in the following example: .Query by Example using a Repository diff --git a/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc new file mode 100644 index 0000000000..5385c6c063 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc @@ -0,0 +1,781 @@ +[[jpa.query-methods]] += Query Methods + +This section describes the various ways to create a query with Spring Data JPA. + +[[jpa.sample-app.finders.strategies]] +== Query Lookup Strategies + +The JPA module supports defining a query manually as a String or having it being derived from the method name. + +Derived queries with the predicates `IsStartingWith`, `StartingWith`, `StartsWith`, `IsEndingWith`, `EndingWith`, `EndsWith`, +`IsNotContaining`, `NotContaining`, `NotContains`, `IsContaining`, `Containing`, `Contains` the respective arguments for these queries will get sanitized. +This means if the arguments actually contain characters recognized by `LIKE` as wildcards these will get escaped so they match only as literals. +The escape character used can be configured by setting the `escapeCharacter` of the `@EnableJpaRepositories` annotation. +Compare with xref:jpa/query-methods.adoc#jpa.query.spel-expressions[Using SpEL Expressions]. + +[[jpa.query-methods.declared-queries]] +=== Declared Queries +Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention (see xref:jpa/query-methods.adoc#jpa.query-methods.named-queries[Using JPA Named Queries] for more information) or rather annotate your query method with `@Query` (see xref:jpa/query-methods.adoc#jpa.query-methods.at-query[Using `@Query`] for details). + +[[jpa.query-methods.query-creation]] +== Query Creation + +Generally, the query creation mechanism for JPA works as described in {spring-data-commons-docs-url}/repositories/query-methods.html[Query Methods]. The following example shows what a JPA query method translates into: + +.Query creation from method names +==== +---- +public interface UserRepository extends Repository { + + List findByEmailAddressAndLastname(String emailAddress, String lastname); +} +---- +We create a query using the JPA criteria API from this, but, essentially, this translates into the following query: `select u from User u where u.emailAddress = ?1 and u.lastname = ?2`. Spring Data JPA does a property check and traverses nested properties, as described in {spring-data-commons-docs-url}/repositories/query-methods-details.html#repositories.query-methods.query-property-expressions[Property Expressions]. +==== + +The following table describes the keywords supported for JPA and what a method containing that keyword translates to: + +.Supported keywords inside method names +[options = "header, autowidth"] +|=============== +|Keyword|Sample|JPQL snippet +|`Distinct`|`findDistinctByLastnameAndFirstname`|`select distinct ... where x.lastname = ?1 and x.firstname = ?2` +|`And`|`findByLastnameAndFirstname`|`… where x.lastname = ?1 and x.firstname = ?2` +|`Or`|`findByLastnameOrFirstname`|`… where x.lastname = ?1 or x.firstname = ?2` +|`Is`, `Equals`|`findByFirstname`,`findByFirstnameIs`,`findByFirstnameEquals`|`… where x.firstname = ?1` +|`Between`|`findByStartDateBetween`|`… where x.startDate between ?1 and ?2` +|`LessThan`|`findByAgeLessThan`|`… where x.age < ?1` +|`LessThanEqual`|`findByAgeLessThanEqual`|`… where x.age \<= ?1` +|`GreaterThan`|`findByAgeGreaterThan`|`… where x.age > ?1` +|`GreaterThanEqual`|`findByAgeGreaterThanEqual`|`… where x.age >= ?1` +|`After`|`findByStartDateAfter`|`… where x.startDate > ?1` +|`Before`|`findByStartDateBefore`|`… where x.startDate < ?1` +|`IsNull`, `Null`|`findByAge(Is)Null`|`… where x.age is null` +|`IsNotNull`, `NotNull`|`findByAge(Is)NotNull`|`… where x.age not null` +|`Like`|`findByFirstnameLike`|`… where x.firstname like ?1` +|`NotLike`|`findByFirstnameNotLike`|`… where x.firstname not like ?1` +|`StartingWith`|`findByFirstnameStartingWith`|`… where x.firstname like ?1` (parameter bound with appended `%`) +|`EndingWith`|`findByFirstnameEndingWith`|`… where x.firstname like ?1` (parameter bound with prepended `%`) +|`Containing`|`findByFirstnameContaining`|`… where x.firstname like ?1` (parameter bound wrapped in `%`) +|`OrderBy`|`findByAgeOrderByLastnameDesc`|`… where x.age = ?1 order by x.lastname desc` +|`Not`|`findByLastnameNot`|`… where x.lastname <> ?1` +|`In`|`findByAgeIn(Collection ages)`|`… where x.age in ?1` +|`NotIn`|`findByAgeNotIn(Collection ages)`|`… where x.age not in ?1` +|`True`|`findByActiveTrue()`|`… where x.active = true` +|`False`|`findByActiveFalse()`|`… where x.active = false` +|`IgnoreCase`|`findByFirstnameIgnoreCase`|`… where UPPER(x.firstname) = UPPER(?1)` +|=============== + +NOTE: `In` and `NotIn` also take any subclass of `Collection` as a parameter as well as arrays or varargs. For other syntactical versions of the same logical operator, check {spring-data-commons-docs-url}/repository-query-keywords-reference.html[Repository query keywords]. + +[WARNING] +==== +`DISTINCT` can be tricky and not always producing the results you expect. +For example, `select distinct u from User u` will produce a complete different result than `select distinct u.lastname from User u`. +In the first case, since you are including `User.id`, nothing will duplicated, hence you'll get the whole table, and it would be of `User` objects. + +However, that latter query would narrow the focus to just `User.lastname` and find all unique last names for that table. +This would also yield a `List` result set instead of a `List result set. + + +`countDistinctByLastname(String lastname)` can also produce unexpected results. +Spring Data JPA will derive `select count(distinct u.id) from User u where u.lastname = ?1`. +Again, since `u.id` won't hit any duplicates, this query will count up all the users that had the binding last name. +Which would the same as `countByLastname(String lastname)`! + +What is the point of this query anyway? To find the number of people with a given last name? To find the number of _distinct_ people with that binding last name? +To find the number of _distinct last names_? (That last one is an entirely different query!) +Using `distinct` sometimes requires writing the query by hand and using `@Query` to best capture the information you seek, since you also may be needing a projection +to capture the result set. +==== + +[[jpa.query-methods.named-queries.annotation-based-configuration]] +=== Annotation-based Configuration +Annotation-based configuration has the advantage of not needing another configuration file to be edited, lowering maintenance effort. You pay for that benefit by the need to recompile your domain class for every new query declaration. + +.Annotation-based named query configuration +==== +[source, java] +---- +@Entity +@NamedQuery(name = "User.findByEmailAddress", + query = "select u from User u where u.emailAddress = ?1") +public class User { + +} +---- +==== + +[[jpa.query-methods.named-queries]] +== Using JPA Named Queries + +NOTE: The examples use the `` element and `@NamedQuery` annotation. The queries for these configuration elements have to be defined in the JPA query language. Of course, you can use `` or `@NamedNativeQuery` too. These elements let you define the query in native SQL by losing the database platform independence. + +[[jpa.query-methods.named-queries.xml-named-query-definition]] +=== XML Named Query Definition +To use XML configuration, add the necessary `` element to the `orm.xml` JPA configuration file located in the `META-INF` folder of your classpath. Automatic invocation of named queries is enabled by using some defined naming convention. For more details, see below. + +.XML named query configuration +==== +[source, xml] +---- + + select u from User u where u.lastname = ?1 + +---- +==== + +The query has a special name that is used to resolve it at runtime. + +[[jpa.query-methods.named-queries.declaring-interfaces]] +=== Declaring Interfaces +To allow these named queries, specify the `UserRepositoryWithRewriter` as follows: + +.Query method declaration in UserRepository +==== +[source, java] +---- +public interface UserRepository extends JpaRepository { + + List findByLastname(String lastname); + + User findByEmailAddress(String emailAddress); +} +---- +==== + +Spring Data tries to resolve a call to these methods to a named query, starting with the simple name of the configured domain class, followed by the method name separated by a dot. +So the preceding example would use the named queries defined earlier instead of trying to create a query from the method name. + +[[jpa.query-methods.at-query]] +== Using `@Query` + +Using named queries to declare queries for entities is a valid approach and works fine for a small number of queries. As the queries themselves are tied to the Java method that runs them, you can actually bind them directly by using the Spring Data JPA `@Query` annotation rather than annotating them to the domain class. This frees the domain class from persistence specific information and co-locates the query to the repository interface. + +Queries annotated to the query method take precedence over queries defined using `@NamedQuery` or named queries declared in `orm.xml`. + +The following example shows a query created with the `@Query` annotation: + +.Declare query at the query method using `@Query` +==== +[source, java] +---- +public interface UserRepository extends JpaRepository { + + @Query("select u from User u where u.emailAddress = ?1") + User findByEmailAddress(String emailAddress); +} +---- +==== + +[[jpa.query-methods.query-rewriter]] +=== Applying a QueryRewriter + +Sometimes, no matter how many features you try to apply, it seems impossible to get Spring Data JPA to apply every thing +you'd like to a query before it is sent to the `EntityManager`. + +You have the ability to get your hands on the query, right before it's sent to the `EntityManager` and "rewrite" it. That is, +you can make any alterations at the last moment. + +.Declare a QueryRewriter using `@Query` +==== +[source, java] +---- +public interface MyRepository extends JpaRepository { + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", + nativeQuery = true, + queryRewriter = MyQueryRewriter.class) + List findByNativeQuery(String param); + + @Query(value = "select original_user_alias from User original_user_alias", + queryRewriter = MyQueryRewriter.class) + List findByNonNativeQuery(String param); +} +---- +==== + +This example shows both a native (pure SQL) rewriter as well as a JPQL query, both leveraging the same `QueryRewriter`. +In this scenario, Spring Data JPA will look for a bean registered in the application context of the corresponding type. + +You can write a query rewriter like this: + +.Example `QueryRewriter` +==== +[source, java] +---- +public class MyQueryRewriter implements QueryRewriter { + + @Override + public String rewrite(String query, Sort sort) { + return query.replaceAll("original_user_alias", "rewritten_user_alias"); + } +} +---- +==== + +You have to ensure your `QueryRewriter` is registered in the application context, whether it's by applying one of Spring Framework's +`@Component`-based annotations, or having it as part of a `@Bean` method inside an `@Configuration` class. + +Another option is to have the repository itself implement the interface. + +.Repository that provides the `QueryRewriter` +==== +[source, java] +---- +public interface MyRepository extends JpaRepository, QueryRewriter { + + @Query(value = "select original_user_alias.* from SD_USER original_user_alias", + nativeQuery = true, + queryRewriter = MyRepository.class) + List findByNativeQuery(String param); + + @Query(value = "select original_user_alias from User original_user_alias", + queryRewriter = MyRepository.class) + List findByNonNativeQuery(String param); + + @Override + default String rewrite(String query, Sort sort) { + return query.replaceAll("original_user_alias", "rewritten_user_alias"); + } +} +---- +==== + +Depending on what you're doing with your `QueryRewriter`, it may be advisable to have more than one, each registered with the +application context. + +NOTE: In a CDI-based environment, Spring Data JPA will search the `BeanManager` for instances of your implementation of +`QueryRewriter`. + + +[[jpa.query-methods.at-query.advanced-like]] +=== Using Advanced `LIKE` Expressions + +The query running mechanism for manually defined queries created with `@Query` allows the definition of advanced `LIKE` expressions inside the query definition, as shown in the following example: + +.Advanced `like` expressions in @Query +==== +[source, java] +---- +public interface UserRepository extends JpaRepository { + + @Query("select u from User u where u.firstname like %?1") + List findByFirstnameEndsWith(String firstname); +} +---- +==== + +In the preceding example, the `LIKE` delimiter character (`%`) is recognized, and the query is transformed into a valid JPQL query (removing the `%`). Upon running the query, the parameter passed to the method call gets augmented with the previously recognized `LIKE` pattern. + +[[jpa.query-methods.at-query.native]] +=== Native Queries + +The `@Query` annotation allows for running native queries by setting the `nativeQuery` flag to true, as shown in the following example: + +.Declare a native query at the query method using @Query +==== +[source, java] +---- +public interface UserRepository extends JpaRepository { + + @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true) + User findByEmailAddress(String emailAddress); +} +---- + +==== + +NOTE: Spring Data JPA does not currently support dynamic sorting for native queries, because it would have to manipulate the actual query declared, which it cannot do reliably for native SQL. You can, however, use native queries for pagination by specifying the count query yourself, as shown in the following example: + +.Declare native count queries for pagination at the query method by using `@Query` +==== +[source, java] +---- +public interface UserRepository extends JpaRepository { + + @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1", + countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1", + nativeQuery = true) + Page findByLastname(String lastname, Pageable pageable); +} +---- + +==== + +A similar approach also works with named native queries, by adding the `.count` suffix to a copy of your query. You probably need to register a result set mapping for your count query, though. + +[[jpa.query-methods.sorting]] +== Using Sort + +Sorting can be done by either providing a `PageRequest` or by using `Sort` directly. The properties actually used within the `Order` instances of `Sort` need to match your domain model, which means they need to resolve to either a property or an alias used within the query. The JPQL defines this as a state field path expression. + +NOTE: Using any non-referenceable path expression leads to an `Exception`. + +However, using `Sort` together with xref:jpa/query-methods.adoc#jpa.query-methods.at-query[`@Query`] lets you sneak in non-path-checked `Order` instances containing functions within the `ORDER BY` clause. This is possible because the `Order` is appended to the given query string. By default, Spring Data JPA rejects any `Order` instance containing function calls, but you can use `JpaSort.unsafe` to add potentially unsafe ordering. + +The following example uses `Sort` and `JpaSort`, including an unsafe option on `JpaSort`: + +.Using `Sort` and `JpaSort` +==== +[source, java] +---- +public interface UserRepository extends JpaRepository { + + @Query("select u from User u where u.lastname like ?1%") + List findByAndSort(String lastname, Sort sort); + + @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%") + List findByAsArrayAndSort(String lastname, Sort sort); +} + +repo.findByAndSort("lannister", Sort.by("firstname")); <1> +repo.findByAndSort("stark", Sort.by("LENGTH(firstname)")); <2> +repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); <3> +repo.findByAsArrayAndSort("bolton", Sort.by("fn_len")); <4> +---- + +<1> Valid `Sort` expression pointing to property in domain model. +<2> Invalid `Sort` containing function call. +Throws Exception. +<3> Valid `Sort` containing explicitly _unsafe_ `Order`. +<4> Valid `Sort` expression pointing to aliased function. +==== + +[[jpa.query-methods.scroll]] +== Scrolling Large Query Results + +When working with large data sets, <> can help to process those results efficiently without loading all results into memory. + +You have multiple options to consume large query results: + +1. <>. +You have learned in the previous chapter about `Pageable` and `PageRequest`. +2. <>. +This is a lighter variant than paging because it does not require the total result count. +3. <>. +This method avoids https://use-the-index-luke.com/no-offset[the shortcomings of offset-based result retrieval by leveraging database indexes]. + +Read more on <> for your particular arrangement. + +You can use the Scroll API with query methods, xref:jpa/query-by-example.adoc[Query-by-Example], and <>. + +NOTE: Scrolling with String-based query methods is not yet supported. +Scrolling is also not supported using stored `@Procedure` query methods. + +[[jpa.named-parameters]] +== Using Named Parameters + +By default, Spring Data JPA uses position-based parameter binding, as described in all the preceding examples. +This makes query methods a little error-prone when refactoring regarding the parameter position. +To solve this issue, you can use `@Param` annotation to give a method parameter a concrete name and bind the name in the query, as shown in the following example: + +.Using named parameters +==== +[source,java] +---- +public interface UserRepository extends JpaRepository { + + @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname") + User findByLastnameOrFirstname(@Param("lastname") String lastname, + @Param("firstname") String firstname); +} +---- +==== + +NOTE: The method parameters are switched according to their order in the defined query. + +NOTE: As of version 4, Spring fully supports Java 8’s parameter name discovery based on the `-parameters` compiler flag. By using this flag in your build as an alternative to debug information, you can omit the `@Param` annotation for named parameters. + +[[jpa.query.spel-expressions]] +== Using SpEL Expressions + +As of Spring Data JPA release 1.4, we support the usage of restricted SpEL template expressions in manually defined queries that are defined with `@Query`. Upon the query being run, these expressions are evaluated against a predefined set of variables. Spring Data JPA supports a variable called `entityName`. Its usage is `select x from #{#entityName} x`. It inserts the `entityName` of the domain type associated with the given repository. The `entityName` is resolved as follows: If the domain type has set the name property on the `@Entity` annotation, it is used. Otherwise, the simple class-name of the domain type is used. + +The following example demonstrates one use case for the `+#{#entityName}+` expression in a query string where you want to define a repository interface with a query method and a manually defined query: + +.Using SpEL expressions in repository query methods - entityName +==== +[source, java] +---- +@Entity +public class User { + + @Id + @GeneratedValue + Long id; + + String lastname; +} + +public interface UserRepository extends JpaRepository { + + @Query("select u from #{#entityName} u where u.lastname = ?1") + List findByLastname(String lastname); +} +---- +==== + +To avoid stating the actual entity name in the query string of a `@Query` annotation, you can use the `+#{#entityName}+` variable. + +NOTE: The `entityName` can be customized by using the `@Entity` annotation. Customizations in `orm.xml` are not supported for the SpEL expressions. + +Of course, you could have just used `User` in the query declaration directly, but that would require you to change the query as well. The reference to `#entityName` picks up potential future remappings of the `User` class to a different entity name (for example, by using `@Entity(name = "MyUser")`. + +Another use case for the `#{#entityName}` expression in a query string is if you want to define a generic repository interface with specialized repository interfaces for a concrete domain type. To not repeat the definition of custom query methods on the concrete interfaces, you can use the entity name expression in the query string of the `@Query` annotation in the generic repository interface, as shown in the following example: + +.Using SpEL expressions in repository query methods - entityName with inheritance +==== +[source, java] +---- +@MappedSuperclass +public abstract class AbstractMappedType { + … + String attribute +} + +@Entity +public class ConcreteType extends AbstractMappedType { … } + +@NoRepositoryBean +public interface MappedTypeRepository + extends Repository { + + @Query("select t from #{#entityName} t where t.attribute = ?1") + List findAllByAttribute(String attribute); +} + +public interface ConcreteRepository + extends MappedTypeRepository { … } +---- +==== + +In the preceding example, the `MappedTypeRepository` interface is the common parent interface for a few domain types extending `AbstractMappedType`. It also defines the generic `findAllByAttribute(…)` method, which can be used on instances of the specialized repository interfaces. If you now invoke `findByAllAttribute(…)` on `ConcreteRepository`, the query becomes `select t from ConcreteType t where t.attribute = ?1`. + +SpEL expressions to manipulate arguments may also be used to manipulate method arguments. +In these SpEL expressions the entity name is not available, but the arguments are. +They can be accessed by name or index as demonstrated in the following example. + +.Using SpEL expressions in repository query methods - accessing arguments. +==== +[source, java] +---- +@Query("select u from User u where u.firstname = ?1 and u.firstname=?#{[0]} and u.emailAddress = ?#{principal.emailAddress}") +List findByFirstnameAndCurrentUserWithCustomQuery(String firstname); +---- +==== + +For `like`-conditions one often wants to append `%` to the beginning or the end of a String valued parameter. +This can be done by appending or prefixing a bind parameter marker or a SpEL expression with `%`. +Again the following example demonstrates this. + +.Using SpEL expressions in repository query methods - wildcard shortcut. +==== +[source, java] +---- +@Query("select u from User u where u.lastname like %:#{[0]}% and u.lastname like %:lastname%") +List findByLastnameWithSpelExpression(@Param("lastname") String lastname); +---- +==== + +When using `like`-conditions with values that are coming from a not secure source the values should be sanitized so they can't contain any wildcards and thereby allow attackers to select more data than they should be able to. +For this purpose the `escape(String)` method is made available in the SpEL context. +It prefixes all instances of `_` and `%` in the first argument with the single character from the second argument. +In combination with the `escape` clause of the `like` expression available in JPQL and standard SQL this allows easy cleaning of bind parameters. + + +.Using SpEL expressions in repository query methods - sanitizing input values. +==== +[source, java] +---- +@Query("select u from User u where u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}") +List findContainingEscaped(String namePart); +---- +==== + +Given this method declaration in a repository interface `findContainingEscaped("Peter_")` will find `Peter_Parker` but not `Peter Parker`. +The escape character used can be configured by setting the `escapeCharacter` of the `@EnableJpaRepositories` annotation. +Note that the method `escape(String)` available in the SpEL context will only escape the SQL and JPQL standard wildcards `_` and `%`. +If the underlying database or the JPA implementation supports additional wildcards these will not get escaped. + +[[jpa.query.other-methods]] +== Other Methods + +Spring Data JPA offers many ways to build queries. +But sometimes, your query may simply be too complicated for the techniques offered. +In that situation, consider: + +* If you haven't already, simply write the query yourself using xref:jpa/query-methods.adoc#jpa.query-methods.at-query[`@Query`]. +* If that doesn't fit your needs, consider implementing a <>. This lets you register a method in your repository while leaving the implementation completely up to you. This gives you the ability to: +** Talk directly to the `EntityManager` (writing pure HQL/JPQL/EQL/native SQL or using the *Criteria API*) +** Leverage Spring Framework's `JdbcTemplate` (native SQL) +** Use another 3rd-party database toolkit. +* Another option is putting your query inside the database and then using either Spring Data JPA's xref:jpa/stored-procedures.adoc[`@StoredProcedure` annotation] or if it's a database function using the xref:jpa/query-methods.adoc#jpa.query-methods.at-query[`@Query` annotation] and invoking it with a `CALL`. + +These tactics may be most effective when you need maximum control of your query, while still letting Spring Data JPA provide resource management. + +[[jpa.modifying-queries]] +== Modifying Queries + +All the previous sections describe how to declare queries to access a given entity or collection of entities. +You can add custom modifying behavior by using the custom method facilities described in {spring-data-commons-docs-url}/repositories/custom-implementations.html[Custom Implementations for Spring Data Repositories]. +As this approach is feasible for comprehensive custom functionality, you can modify queries that only need parameter binding by annotating the query method with `@Modifying`, as shown in the following example: + +.Declaring manipulating queries +==== +[source, java] +---- +@Modifying +@Query("update User u set u.firstname = ?1 where u.lastname = ?2") +int setFixedFirstnameFor(String firstname, String lastname); +---- +==== + +Doing so triggers the query annotated to the method as an updating query instead of a selecting one. As the `EntityManager` might contain outdated entities after the execution of the modifying query, we do not automatically clear it (see the https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/entitymanager[JavaDoc] of `EntityManager.clear()` for details), since this effectively drops all non-flushed changes still pending in the `EntityManager`. +If you wish the `EntityManager` to be cleared automatically, you can set the `@Modifying` annotation's `clearAutomatically` attribute to `true`. + +The `@Modifying` annotation is only relevant in combination with the `@Query` annotation. +Derived query methods or custom methods do not require this annotation. + +[[jpa.modifying-queries.derived-delete]] +=== Derived Delete Queries +Spring Data JPA also supports derived delete queries that let you avoid having to declare the JPQL query explicitly, as shown in the following example: + +.Using a derived delete query +==== +[source, java] +---- +interface UserRepository extends Repository { + + void deleteByRoleId(long roleId); + + @Modifying + @Query("delete from User u where u.role.id = ?1") + void deleteInBulkByRoleId(long roleId); +} +---- +==== + +Although the `deleteByRoleId(…)` method looks like it basically produces the same result as the `deleteInBulkByRoleId(…)`, there is an important difference between the two method declarations in terms of the way they are run. +As the name suggests, the latter method issues a single JPQL query (the one defined in the annotation) against the database. +This means even currently loaded instances of `User` do not see lifecycle callbacks invoked. + +To make sure lifecycle queries are actually invoked, an invocation of `deleteByRoleId(…)` runs a query and then deletes the returned instances one by one, so that the persistence provider can actually invoke `@PreRemove` callbacks on those entities. + +In fact, a derived delete query is a shortcut for running the query and then calling `CrudRepository.delete(Iterable users)` on the result and keeping behavior in sync with the implementations of other `delete(…)` methods in `CrudRepository`. + +[[jpa.query-hints]] +== Applying Query Hints +To apply JPA query hints to the queries declared in your repository interface, you can use the `@QueryHints` annotation. It takes an array of JPA `@QueryHint` annotations plus a boolean flag to potentially disable the hints applied to the additional count query triggered when applying pagination, as shown in the following example: + +.Using QueryHints with a repository method +==== +[source, java] +---- +public interface UserRepository extends Repository { + + @QueryHints(value = { @QueryHint(name = "name", value = "value")}, + forCounting = false) + Page findByLastname(String lastname, Pageable pageable); +} +---- +==== +The preceding declaration would apply the configured `@QueryHint` for that actually query but omit applying it to the count query triggered to calculate the total number of pages. + +[[jpa.query-hints.comments]] +=== Adding Comments to Queries +Sometimes, you need to debug a query based upon database performance. +The query your database administrator shows you may look VERY different than what you wrote using `@Query`, or it may look +nothing like what you presume Spring Data JPA has generated regarding a custom finder or if you used query by example. + +To make this process easier, you can insert custom comments into almost any JPA operation, whether its a query or other operation +by applying the `@Meta` annotation. + +.Apply `@Meta` annotation to repository operations +==== +[source, java] +---- +public interface RoleRepository extends JpaRepository { + + @Meta(comment = "find roles by name") + List findByName(String name); + + @Override + @Meta(comment = "find roles using QBE") + List findAll(Example example); + + @Meta(comment = "count roles for a given name") + long countByName(String name); + + @Override + @Meta(comment = "exists based on QBE") + boolean exists(Example example); +} +---- +==== + +This sample repository has a mixture of custom finders as well as overriding the inherited operations from `JpaRepository`. +Either way, the `@Meta` annotation lets you add a `comment` that will be inserted into queries before they are sent to the database. + +It's also important to note that this feature isn't confined solely to queries. It extends to the `count` and `exists` operations. +And while not shown, it also extends to certain `delete` operations. + +IMPORTANT: While we have attempted to apply this feature everywhere possible, some operations of the underlying `EntityManager` don't support comments. For example, `entityManager.createQuery()` is clearly documented as supporting comments, but `entityManager.find()` operations do not. + +Neither JPQL logging nor SQL logging is a standard in JPA, so each provider requires custom configuration, as shown the sections below. + +[[activating-hibernate-comments]] +==== Activating Hibernate comments +To activate query comments in Hibernate, you must set `hibernate.use_sql_comments` to `true`. + +If you are using Java-based configuration settings, this can be done like this: + +.Java-based JPA configuration +==== +[source, java] +---- +@Bean +public Properties jpaProperties() { + + Properties properties = new Properties(); + properties.setProperty("hibernate.use_sql_comments", "true"); + return properties; +} +---- +==== + +If you have a `persistence.xml` file, you can apply it there: + +.`persistence.xml`-based configuration +==== +[source, xml] +---- + + + ...registered classes... + + + + + +---- +==== + +Finally, if you are using Spring Boot, then you can set it up inside your `application.properties` file: + +.Spring Boot property-based configuration +==== +---- +spring.jpa.properties.hibernate.use_sql_comments=true +---- +==== + +[[activating-eclipselink-comments]] +==== Activating EclipseLink comments +To activate query comments in EclipseLink, you must set `eclipselink.logging.level.sql` to `FINE`. + +If you are using Java-based configuration settings, this can be done like this: + +.Java-based JPA configuration +==== +[source, java] +---- +@Bean +public Properties jpaProperties() { + + Properties properties = new Properties(); + properties.setProperty("eclipselink.logging.level.sql", "FINE"); + return properties; +} +---- +==== + +If you have a `persistence.xml` file, you can apply it there: + +.`persistence.xml`-based configuration +==== +[source, xml] +---- + + + ...registered classes... + + + + + +---- +==== + +Finally, if you are using Spring Boot, then you can set it up inside your `application.properties` file: + +.Spring Boot property-based configuration +==== +---- +spring.jpa.properties.eclipselink.logging.level.sql=FINE +---- +==== + + +[[jpa.entity-graph]] +== Configuring Fetch- and LoadGraphs + +The JPA 2.1 specification introduced support for specifying Fetch- and LoadGraphs that we also support with the `@EntityGraph` annotation, which lets you reference a `@NamedEntityGraph` definition. You can use that annotation on an entity to configure the fetch plan of the resulting query. The type (`Fetch` or `Load`) of the fetching can be configured by using the `type` attribute on the `@EntityGraph` annotation. See the JPA 2.1 Spec 3.7.4 for further reference. + +The following example shows how to define a named entity graph on an entity: + +.Defining a named entity graph on an entity. +==== +[source, java] +---- +@Entity +@NamedEntityGraph(name = "GroupInfo.detail", + attributeNodes = @NamedAttributeNode("members")) +public class GroupInfo { + + // default fetch mode is lazy. + @ManyToMany + List members = new ArrayList(); + + … +} +---- +==== + +The following example shows how to reference a named entity graph on a repository query method: + +.Referencing a named entity graph definition on a repository query method. +==== +[source, java] +---- +public interface GroupRepository extends CrudRepository { + + @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD) + GroupInfo getByGroupName(String name); + +} +---- +==== + +It is also possible to define ad hoc entity graphs by using `@EntityGraph`. The provided `attributePaths` are translated into the according `EntityGraph` without needing to explicitly add `@NamedEntityGraph` to your domain types, as shown in the following example: + +.Using AD-HOC entity graph definition on an repository query method. +==== +[source, java] +---- +public interface GroupRepository extends CrudRepository { + + @EntityGraph(attributePaths = { "members" }) + GroupInfo getByGroupName(String name); + +} +---- +==== + +[[projections]] +== Projections + +Spring Data JPA supports {spring-data-commons-docs-url}/repository-projects.html[Spring Data Commons Projections]. + +NOTE: It is important to note that {spring-data-commons-docs-url}/repository-projects.html#projections.dtos[Class-based projections] with JPQL is limited to *constructor expressions* in your JPQL expression, e.g. `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. (Note the usage of a FQDN for the DTO type!) This JPQL expression can be used in `@Query` annotations as well where you define any named queries. And it's important to point out that class-based projections do not work with native queries AT ALL. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] \ No newline at end of file diff --git a/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc b/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc new file mode 100644 index 0000000000..e376e32afa --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc @@ -0,0 +1,99 @@ +[[specifications]] += Specifications + +JPA 2 introduces a criteria API that you can use to build queries programmatically. By writing a `criteria`, you define the where clause of a query for a domain class. Taking another step back, these criteria can be regarded as a predicate over the entity that is described by the JPA criteria API constraints. + +Spring Data JPA takes the concept of a specification from Eric Evans' book, "`Domain Driven Design`", following the same semantics and providing an API to define such specifications with the JPA criteria API. To support specifications, you can extend your repository interface with the `JpaSpecificationExecutor` interface, as follows: + +[source, java] +---- +public interface CustomerRepository extends CrudRepository, JpaSpecificationExecutor { + … +} +---- + +The additional interface has methods that let you run specifications in a variety of ways. For example, the `findAll` method returns all entities that match the specification, as shown in the following example: + +[source, java] +---- +List findAll(Specification spec); +---- + +The `Specification` interface is defined as follows: + +[source, java] +---- +public interface Specification { + Predicate toPredicate(Root root, CriteriaQuery query, + CriteriaBuilder builder); +} +---- + +Specifications can easily be used to build an extensible set of predicates on top of an entity that then can be combined and used with `JpaRepository` without the need to declare a query (method) for every needed combination, as shown in the following example: + +.Specifications for a Customer +==== +[source, java] +---- +public class CustomerSpecs { + + + public static Specification isLongTermCustomer() { + return (root, query, builder) -> { + LocalDate date = LocalDate.now().minusYears(2); + return builder.lessThan(root.get(Customer_.createdAt), date); + }; + } + + public static Specification hasSalesOfMoreThan(MonetaryAmount value) { + return (root, query, builder) -> { + // build query here + }; + } +} +---- +==== + +The `Customer_` type is a metamodel type generated using the JPA Metamodel generator (see the link:$$https://docs.jboss.org/hibernate/jpamodelgen/1.0/reference/en-US/html_single/#whatisit$$[Hibernate implementation's documentation for an example]). +So the expression, `Customer_.createdAt`, assumes the `Customer` has a `createdAt` attribute of type `Date`. +Besides that, we have expressed some criteria on a business requirement abstraction level and created executable `Specifications`. +So a client might use a `Specification` as follows: + +.Using a simple Specification +==== +[source, java] +---- +List customers = customerRepository.findAll(isLongTermCustomer()); +---- +==== + +Why not create a query for this kind of data access? Using a single `Specification` does not gain a lot of benefit over a plain query declaration. The power of specifications really shines when you combine them to create new `Specification` objects. You can achieve this through the default methods of `Specification` we provide to build expressions similar to the following: + +.Combined Specifications +==== +[source, java] +---- +MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR); +List customers = customerRepository.findAll( + isLongTermCustomer().or(hasSalesOfMoreThan(amount))); +---- + +`Specification` offers some "`glue-code`" default methods to chain and combine `Specification` instances. These methods let you extend your data access layer by creating new `Specification` implementations and combining them with already existing implementations. +==== + +And with JPA 2.1, the `CriteriaBuilder` API introduced `CriteriaDelete`. This is provided through `JpaSpecificationExecutor`'s `delete(Specification)` API. + +.Using a `Specification` to delete entries. +==== +[source, java] +---- +Specification ageLessThan18 = (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), 18) + +userRepository.delete(ageLessThan18); +---- +The `Specification` builds up a criteria where the `age` field (cast as an integer) is less than `18`. +Passed on to the `userRepository`, it will use JPA's `CriteriaDelete` feature to generate the right `DELETE` operation. +It then returns the number of entities deleted. +==== + + diff --git a/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc b/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc new file mode 100644 index 0000000000..8985be4515 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc @@ -0,0 +1,97 @@ +[[jpa.stored-procedures]] += Stored Procedures + +The JPA 2.1 specification introduced support for calling stored procedures by using the JPA criteria query API. +We Introduced the `@Procedure` annotation for declaring stored procedure metadata on a repository method. + +The examples to follow use the following stored procedure: + +.The definition of the `plus1inout` procedure in HSQL DB. +==== +[source, sql] +---- +/; +DROP procedure IF EXISTS plus1inout +/; +CREATE procedure plus1inout (IN arg int, OUT res int) +BEGIN ATOMIC + set res = arg + 1; +END +/; +---- +==== + +Metadata for stored procedures can be configured by using the `NamedStoredProcedureQuery` annotation on an entity type. + +[[jpa.stored-procedure-entity-metadata]] +.StoredProcedure metadata definitions on an entity. +==== +[source, java] +---- +@Entity +@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = { + @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }) +public class User {} +---- +==== + +Note that `@NamedStoredProcedureQuery` has two different names for the stored procedure. +`name` is the name JPA uses. `procedureName` is the name the stored procedure has in the database. + +You can reference stored procedures from a repository method in multiple ways. +The stored procedure to be called can either be defined directly by using the `value` or `procedureName` attribute of the `@Procedure` annotation. +This refers directly to the stored procedure in the database and ignores any configuration via `@NamedStoredProcedureQuery`. + +Alternatively you may specify the `@NamedStoredProcedureQuery.name` attribute as the `@Procedure.name` attribute. +If neither `value`, `procedureName` nor `name` is configured, the name of the repository method is used as the `name` attribute. + +The following example shows how to reference an explicitly mapped procedure: + +[[jpa.stored-procedure-reference]] +.Referencing explicitly mapped procedure with name "plus1inout" in database. +==== +[source, java] +---- +@Procedure("plus1inout") +Integer explicitlyNamedPlus1inout(Integer arg); +---- +==== + +The following example is equivalent to the previous one but uses the `procedureName` alias: + +.Referencing implicitly mapped procedure with name "plus1inout" in database via `procedureName` alias. +==== +[source, java] +---- +@Procedure(procedureName = "plus1inout") +Integer callPlus1InOut(Integer arg); +---- +==== + +The following is again equivalent to the previous two but using the method name instead of an explicite annotation attribute. + +.Referencing implicitly mapped named stored procedure "User.plus1" in `EntityManager` by using the method name. +==== +[source, java] +---- +@Procedure +Integer plus1inout(@Param("arg") Integer arg); +---- +==== + +The following example shows how to reference a stored procedure by referencing the `@NamedStoredProcedureQuery.name` attribute. + +.Referencing explicitly mapped named stored procedure "User.plus1IO" in `EntityManager`. +==== +[source, java] +---- +@Procedure(name = "User.plus1IO") +Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg); +---- +==== + +If the stored procedure getting called has a single out parameter that parameter may be returned as the return value of the method. +If there are multiple out parameters specified in a `@NamedStoredProcedureQuery` annotation those can be returned as a `Map` with the key being the parameter name given in the `@NamedStoredProcedureQuery` annotation. + + diff --git a/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc b/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc new file mode 100644 index 0000000000..f4abfccdd3 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc @@ -0,0 +1,91 @@ +[[transactions]] += Transactionality + +By default, methods inherited from `CrudRepository` inherit the transactional configuration from link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html$$[`SimpleJpaRepository`]. +For read operations, the transaction configuration `readOnly` flag is set to `true`. +All others are configured with a plain `@Transactional` so that default transaction configuration applies. +Repository methods that are backed by transactional repository fragments inherit the transactional attributes from the actual fragment method. + +If you need to tweak transaction configuration for one of the methods declared in a repository, redeclare the method in your repository interface, as follows: + +.Custom transaction configuration for CRUD +==== +[source, java] +---- +public interface UserRepository extends CrudRepository { + + @Override + @Transactional(timeout = 10) + public List findAll(); + + // Further query method declarations +} +---- +Doing so causes the `findAll()` method to run with a timeout of 10 seconds and without the `readOnly` flag. +==== + +Another way to alter transactional behaviour is to use a facade or service implementation that (typically) covers more than one repository. Its purpose is to define transactional boundaries for non-CRUD operations. The following example shows how to use such a facade for more than one repository: + +.Using a facade to define transactions for multiple repository calls +==== +[source, java] +---- +@Service +public class UserManagementImpl implements UserManagement { + + private final UserRepository userRepository; + private final RoleRepository roleRepository; + + public UserManagementImpl(UserRepository userRepository, + RoleRepository roleRepository) { + this.userRepository = userRepository; + this.roleRepository = roleRepository; + } + + @Transactional + public void addRoleToAllUsers(String roleName) { + + Role role = roleRepository.findByName(roleName); + + for (User user : userRepository.findAll()) { + user.addRole(role); + userRepository.save(user); + } + } +} +---- +This example causes call to `addRoleToAllUsers(…)` to run inside a transaction (participating in an existing one or creating a new one if none are already running). The transaction configuration at the repositories is then neglected, as the outer transaction configuration determines the actual one used. Note that you must activate `` or use `@EnableTransactionManagement` explicitly to get annotation-based configuration of facades to work. +This example assumes you use component scanning. + +Note that the call to `save` is not strictly necessary from a JPA point of view, but should still be there in order to stay consistent to the repository abstraction offered by Spring Data. +==== + +[[transactional-query-methods]] +== Transactional query methods + +Declared query methods (including default methods) do not get any transaction configuration applied by default. +To run those methods transactionally, use `@Transactional` at the repository interface you define, as shown in the following example: + +.Using @Transactional at query methods +==== +[source, java] +---- +@Transactional(readOnly = true) +interface UserRepository extends JpaRepository { + + List findByLastname(String lastname); + + @Modifying + @Transactional + @Query("delete from User u where u.active = false") + void deleteInactiveUsers(); +} +---- +Typically, you want the `readOnly` flag to be set to `true`, as most of the query methods only read data. In contrast to that, `deleteInactiveUsers()` makes use of the `@Modifying` annotation and overrides the transaction configuration. Thus, the method runs with the `readOnly` flag set to `false`. +==== + +[NOTE] +==== +You can use transactions for read-only queries and mark them as such by setting the `readOnly` flag. Doing so does not, however, act as a check that you do not trigger a manipulating query (although some databases reject `INSERT` and `UPDATE` statements inside a read-only transaction). The `readOnly` flag is instead propagated as a hint to the underlying JDBC driver for performance optimizations. Furthermore, Spring performs some optimizations on the underlying JPA provider. For example, when used with Hibernate, the flush mode is set to `NEVER` when you configure a transaction as `readOnly`, which causes Hibernate to skip dirty checks (a noticeable improvement on large object trees). +==== + diff --git a/src/main/asciidoc/images/epub-cover.png b/src/main/asciidoc/images/epub-cover.png deleted file mode 100644 index bd31c43faee2ac3947b404ec9fe134a628211e8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50064 zcmdqJg;$jA7d1>tBSUx0P!iHeGdeVq0)o=rEig37P|_vR5|T;?2uOFAfOMC1N!NRi zkH7a_>-!VF#RAsN%-nIV>(oAb6Rxf*Pk>8}i-Lkepr|180tE$w8u^Wl3H)Vmj>ZW1 z#b~DRLInlIn-v8m5P^bn1^g-SHwub7FAB=GF$#*R?k$Pp`cVm z`(A4#0LQ3qFXUk;r9-q^zyYS|b9ot*d*s)r#scKaIL->M+)z*+b|Ak|lQPVu2;>%%DP)LimF|v!1*0|vY!%Rpy1a2kNJSRnGuf9~C8QR?kNHWCSE~Qrw&+MF7 zeZBQ+mO6k!ug+$j9)l4e81~;E(AsAPjQ{tjId9{hHzSM11vxXIM+h^HTYEiKTJc*&PPg5%cg9uc*ZTtqvQrU`JT`7?!}XOt8Q- z6`T{XngUW(H57RrG1;ni<}qdd%ItzJ6QteutEc#Ito~ew{Uqb!Qyl5P)s17oA<#gJ z3EoX`dK0N*(n#|n>nBh2Hm9}X5*rGa^{{_SD~Rc1j1Dj5@q zQu#ADSP~I$FV^6TUv`zmPyW5=m%|rV(f?iF6`T#mSP(gT?G!A3A#Hg2-ma|b=R^%y zM2lTd;CUnDpC5$BATTKydF_2YFQ>Fr_bJ&AaZ?&s>%J1x{(A#N^x|l$!fXN$6Fcr> zv(NFi0mI#85vLAMl6s6xv|I9!)`E|(-9fs*^dPXZDP%je51B7~PkV}qwEGk;{ZelT|S+2PsuX6`ooP8nssy0#aU4sT?1Vh zjp9wQJg>^i)*$+~#OIIRKp1vS6V{GS(@%pYXexql2((VD-JEJF2KQ|!+|8gMj-cOK z$jv1L`S+RShhBm=JZmN&>)meTwBy4VO7CvdazfRb=UK1UGF5BlUo1OUacJ>UoY4<& zciuO_Q4{>LxpOx`(E@5#fY*=XFO%{XkLQJV)`90In@kLY79Kxq>(M?K z$a*`em~=Y$U344H3GzxZF^t7ZEP zV+%Xdkquva3kuydqPgiOo{n%3pTYRtjKN(7JN z<(bR$TqZi_Pu+6=RHeTVo-l}7fmN#dge`EhOwX3BB}ND zb#?pEapd|nd?;P?R;7CjdZi(tf(`f$TI?KQ+%p`8gtW%)Z{8Uq0aZ`JA<|?Rh2|C8 zPoFu39M{a*WZbOeG9bcM%TCYQ?I0SpuIoyp$%fuV2LmYrSgVboXz;+!;d1+g!+YuBObwOR}_tz=NHd|1w3X!=KK+3YE5X}`UkzHC3^d*^5y-9 zbYfq6dwP1>XcU>$N8s(BwA5tet)0C;1VaR6R_}0HZVF#jZnXaTs^_#qZMNPSTDvm7 zkB;?l{B^ahHrt>e7y(47>|%`idFqXEl$fMfWidnBW6m<&>cubYCG^XW6<+ri|GHN^ za8L=jokrX2638DPw1{E#fkN<=Q~1}ZU`#ip+`6En?b(`x=b8noblhgTVZWtoNeq=5 zYA27Je@~Q5wh=MODXY4xVO^Q`#Dd4J|7-~`FzHmveta6aQf<-y;X`{UVb=RtDc7~0 z@G!#BhUhxCO{w)av^?xj@JNRqA;KOgrs2nLqY_iSAjAlo7SEhjdW1+jPw)YYILVjT z8hm;Gx#TrV#0QSUh24bS4AOXmSc%Xc~KcD=o%qZXhSol=ymtpF!zT3a55p&;eko5lZ`$(tEFshH0 z9vn5->{zP8#Egg?SwgRE9rpIqA}{?zy)bUE)%1!d;ij_u{6RC8Q%Ldp_Qq|q-1LtJ z!6BOU2`{am>RQiKZFzWDdSDUFq6e*Dv|VVp{1t>n`0LGy|4N+;AQ7Y{ov-#+G?Mq&+CGgZMI&&iQo;c1;VUBXgcS+KRd8~Xi zXd-K%e=(t_h)tvyFSm>zhF{DDJ|;P|$0=2K))X9d@R%m;zEOqa`v6`FHV8XQ#B=@R zARrl;+ORkzs~A(R!alCG5lcb1K;2QbORPacii}6my5G!uN$Nch9Id?>@c24Hc6WE* z#E;u_;krmN+vj;de-v<1(|o(v4QFTkeZ&KUbcLrice2n_m;k06Ob~UFW|r=dxoPnv z4_9lyz2<4YIkrRyW3V^P&LKD%pQuw=PnDYt%jc-T(Pwa|Bsg~^NECOK>2U@zfxRCB z0uN;}2Lg|wY>H0Cj`@TxiwHz>_@)PH1R|YTm^}H>%;Ia>ee@WLO~4la+?rACou(o$ zzg_cT8(z@iOKcVa$ya+`1hgW~C2!zHL-HM!u;um2u6x27h0ceZ^ZiI18J&Fx!A}%Wmk=K7i zX!ER~&)F`jM{~h95-&LrWzd1uzE{!@Qka5uY!9O^YZ;{TZ1hzaffMcQ%=M4h@YMUs zA6s2|W!Lpp--Ph62}G?3EtoZ%%ll3EpY+HLN3-60Uq&KBrO)+V%}$lWtVa;LM3DI# zAUI}e0vob2^=ohtO2#R6B}}!ZttitR{S5zF1_mh3SHC zevjC|?>-XKFu@p~Gyu_G?fUZ4H)h6UkqWVxe2@$#QS=l@ zKPu0#uIuIAd%~GJf@YqVc~j?5fyqN#g^;@z0#fO=eM%F2nkV+wJKE+wPD{>GnhxF_ z50&<8KHw73^%TH*e&)tVUA-T>&J4J_G${r#b(|jhefK)qc&Wy8Ri?GhNlldv}Bex zDql5ujS>YU4%%!&e}8&Y^i3f;qgrPZ9*Zt=h&RYm+8P6_nt$%BCUw1AnJ}&g(HKaXHqYNxty^;h+jjH z(xWzrdy03H?za+^cW5p0=C?XQ!qHnhcV?-ClNAqJ&q#fO;-_GlKVZTL%^Z>V{vy(^ zpfgbP(+8|$M=EJgs?T;R6~=9km8{3|2j%Wq5bqk4JPuO$E&2qA{{(}Gsd0&@v^X2(Dv>n&j!Yet~SFWj<)sYN5gF zHa-4VoV;u!GeXj!-owCAY{Tj|k>VqVN%m^HgJFj4DIhi{c^ojY;a$EGG=*W~5Ih-J zl(eY^o{j97UqY)fV&{DFk!C^5@H89AVo9sk1ISMcR`hyq55P$nSI(c*i-vJEtQ0lS zaaZV*)CsKk?PHWCCEL);(p){}&7Y~jquck1|CPd_Y|N!G!xKLlnC4|{$kNxiH0hU! zyQgAF{lva@c59kc{{dvqD%~Jn9-I77vCaO9n@w4Ri4_rsiBnCP%WUFYjRJ;!zIp6P zo|?QYmYqF!`zu?(>7G$AeP+I5T5`{l+3a4%yCR|+qbo9l@Ap}y3LdwI`@2fp$`+=r z^;97{F;Hr2wCbUin+kKc-CXUFs=jLD`^S!DF*_dxtW*QyebxzSmQU*FZJSr)X?!Iq z&GU8j;;+d(ZnOU|FtRR5sWH4@9UP(wnLk99DA}Y;ew6HGA8#g`-8P51P|-!;V6|u> z^7~yuSA$ke!CME4;vkTv((BY+@L3}H3}{Cr#lHs%T4+;+J#a|+Z7+Vzw@HD<~b)!LRWrAS$gpH?CMmRI4+oP z)q>d2%hF4wRM}KT+K#WBgNu6ZBR+<9zb;di!064%_ybMbPY{CRF7?g}T4&5N8IQ-j_@CZ9vEUVTgS zATb*_CH>Hw@#J{Ln|G>^Ckq-)ebmcM8~81|r~&0*tYFwTVWh?P4@A&82Jjb$M}7=S zXFidFhLqb{1ZfS`zyEhHH`(M=&n3=g%$(!nrGNBC=CI||*3@aA5 z_bGYhJK)WbwDLo)Sd0MMnKab^OkHpIJ7;4z(*?H!3;dn z=yU!#|2QLV{?UrZ@k34Q^OR>Wr zb?BSQ|B5LmFOB+^M){oi+wOe*`8)Ww^i0IlFx_Q&8|i(vp-xF&|C@_Z31~anGud4k zY@Pt8558;6GwFzpDWo`4{_Ys@= zECGTVtKZ*Qa92X76GptK(@m9Lwao7QWlC8a3Y;wmoi0$0Ed$;BEL7*gYb<#K!_7gVyS&MTe%A zlf@ONW{5eNF<)nh2l-@pjg}cTdma4@kPrrvaFtzGBvp!k7=G*UL#3$Hh7=il7jHxF z(_Ma+w`&cmWM+rz{DYd%1T9>!3=Eeb44=b{-Sjd4(?Q$^4xiJ34GNSzqCt>6Gy=AS|=zV&Y5}k5P@e!Zr>PPtqOBv&P;ul1OA*>Me z6(wdURx20%45vRz?);yDH^mz(XH>RV4WA{osn>4SDB4V z0-=||T%t`sIG*VDf@1aQfA~?iJ1Vo6)f$8~{APxr3-%H2v6h#>E1Q$kT<52PEu!wN+447ezA6!>?8Jf`yIxR(^s|Gs8W8 ztYXZd+Q3eL+%SZ!GEG4-!f(8E;p)8=be zMq>-<#YbDVq&)S!-bo#d7mabSy$u%)u6cM)c5h|)AQI9QI}D?I-Q?>v+14St;c$O< zyZJGaBc22E{-MU8;{q|ah3ByL*Vm_EV?v~?g`-lLpP7}{Zw7Sg&*I-y^9~aoM>v-% zuo{r<^R26)lr7Q>XeJ0x2BM-{ir+qVi@dZK&VoCh*q?DAu)#8II0VydXK#EZt!|A8 z;=-`vz)w&4-M{)3`0JLv*x-*VQ7fP2LzZ0mQ)<{9SSO|~krhKZbY>mHG6DX^Z2ZQB zB*{)cUe2`QQD|z`qWA@l{#o!jnBTE5aY`1V)p0M4GcX^DA{r>=3n7zJFEeZ;mn*s7 zZBzN3R-{?*(5k}Ls>5Wr99s_5G9|zgmABWCTyHQAK>@3ZP&r^zXeE}DeE6+th^-8_ z{zy#we#hnbjVq_%Qn0D(pe4PFn%LfV{*Z=42?!MD96hJ7S1<7TgTdkr4)jF&Si$fz zAoR!r8Je(#4$$9yNT7tF&Amm(8sguR!m3Gb%5BWI5_pGsZr8A0pdgD&!Y%H(-|(~l zQqkO&m}oAHa=qjLRX#-nqFl;uqzpN=?phg8 zx#I0NIqJ{4yv^h_X)A15e32a(%qRYaGUw=feb+lWrfHQnPICKtk3Cy+*HgI;##Wl% z&9=r&<6KPXk6d>iU7juQ8iBmr2Y#FRSD33qrb(iKjAm91741=ca%lX*St>p6d)pDB zHMSGQs*W2p;_Jwwu>Ya6@jkBAZygL?@zJnNjMWuEHW5eH{*41l@d62u>N#7s2C~7j zI;WS=Rfb9DRg@SL8zZZlce2rKPnU8V!?NE2?-ta9p!UY*WrNB_< z-;ekXgWg%5(+$(c1DJ0ZsY^=kGU4^zBHFqI=!lj7sdGwydS!S<0qWt4sRl2%67_ku zSH=}Pa_|Qh)cuLN`vdt&*2|I-NV7DuDmLk8I zV4UIH+i&;Vsu+FQ=T6EsF3M;FrM76i(0frt}bx+DdjEa|-H%Kj)Qo9Iah@l&j z*{?mC<+13ahwPib$hhAl4Ad(fap2%YaB4-nf|)9dJh6S*?XN@Pm-D-D*BVg_x$SBix0jq zX^#*Kca>-?SR6!WI<9LzL6%Ih@P-&uw+CeTYys8dx3h(n6_og0xTb+i&F#}CPW9fW zaEI~>yve1`sZ(#iID|*aoc;XIPtSrRSv~b5pA|TR?uLeR&6OLl8Bwii3^`SC)PsfF zBYoI}g@p+wT^sP>N7sLIzmeN`@8KCl?s9rTJ*w4th6c}sWwfQAd)yXk2#$)hWKvV{M>@{$bppMrRv=_Z9%S!YTJFW`WPSb6;^EDQSh-1yn+Nd#fqEKnJ&~Ibd zsZ4We(>%Rq*BA7BFrpkwKZp}aj1?zj(#=pM%^$=XJV3=53m-k$TRr8VOyuWY-W&S5T8Y8lfLwk()!`C_hT;R zWQoee)yMC2LPrnbvj86^QqPr(k~SP(PE;r_7d7cCj}}?dp;5`EgSdkDiONJ3#|@V$ z;MDeuO^rp92l7hQD_}FvNY|w0^n{LKRxp)GJNqJl$|9uo;ez%O4fbDPtTK;497hU` z0RsY8X07v|i(l1kM&AIvjbx(6XZyh~<_DfIIyjL}Un#a6n4SciaU z02Y|$Gp}M|a=^m+gc5_SKEsJ8lg`G*rtV0p%W#-zUxfI?-u1zGzqOOojzZ=j;WwU7EkI7bGMR1CyNB1Ll zDxjwa!8)o(oL6o_tBrmkHwS-y@c3|p! zNh72~I1KGyuZW7{RQxG7Fkr01*j=2j+=vgq;IDZq1fQ|?donJHBA8=6yUWVan~ao) zS<5c{dSW^%$A|RLMze;`G`~;)El?PH$wEq5EY9*!EzhfoZIAnwFr6f4i@*;hdP`R8 z6ql_do3aD^cvkv+>=~U8l}zv~^=0*{tdDGKg*M+Be72^Xr+n7X*87QpfN2nKIYjc} zZLcQ6|Lpq5iXW|MRDyhvky)#iv98%1qy66^3(S4UZ!xQe$p;?kGV6}k&dIomuH(@F zOD(@@6$m?^kiZ3f>TO@LTKIS~0Mzq=Hr!Q?^WF)QKIk#nn%@j1*s){A3-Q3jhSrRw z2AHXj@Ea&!zudBx)M|XLZ=iFjw_l~|f28sDG97oB#L>hPOIX>EE6)l5vOg-3t43^% z=E}#g33${R;`r0#sTj^>_he|<`8{5##i6A;3pe{HFC!%NIWL!cti=89iafv zHqri0{1n&9HnGUe{mbo6?V&05#{LU!JX`|y_yRDkvu40iCq-6QXO-1(W`uG!)~@Z! z^wae)XQ@pq==HSK_FhM4;*GKlMM3=cWo$u&CKy8eFc$Y#zKMD!Eh#A}dzu-BYQpgW zjW%mg2xOW)3WC8|EVbDlS$Q3|jlQNvGm$rx4VTOP(xf8a{kaFl#sY7qk8LR6ldD&7 zVExv{V49w2lCai_0N6Alff1xMLE08k$T3!|T`~wX5OC1)nS+ihKmDi1`Fc;IzS2qt zh;&1Z&V`zU@4ZU8h)bS?69VK9s>JH;@=T7=K94ZQPOyHl1>nlIr|+-rZZG%Tac?5! zr6K2jwW3(CU^>B_qF%22J?R$~1Pt(o_jE%0czD&^<7A@yuX ze57ItXBD9mcbKV~{@rEV7SZ~3l&)t%O_rQ^FYYw+p2T(C(=l3sDy(SSkcbMKCXxa* z@kKH*H3EdgE@HoL@(5za}J*@q^ z!))p_OWI6xK!BDsR18_Wt{Ez?R5>Fso6T-i#J3EIS8xPW!9v*R{vR;4rLcWKRLWfs zL(0?mD1{y8C<};zvKxUB`;4KLZ<&maww$-1)^k#1=`$hLXoH^O899HG0!>hfa z@OW6@VrsP*BSFKema7}9v?mnoBN7}%8j_$8$FctYvXn3U#+L%3E4y9EF6uz7)AXDu z`Hwj%j2SV)=oB)ZE0-envg}&m>!$zd3qzK08O1-8xa&@Jl}pJdTR|=k9pyAmGwf}_ z{I6e?O(N@68FTOVBmy7Yx6R0Y_(qxfwINhG0QxP>g6bUnz zB5VT6aFuQfZvCl#VptfQf|@(v)*>89Aka!?gTi^L%6>`mNaQ$;jUq90R(GHN`PUUK z!))9g0T+mbYzY8iqG;fvoevzfCdNo7mmC#@B=Z<8OzfXy3mF<@2UBU%qewUu9_*jq zzy)N)k8B#G+3Ug5AGo1Jn<|2WXP-zdTM6kH-_5gT#^R0xwvX4yC{nfTZ3w)aUu2%QNl(-> ztH5PUVnN>mM-C?mR~EutT$9a@=8%Y2+kMukZ=W)laT@@0Azad?_yC z79k5R{3}`Tm2j5PT1?kf5ihRgiBEa4u&`P(Y6Y~S_z&R(^my2K%x`uVtrLZA9eA&Mv*L9CFX_+e; z#I9W0=XZVP$NqX9s!2r*OC|nf@=*}TLOzf78wSufq($mWdoj`GILjK2tuw^j7xLAV zy%YlLq5;Qut6X)Mp<=Pg(JXwM=0mN~p~izY*4W^**yPJoFKLGzFGls6x-;#>Pgz z$sG8ij_4uZry9rk8Hq&VAPEXk_-f(=Kd?AlkHhxGG$>NdV;F6Hx**Oho zN>XSn@Q-I)DpXGrPFV+Z(fAOrdq6Eba6+Ia(F` z4-lBdu&(+M6a;%DkdAW3hF%!+MW7Bvbc)swaw#c1>@t01EOIDC9@8C7S0Yo2Vdyv) zD~g2~wz$VMj21$MhFUMpijF1yHn=RIULe9M!31)HWfCgYtt)^dooL1!lHoP??FV=yl)>$l=4D!C=W>6`bq%H#zxGLo-tTGh;bI0h4p zVl1+;9YiA2peFzUR)~O%Fk)WCg^hqC zE=kK^55Q|{Rq^Vw9ksj1jK{<5zKghcP{9Vpwhw=E%SHe5=TFz^i5N>|-g<-O{qzu9 znQyt|#m{h4Qc?tZ=(1nY)5rm{aj^Q#&lrSxdQ65m4#8y5(Xjsc%D1SvD(lglOn_`N zV^7V~p+t;&j0>bF&1G<4K~Tmr-Fqy&OkV6*$4xR!!8V=4lkH9lBj3~paK05=v%E2% z_BWR?LyzXY89QPT2#z5E;2gGe;XYyC^V>>A-C6|?(u<$U5Mw;XcsN$OGO?sX&jOaQ z1s$2kidsFiC``k9k9*oD^qs!GZ1G%?EcD(rU#R?&c}J((d!0o`#yGYoRu{u3kr}og zZXD*m*yQIkX8rhB_lu>KRlzkoKZZ<#@%e@`b}hXA@g5rl%TO3Ok8yH!_Y}{hL?Fw@ zuSZbU)|RYV9C2K{3JA z@oC#{fVYi4k$HN2m)F)9Dq{YFt!JI6|2@r|j(*VAl`e&|(+&*~2i>y4*XFjYZwELZ zy!u|M-Rz3TqN@z<`RsDJy?zkD>I)$vhRsk}!i5h_pUHd?yq~Gt3o#ux-i>?2{aUd} zVU655inYIP_5lwByR>BHG#Wa^*Hw=iLl)WVF#BDI=~oH`o*4$lf^@oY{lpqd%M$)s zDmb#MH&7{+=fciS1hl9YFhaP-(z41(O|@6I$(M@l)4C^?*hi%=AUC|!8e}zt<6jlw z5o^YBK`9DJk8j32CqhH*600M}^%>G=Y=0BEGhOfLua^ZsPk!~Lp)yH*@N2blBhUg>h^rPpTvjZC;NpNA z{?b*SxDt*vE5G(TOlYKg(b@CGFu(M4Npg>wJMU5DcPTwausM>`XXBw(Pu2TVihPiZ zS2s*k8=@}8W4yF3j50;EY!pfD@%)yYf|om0*r{n^e+n5=bjpc3g}#}2Ew#LTtsh`q z6Jm#7%VGv%&-Jif5)9xrYSQOQ=?H+WP%58Tgqzzk;Mp7Qp0UkTSAJq)Ls%A} zB9w)&SLgGLZ^1{R zK1T@ct6#Wx!%?4zlVM>a;GwjSZf&~QjoAY+>m=qyRoq&u6-uypa-4vYP1&$OcMU~GbwhTd#Y`dSR-0l*}jrU-Mu1=EsNsrIkViArQg8H0|Elthc=3cTsY$0N`0Wr<-}{=Oo2f zfWRxWd*d-7*Q1@2Nzo>WQ25wDzA=<>otW&VDP%EM>td#yRWkA))iWS0>@Vntla-YX zG+4jNKa70^NO86wm~mbm-1aJQ>S@w}WoV^0u8@(i-VE7C9Q}OGb4sw;kVtJmQ9NNO z!L%Dq2sBlx-bTnrYh!>g2{OYWJ>B~|R;LbPuMP1PF&6N}qi?6L0AvMnC2GTh=Pi^6 z3)cbPboKr|m@HdtSZ)thlk_>Ww-MfiD4QZD!GmeSi?&y3tSYIyzejVsP5;rd;aqRDqq6C$T`j`M|98AEv2{yb(`e)+O2+f{o;W79g@F58 zr(&yM~Co6xlYfGz9b$geyh z-762Yl4ss6S6wi>qtVx0(NO%Gv-?O6K-^Daj5j z33gl{Od1kMb6ZApubP{+W-n38HJpYzoMau`m6xPa^JKGBV^f|oQlYM~V1EjriTa?H zwHzo=D!TTx3S-_@22QVaUy~8~kQ%RTO$n4l{ZCvkHlcxB|ol&!^n|l~Y z5kC|6_~l=VhcWo7VR~KK-fUPu{Y#c$=Fda9Pio{nSd5o_{z|)lLOb-YKLdlU#9D9? z^=y+jVTtKhG4(0)W-}qzUr2Z1K26J!?quikrhhC+h~qzQc$gL#ZT)dfm&I?HD1P@Z za@sSt9SP~HRx!R=OsIq(LbPW(Zc>>A|DF5=&SG!~vKEM`ku!CcT42=7%|PP{*g0Rk zZ#KN_!cF+crw@CSK%7kyGvTj)+1W$jvNH1TdqRR$fmDgOpRApD{#J{A-# z`ty9xwg4qy=A7Zz-@dN0f$HB`ARYkyHBeZ(t1-qJk&td6O7q39z=QlE zAcbV8QBN=jN0mp`taM5X`TKOjhw0!Q{+^T4R$ zs)<5Yum>3{n%NyZXrQ)kNRtBk)A+wA41PfktZ#g0A8mxTS#fev36Tm6M743VZe$_*lh1A(W#i_td=B_w@L5)!>`0K6Bu}aBoC6 z-ipNhC0((5io1B-Otkk2-ZHnfM<~g&s=&Spwd!^d{+hXC^aKS`L>WGlU z=Jlakk8Fx;m$Ma|+#VE!a)+Kf5hBj`{jCfT9R4%>cuv?N8<+O(_uLBW9$>6xE0}=6 zriS^cPo^PQ`Kfa!PN90kHyGPLv#|#s&=XY5w5kC4BsT?vl$F0jFDd+HEpw>p&f6(b zKIosdst*mU!zLc?)3J;RrsAlYK+yjbLj@hB&Fv+rmF8Gt03tB*oia*sf}+$bj}46w zAvAH>+OM%nZ0jx_n|N=p$UpGse!Z5DNB!#GTZ@+iqC+UL2@*v~!~5ejonGOctGba= zQLY+IY?Dpy^+@XQQke%6_x)EU0G5)3yjb|=d(?+DUfo7e@A^d7%|peRl&YuyaHp#| zV#i}jJ6(P1f7^j9JZwb0ST`ki)V>3-f(cOl>Z@`)oe8;Q;up->07+{#l!p z83L0|`YH4+XF4ngTBpdA!l-|Bl_PXI90c%TJjkyWSjqHW1Di}G%){@n?*G~y?Y+!O z-sqEZj>0Mn*i!yCnLy;2I^*D81y+T3w6;d^fKxRQDG{rnagr+2p!n}{dDOt|@s{&e z{hdu1a0TYYI3DvoMP@?!qVpJ>vG4bnYkNEXI-l*|GX|2C&}CfS6pQPCxnh$BL{)t1S1|x2*X3t0dP(5ego$t=!-^n(>@C~K zf&f8GfR$wmaTuo!y3(sD}sk@G_dg=GX!ThFeK|%B~H9v$_H@mmFijmb@E00tTBIRE3Z<}gm&5B)mV z&%cox0sslgZ%=b-@(NH@RVBj+fl>I%zSmzD)d%=6x5%y@sd~@@@o82Nb{n?upVNtZ z=&Rg!9M41p8W*qDcMlr(qAe^fhhl$oAr!o_QKjw5ShB%MU@g;2z2;`_FD)1rTv_&i zhH+Ti0dh`#PHC(P8biE~{M~E0vr&rM`kpOiU#}Bier{Oy)UF4$R_Kw+gcS zk0N-QYC8w7r$09!btEGRvSH)qNI-jYIBU@g(3?06WkHP~{n;b5+ z3i$l_T_KW`*`9wO)s(jK%$nE0=wP9dLctmY%WD{k#SZxpMvGAT;^$dW;){U%QEqJRtlt(g<)KuQ>|E@1EJXbj=P$^d>Sf>)?za}QVx^C1<#xi_Os+vB1i z*N9V&U-~zZ zyk;v45nzvEsuBSV1P zT_zhre%etjoC0*(y?_*_AcTV-Wtk+E50-~1!3lqI?34$^i(&5=KHZsw-W>PyKvQ;y zO#r8h+YC|2lxS>2FUcJZb2G~PrO<^cui9v#GBpLYkiisrbMWP>*wF;floDfeh(1&A?<7ik5i}E`wb(76iCqOV%eM$Xwzxn=7_;0p&PJ89q-Gxmu$J^7a~ ztld69;!;L0=V3BE`U*tCa|4LSq%!Q!@f7Ej!6a)&xB@l1Ba}{$w7* z%NAtKD0}Qys^6Fw7s6`^=^x@?Y#^p_yt8gKRGM^($-%I#9zQXeH%RtlX;O&)OAfJ zvr{u)*{h4RPxjKFf%DK_;d@j5Ok7cb5J@lyf{MU|e_D~oduesQ`A5OOt2g4Ir1!C`$w&gW1l)(gR8m6`MHJC-=jBJO1)hh`!f9 z(A&KM)F~rfL*u1w%$h5gy|_2t&dr2>Oo$PZF22d9Nv#V$MLO)_!; z+Wza@nE^%IIMmk8CphL0Sl;44@ED)U5TKf>J>k~-{zZ@DPw@o2ClZEWF2T-o3NQFRXF;G>KJr${xij@3-WyB8f8q#bD`4Tp9>c$H)++sko?- zE2n~N9#<5Mu#z}>=8eUqfZ=zxs%aQdL=e$rXTLZJqcLXi^_R)rVgmG}ue5V!-;2V*f+-FD^q~#BVg=1P-g2 zGE@gojSd6)D6i;}59IHOjyt&FTxFpRsBc~C^a)o}6zWuaq|Lm$_@Ig*G<#?2qwms* zJh}_RmJ@a4KQ8-kG^r+TK6DP?^o#{T_Je_4{!u5gNK8 z14GGeqIUgCkm7_Is}p9J+|M!d2hx3S2Gn#6$ThU;bSo`YLWLK7PyD}qnN4ypdF3*% z8u>piK;_0W58|V$PZ+O7Sk`pEILy^nM9m-UEetzmv#(0ru96zA==cC_VPiP{AJx_6 z>RVscJL1epo{B{PGV!woS~!A}nv{$Qq;AqASt#A^y~Qb=P#+zl0Py!cucrg@cT6ON9cIQ; zcTAM`x)P3~{~K=i(IafoDZBc8NQ_D$JT zy}M@rwUUFB7Bzxj0xI#j{$YUh!Tutv;vjeOE3);|k_J(JTewcBUY*L443=LD&5JSE zxnpIIm%=yvs2jdhNSpo4Qyf07TRy8Ry8UC;GO{5yTjSLH;e3W_ zn`_)l1}jrE+*hi5XbQi{(`$6|4?}$xWlvHc z9+=TW^1N1VOi~T{kX{0A`K2_ZMAwNqR*A^!vb3UK zVH4@V(3d94w+@f_;>4i!gSc7F71&L`Ue^ zBnVc4Gu^9t@EUrkO+CvopF@^46qdZAk7*Tu$(`awsznyU-C0gc8mq{d2+UmmsufK` zse>wP#`0(9DWj__mfM0GaY?9&i*5sFB|@A<9Qfk!|b!$)5A&CepJCR9S}jp3vC{RK(jF^WJ#2?@o&g z{`g`v-WWVN1L4}=!6vV`fnXeQ2SW-$tXS5E) zURHAX@;XmV3(z?Mil9vO;a*3T+3ya*7Zz_eLNnMr+}h^(Vld+DdG>*SFd#IuuI108 z^;hxOF%x<$tSa{=ry9ES^FZhSu=kd4QAKUvFe(ZNN{MuLcbA0HAfa@(ASqqaAV>_| z(jX}yNDM6vgLHRD#{k0s!@PUE?(2E~f#=I}9QUUI4))CKz4l&foxeKo@y9~Ry?+rI z{FUBmFr|~Lbw#4IF@S^x?<wh`GxIk5MJZglETo;1K*7o^?$$$EBb~?m zg^7OPn4rA;zyFJy0<(oFJ})pKY*zc}WC^1h-X|)DTH=Ac)&Lr;`0yJ6*;yZkVWqH! zBaE@RFd9`G0^mtTaz4R>E9#(Su}?c67cF1MEPbK{8$DtSzA&S?Je+q=WEINshQB3V zA|Wtt`J^v10^XC%ZbI4I=NN5m`PT|Y3a}o9S-JOSWf_?%W&Y;k6JKV6Jn`&|&d0r% z6o2I@4(~IIB2Jh`xa^aQ1F-916hn%D3eWg)z9X~Bai3@fZv(8Cw_*P?V;*Ce_$!>pZcx!(MBNVREF}Ak zNXC@rNsx!(@e-3MW~Kp9P~%eliBQaxZNd`I=86Jn0qyw{NO&l-r(vX2z;L=sC%5uT zPHT@UC^-OOv?wi?diUMAB8@g(-T2pv3;PwXv1v+kKE`GN!}2Rqj5LP?*&DZ*L;UA+ zI03gsb2LA-l>fDk_@ghciK{QOhmYO$-hy;Eu3`V+b7lb=zB%x!gCFleRN>DU*z;mA zNx@+^UF`UaOYr)~rZv?7}NwHF~ZrR_NA*C)!Epk=seT{t4rkZ$WrI zL0^~ILUz*pz~)_(GRu7o8p*)Xxk+s!$Parw9;AM@fGObLm^0oZ8HP6y1D7ic-3xs4 z)*p}t9CNCcncL1OS$&H~DK{Vjxg@G>Z(>P!GuoN#OO_E4bDdC(GnSX4y4g2IwAj%P zWXVNIRdaEo!Wq0926}?-7pyN$Eb?ehKXjpLBjb<(~gp zCi$TW&VzgR-3~H2rc)c5-6$&B{GO1fp<{#J%znteLA?E{H#w&EjY%>hsRCmYX#-I} z`^Fct9LF6aqMT$t*IPiHvw+RvW*=&LWZgA1titu#j6T;LzsEW4g3uCgb0K+e1#N9(lj~#VUIs&X|bgBTy(8Z(evCsVqO2{@?nj?kIYyB_%5~9MD_kSo49}Rx7bMm1e`l zE~hTL^GZy?`z7u<8QIFS;Q`<-4E#-3KmPiT_uWe@l37peb;X}~qQq5KmLl|Zzl{Gr zlEuu}Q|q6t)Sv7+YR)!l$0y-i>FBVcb1r-yHMd!r5Bs?GK}eO`x1S={yDLAdY7oGbdQP9D@5n4MG}WiUWP5|20O@((_vYi z^2$FUc>kuZ=o_UvBOW;Ug6PkKKOJ(0#xT``FoG|2_uQNkv{ zu$5brT9<)(3=Ld)!chWR^d-Cs+_(tcM-if}$Y}Iv3R8fix}3N=TlM2KyXcd(D$^{9 zyVMv(aux%1ky-XR-zzq6ezRV)SZ|;8Op5OVom?M8-o&x02)C-Ji+4KF`V^Ht_b@w}jXXM!=k>sDSZmdOud!myaMw#OGagX4TY zM!;Ynyt+8cR0yWj)8c0GArri%xGg-qCe;0|k%ecCOFt z&DxTm=J3xoc_2dNJrGj@lA14E)*FEjcaT8&KD+VDKY6J~@`8jl|DJOYO|5!h%g0P% zGTwoL$E?#4Wd0bLn?vvU2600nd?d*D$|{k3?`orKy%-$)ow|G>5J#zIem-ZYy+wa3 zt8QWQjPKNnx@g(>co zONh(9Z@_-?pQzCpv3=hC$|~`>_ovBgUL5bXhuxgI*xttTh}_*c(-p5J4ev+uqvAqj zItA}g8jIkkFD}`wIbyAvRRE4V}8~-&%1cn9L@!C3>ljq>>LvLQYP+mqxEA#NPy%PUCYLTC_<+5^Z4aD@nV@MMI z9__Z}_r1Z;)*IAjE+o~UpN#WG=?36`yprRJzY2bs5_!SG`ho8oud-@uO<)}Jlb5qr z%)LCN1*`({kNVQtgah-4EmPWv%WrbFlT8QW<$na9S9!i^Go#aBbv5ahX3A}Z3Gc3(h%7ryuO?Tg%Oz+*k~(MTJvJ?afIN|B zWVFxfCvSMVV&HS}Y3$W(bL0D);V81H-s|1$!2X-H`kon#-RHzDa#Siqt8S&!TgO7I z>^$gwF-7@KZf2j9ksc*fr0ZQyv_DokE)XlC0;KAREaHPd7fDwX;wZ|=s@es9&L-oBNT!wo+=m1tav`xFx11E7TTq5 z#lpcXhbHT8V00u zNOU>577ZIenRE4zx$N3isI2w~vV6yJ6i83dbI}XOJ@2MbHC-)&-n^dHEH=Mv^i@yUqIy+X*MW)6b>3WJb~{i($N;-$qa1r}Jtc zrw^gx-QUQp^1;98b*9iSZ-T-NjOhuEicfNuY&MI*CVe@gR($t_#fTr(Y-W5W7UoNC zoFp?ps34u`Z&d7FIr5W+k<{^xZxEuhPD1-nZX-`-0K3Jc7kWp${8*e`5c5;uuKRVn z!D+~U(8Y!e0c_O)I?BIk2~>j+`W2|4m#oa?YX0`Ys?)P(ql2wxT*4tkS`s$Jsa9xL z^7D91o9(O)2w~RJ#LkM7WeVB^B}2*P^#xyF7VV)2MLhz!+*bb*tp=&2^RzZR3gu;;1u^*SrfZ&IK9Az@ zT#=i@LA!Vm?Me6@_F)Y3Ih#t@Ml=Yu^|pbPWm}hF7onsW-Sz9mX(sy)FsiT!ROD3q z8+LB&2Jd;n==aN~-V2OhURPb{I0rG@o>JehQ@2lezrh$v8q44>?O5j3e*X2X3_4Vc zazjD-=191`Q$S0w=KPp;L8>fs1u3aodd)TCNO&F_?Y?Q<1e1Sqc9S@KW!8Fd{$XU{rt;_2 z=|sX~23aQMWuvPyUta%zmb^i=^sPj1sj|{AydMmi<~&g=@7Y~VK?i)^|&n4b<8(;zvhhJrx+T`@mEgt1!mt4Om z5Ssb^?x3^QyeAj;@{UnCnP+Fr8;O9OoEfasthjvOHfPxyP~3qh?;pV`aF5*R{~73h zPkL|QPd7QQrhz;tSj_E)EbCp?yG>A{_xJUJuCcq`14?V3q*0Mf-PdQE3Jx#A@&0T@ zc(<|0vfK!e{n6}!8vn!`(vL$9_+?5< z82G11DTxxrKfNf0#ZpQ7`ir!BtHUW(t?vjjbR23HsKHX7ls^&2oJn{d>S;Nn*a7pe zdnC;tUudQ3L8v>zBV5?jX#~>SxGwxr2~GnF;RIesC+YVGv?gCafg}h_P+Q?PVr0Kd zyzN8yOW)-@d&B`!-vUrPiPe1QRTEzMPI@3~Ml&0cCP5xR7WcP|jqtwP>8ZGP$tK75 zJpc2-G`Z3Aqh!`R3cSIYc=e-8_GlxCp11;g+MK6H@mmypDTcAr-b}6L@)@R+&jpka z0ADhzUN0#2CV(YEVp`%bE=R<*dv-a`g-|^6_{8c?>V<5t^y>}yhKn&oG%3dwB_xzZ z7@~*A`cXHJiPRWWAHyQ{BD`LP>+JA=5}fn?x@=`e$s{p0kl^`|=j$0)cqFW5|>CSBMZt4kFEGrI%JO0_sMYX@LYhjkRtmhugoE zz#`MaM|Ns~0&JcN&fwE1=IXWg8qZL#Myqj&Nw6Q^>kDohV&cmeEQ^v?1yG-0mVc4Y zHys}u#?#b8!>7ObXG{V|{uUGuAfe7D$@PqB1GrrQ^R84?tTsDrY-4e`M5$MwUA|1J zy_B105Gazp1#1C*KYAeG>x9SYZpeYpeP^W0^LVwZnYgSI$eA%4`B8CDeD|@9-}`he zquyqe%#OL8FE0|8S_%l~G9gJ;{n$s|bp&0O=ji#6UgIhIr~^8j@K0mqBA2m0UC{&q zLWo5B0(NxHJtFE3*0xWipr_zO1F~Vd$(3Q{S4v!2-GCe-(!ny=fXIuw9Q4##M zv9Y;fx(6V_K+eARYq{1O?C-vRiVd>l&GmOeh23%M)plOBV&E45^lpvk;hd`n-6(Hq zQFNfUKyMuz#%9;AImsYzu3O%C6JULQY0^%u+Mmjye;$@6+k?s)+5nOl7kT30>hiCY z0g!ZC93;wqf<-HzDE~|GTo6xK+OOZ;kdpWTvNjxUkL{)^jc zI>qLwyyK&pW8`bD-*`yLDOZg0J>&qI{ru5_d8bm+Q?clje8k_v`1TOp*kvvkwuV)9 z?_Qehx!<25dD0#6)QHG6OJOMzDpFEX{VrJCr1uS@0F8vji!;%#>g}!g=xJ{)`b zToh07OmwRZrdczI-W_@#%5@D^3^*v_KxYmveS%$i!fP}+d*gkqzQ5Ucg5y7l`1(!I zW+dG;&^v&(k_P?U`4ka0-s^6_PDR`MEZQxvF zyp)!@-0z{0jadXSCBVZKG8g&Q!YkGMvmT{Jh8$e}uA{?%&I*{~&9w!tp5|=7 zj$OZJYKmOu?M-CWE|J(&kWMh{eX$~gWY8Wcz@q~kH~%Ows><_I)mF>p)*Znlty^xn-g04^+hxIJ}^RVzGcZC8^$tYd#5j(2_>{d2) z*A@=52pm$rh-2{(F3&W z!H?S;eZob~5q3FJ4lZ;Y2We9sgD8blWlST>SBjWXSP!_v6Ayg1eb^ER>1o)!SzqtH zvwGDwVM%jS%YK293mjcgEh)^?y17d&Rj9dkkw_k5MmR{Umke+4X1_Kr>Revh`W>t} z0~m*&t#j>Q5wlc+f2jW&h-+hgDN3|(Le%ypyA6{^NRCqw+nd%LXOYsfJ=4>`Rals` z#H2sTVIN=}w6y(xrL{3JUp$f>Qq5IYA!kcd15~N@`m2Tvc24| z3C;?rYha5jX&pCp@*>vUIV>JYtsXe}_-bs331#-ITo^Rkz4`r&Ur?R;{pBx@ZD6-| zE}TYpd~7;*nbILBv0g0yXJQ7k4gB$jPQWu>?ZmvR?=~A0 z^EeEUM1y3LShHQm8b#apRgl&12#9nT7eFHN+ z8f!@R`oP^s*IL7i++fq%+Mg=djx7Cfi$Kedw@ULw5dUKK(Qqk~GY_ymms-33M6hKN z2;XMr^*%zEsoAt|VLGN~Tkq`w613|8@x~5Gmiil-kF*VrD784=63W31vMpNd^@|l= z=LHl?hq37nx}?465b>sWJcrH} zIajovfi+g0{LU(%KG;zWTGN$&PB5mv4&>g0gGazz&qcSgP<|0kz4pk zu|}o4u2el&AYwNC>e+0+M;KJ>^0z|u>hLbZ_kquPDf`0GF)%@gnNI>yXp03u*w4o0 za6cLY+9Ky9^ZpZ*g?<7h@zS1ao|6#=!X8JxRs)(^>=Ok5M_Ie@l1oq1;k5`Sn*K;q zk~v^oL*CDJ++UV5S;7pE9%XtzHe8#IimN=u-;-!KEu>R_e`EyIe5FC~Vbef!gSLs4 z4kY44x&$n|{ArSLvRTiYpl1b0K}%E5mC#?AGQp7)ZtZADZ2V6a@?NCED~YB&=xDYm zKfl(Imsk)=#npm8$>Z1o-^n634MKP$c-|`Q$!DiF)YMf3*NskxUwF;+h)^w)dt<7~MCQ2aACIrfA`3 zvu~y2d|hei?vs##y!(q%1x)R9fO3>J1@8NjH8}ZQ>)YcRKqxoQ73lN46~F%1jsxPn zN!K)BS_mij$NKH%aqG$bxXxg>(EUBE1Np4)7K7T~r70`WV^Ipd8`x8IGy#6U{%Xlb z&b6)1%^mTS;_u-LdX%ng^uvDmUoivRTGV@c`>Fg6@K%F@81}=pY0bM)yOnMJ!S zHB7=JEVoL$a4*Ycc;FhsL$A&946m^ZyD%MUnZO^nG$|3O24l#j$X*>J=OP1$8Vf$y zXV%_$p|$c($ybUpX4MnbfI>;YEO%j9+@ISm&!=iUxalE8QNk>{-PFhR>ypMZGJ!e5 zQ;Q8M$mpnAp@E9|3d2?vDS!l`SA7V>Q=%0>k9h_tZi-TE^Vc_cM=_&u=!RtU&PuJQg}ii{G4o{cOX@y63-vN<7>@~0W{#)I5jrIful9K#8s^ishfe$XuhfbA z1WfwT<&pyBTx7P+=tV6>W?zBSZ-zeVz2OOa!(rOq=3t(pYDazIs(ps;nl5^ftiZSc z!<{|(gfy$=*H|8VCw~Y;spFoq37~~6gkuwbLD93xlefR#F#q|%lNh{P%LXKlhcwgv zA;-fbucSs^qbXtbgin0gt@f`brA?ISMYGV)vFOrA6FnN*!XQ3;MlRIwH4Lv)u&$L@ z!}Z39P`r->J@Es#xYwDOmlhad(j}q?u-Sg+#n(P2o~690a)Z$Xzo=q`!_YdO5syVC zbg|>d()CuM!kGJ*3}$0k!b*q#`Tn3%Mv1iJtC$`6zH;fE?4O(Ww=?e|9M<8Ry46(j zjvW;XJh@BSPSC0_HY>pCSjvkp+xv%%o|Fs-R&GQ>Zma=+!$-<7Q6XxR@_8EIn_~j_ zDblJ{7>tUgK}0_iw&uqb!qeCD0IKI_;vpq&e))jx0jp6~FuTB-V-Xgqa$|3PKul~3zaZxgi4y0 zz@SfjkvL8_)JHddW1m2GM<;UT2qRdLxs$GjpRwS%s*O|u`r(iC!LB0PdVB8ZFh!ps zR})$KUwN3}r@m6MAMEPcR558mCU%%n&pImMdRa~J;PwvnVhp*bg?eoc2C5bEQ3Lq| zsnK$)?E$9U##1^LUtJI_tOrVdq8LYNz5eLsD}mCzM6$`4hBRl?Pudr6!`n9l<;-{(JFto5 ze*>k1Af0X(N?8JRF57;_Tdl~VXcR;j|15%1e3E2o3w;hS#5f55fU0l4Mm7QoLWG-R zNP6pD!iZqoNAHM86mt8WdSueI8$|KZ>2^pU@u(fG9A*f4SFqIX38pr0%HbvUu}T}_ zR}ElayM3`vl|M$^_dw>$i9@(DChgNrh1I~@j}wCXp_B9ECtO*_L-h|5%>jj7CKdsy z{L{~g-5c5HCYWR#b?4j-F61^crl?3PjvGCH;`avpx#qm_f7A?>q~6>@xdVC8^!!0I ziH{k$oAkG*@3$iT9$AL9_Y4QOW0ORYkX}&2u*91&T@zhK$8ZbqH}E60?$2F|Ipeq`tFl zBlC2_Or7?eX&<3To>3ZQIr6TLVf@VkvG?(KpPrA}W91yT98^4Czlcjt@NKjUW;MTy zG_UQF&N9AiRMAT^n%hES2;ykbxo^7K&aioSA*H@+;ZB-8C5;w~6{UH8L`{*WiSn0B zlmABvkBM*mg~6d`;>7WSTeK9n{hpirrkeF#!-&T4vTvyeJZl)((Aicc5yu;*)tJou zWv1PZ(Um&&2FrjM`ToaPA@BIF-9}QQ>pyKR*uTfx*}tI*o42~#!nxu zBAKPzH5pU?>q&@U0FR@Q-R#_DP z-*d!$p;X+b75q*1tn~_MzKV^FYSE(7lQ#z>mt7+|j=s)*`9I%gBQ*KcZUCYYlX_Ql zwp_JqbiWe=>X!=Y$S--d5lk%CN+y|iq|>TVA`9sG|9t>mI}7Ohvy)Gzc_SB#r31GDFQ?Up{VX9k|rlj;FQz!lHt- z77A&lx12g#Wl~r-B2h-01E6z4Y3j6?Mk?|Dm``LGp1huV{8+~EpH#Xr+aqJqZN|o= z*|+?cFJlWH(;pP>owYpa6!Cv91_3sZsg2&vN>w)H-__SM+;@moXq_Zi=cZlQU}ch?mkqR?0OP5uUBE zD>3qSbz1*3{shEW&$trS@JJbW{(DEA=Z{C@ecSKaybN4yMM;2GMH+uLxru~fC{bZ0 z(g?h!CHSAQO*B%p!+eB};U;qGyE*3oR-*V9v9(WXJE5$*I>S+gh`o6F?0tpse+Nr6 zaeNK`yUwYY>wV9{qT8&EO`dmQVH-st+DZ7yO&RxJ1N+yUN7`jq4*NTh_fZT84gPajd1zP!<|{k28uh&ZKA?Tb z$Tf1ll{|g4-_P`7__1PapC-oerA=cf9uuH*`N2ffkD-`J{h}ErQDGKX$ayR zX^Kp14(vGKREkssm&*BaF*Mh1!n^{y#xa3TS-({Z%)i{X82m=6iWQa0Md;q`BjFu! zH>dTcl-G-p`x8d&03_dTw+3=kD*osi*4mapMHwdoG{+)%R?pIP-SK3rA9)FnL~buX zlD<0xKT7hxM(k3bNaguS6!mxGb~GV`7m-2j;LVxVHZw(Z;6SjVz(_v&0g|j1>9Dch zfq(|hOM$5Zk)`ooNW?DE2Zp@jMta;f>y6S$67jiiBG0G;PhCMzg904s6oBL3(EQ30;q*KEB>gGj;j_ z;3`2sj~W*P>qdnZ{r5>8pz%;E3ZMl+0Y9D-wDS>e_UT$EE)^1h9gc5+TpLIDiPMW! z^m@OepR?ouQ{uM|r1^ja2O~A5yns$v!tIEw#n#<3z+`hQx}B_jT#XDI6`ZY{2WXRt zz@Xh#iK{tF1Bc%bkCyTQiE#hargeT7M3;;B4zXFh~02|)qRf~4hc=r_s{ER$f&-ra|D6Z6X zGXo=Wci>6@Y`GNpev<6j9sEgQ4GaP_f6zvJA8&nOul{vclI7Gw6MqZl0#ld(rxS4Q z-t$bjQVhsYaBg13 zDC23~Gy*YW=#0=eU{U#&V40l%V*6z8ECGhfT*FufvJIGbMA+15l)e}`gD;mZ%>4f5 zN5&C;cFLT{+WTHn-NFDSOtO1R_gVfC4?BoMWGg`~chm@H+Y8HJQhPm&P~Drxoo}i} zLw6ZSV7Zm3}ISW%C7a#OwFyrEU2L*e4Y%FHnuWmgS14}^OMqSVi zjz=+gwG4(bkh{fJgRd5*MNQzwxur%YZO}3Ph?F2I_RIN{uoZ5=9GdscS1ryqVtDHe z1%jdQw_k4K2d%SQS?$?`qQW=J2ky?-%O`icZswd_H>-H#84Rd?LqU>`8Z5nuzYe*XU1vz|DSRJvloPlS^aylOk^Blp{e!LujT z#Xl|seR!am=Xs`Xqd3^-LnN03-uA&I>`D=uV=<^e+AkMxh7pXA$yQo-$-5ol4tS*I z&Zmrz&3VA*Jkd+%aK?&JZP}0p8eT=F?J7|f`5{<1-`Ky_IG6`i9lq9ZO-F$7^Lam;l2fqq{io}e+1eU7%e zmh;I@J!g2{cl0$Oy7vm`x{x*uRmE$@YqhsoiAKMIlM0aTluf8Le^SfU0X;0{NSf0J zyawf)jY1{c;uW~byBRcYb5_e zdk>z@H@dq8wW(8^Ot6xBkIx24@*fiv8M7_&H%QiIt(e&sAl`)tREM@y_DjA&a~~bY z(v#;4^^&xr!wOYO11r5vDX}?^(#>MY;BTuC-%da0B|=a zwckFU`lnxz@B233v26V$U1Lb9`dduJ$HrU6_=hU-77qVF9=h#fWm1PTTF7A28Rs#N&`JuJ9O(-L)rd7 ze=cb?Yg9PSm*jXZ?1f@;x1HXYB$?Sr*Eyml8!s*2P1QPTY<;F6H47f7*A!M2danKt zx?8ECt^95O0liZvvlMijYVZi0udGxj)iL1itUsP(S8nXGQYY!CBF$B;fOD9yLxw_OGtD{b`8M5Ol z+N6~`pX#sfn=pFmFEF2*tg$o48XP#}GJf98TH3}m8+7#%#GIRoMf1dexk;sXE2lp(1O;ROk@O~ znEnoL0&C5&7b9=NQv)_VMsKBUJH5`FFU~geTvbv)pjS)&Z6XL%$o!L)qmp`OD-B%5X#%UgKJth{Iy~Rfm zJcBkb)u|~^xb_mxM@T??K%WbAYqQanyaGW=SC;w87BudapOVxs&wnbNT8DJX1Z#rK zSxW=KR-lmqjU6|o_-uywn-s4AsV$>{D-Xj^6`Jfd-hRd|yOlI7?N-ilpN}SKkRgZL z<<0XJf~_lu0Ip%h9a|s|bk`~+Cr~cEu14wb@}|r? zhq)zbWf}{l8FGQ1wcIPu)`dag(y8K6oBjS*gFiEm8mKT?!5|5E3gVl5IT7S=(2GD>PTJohQ4fEr`%fCMNS z1MFEf*79h07n9Jq+1I89bMKC(v^9+LzBMtBo-g>@UKKk9kFDN3c1S!@)~Y}+hUcQ! zi4xaw!;f36rFPYZigjmil9lGK+^A9^LmC0xDk|;-mhQYdWx93w&%gMAnUo^lZ%={m zf%!UdGXp~5W%>rV&6l7-ugBK2$$dT!(e7$UW?Rj{`47bgV6(F_L%hRZo-f7&?i#L9 zQIqOPe;cOjD(M;qW{^PDaPwV9M&3;(S5o*6GfY>I)~xaGAG8>GHhqtTpFq>bcF{?Q z5kTVfHGonf8lQ!fy(Tq_6lCqAmA-+@sX__p_qvU#N||3F78q!>eArX!bfoHs-gq%a zI0l9M@OL)J`mzNqzCZiFaZNHtDR19qyN~H-&8DLHF41(TqZdzzPs)$0j=pIwCdk=L zQ9c*he(%ed{3Q^?rjIDneJysIsO*D9W^mjut4Z*h^`xPCW;^+tu(keJ2Sp@0-?D>f zm)&yAx780UKF$v-E)oWy=(+Zi)KegXV|x8%rdeOfwzW)$YW<{@ht`I}q8Z<~9ykZI zZ?z4rx=Rl^K-+ZFZd+1dsfJFXHM+VN*}|gKpg9&mxajQhIrm8Mvth1nmhW=Kd4MQJ zlHGt$`pjJ*AeBtLpsRo&2#RJ0%Y84iGLp4B4y3W#aZ(gALAiL>%?83Y=;ly9&cBP>pyx-G_*^nY}p4Y)3N6nrw%WhM^Z|$ z!TLsetbUT2as3+xNgENZv%Ec9&sN>0Y!@D)LahfQyQy#*UnMi``&?z0rdEf(leCX2 zzd2KWm<&sn6r?+i787w;r>>KPHrDIt5egI)q(Ky|+f0S$1Bb%wwtXtXMpZ}NG=Bt4 zk3Jx_&1A!epD)x|BuTYggZ7Hkp8qBOrs^*J64Y>N!y?y|eJ&BKMQN3~R+j3tw@n4Q zRT%oAOAOC$g}2@Kz0rp=^h#C*A(@Ndy}`Q;gW%&Q#==~>aPx#u~wU8-J4N;Z_V-)$J;yag#8g3wu% zBAnie+ex42aKbF}2QHHl`?D%cv#OFl`zL|0)}xjO9+Ferjf+m=es4v0ytf2BHVRgk zNM0K>-Xf|NpBkAK8k0tvP^CeO6jeMLtI%D+J8pZqr5?lnFtH=`FCumWuZD!>JP4AR z(Q*#lE#5-%leHf%C=Krc1;CvB91Ny1r20W)Xd+-tysQznq67 z6)ll;pI`f)L0p^eG_68vDj5>{Ng<2QtYcy%Bd6gv&gF9{Yu_qA@ErPSC#t}wg8e%N zu_m>IzUprw&(k{m9p)S+9?=lA=V=QAU#XxJ2s$C;LrMmkqBL->< zI`}5;dj9Qz2K@3yO~k--c>B!XP>kqR!Go9i$0E>|2mW5nLmzV56s>DVq;k!8i5iYU zDSKiDY%cct`*E+UT2o6N&N^?0nGmg?FT@I-5Zg6Aebt(B;WWJGn5ER5Li5wQdHWWa zOBw9QQ!C40^s5=}p2-jkl1KFj4M$@(1$cjI7R8m37 zEtWt>eKS{Yl{`s6@!%2)0a~|%w;XFeD%I+nB#AydPx8|Z3>ufgkX+5lc5eTBPd)BT zj*+5maHqgpYERiSFBMc0Kp6}2htD*%UlI$F0cVxo6x>%9yji>EoSD)u0u+-citOf8 zwpF)I1+*RP@qTJL@4Yxjf30i!p-nv*mE=`M6aMfy_e)^;T|6HScP$Z)TFk|V^Q zy~%`K$EwXe^_1d8@APfrkv*6i`?sp!@{~2tSY6(wE7d0msqI*LD%Pl@Hb@hThLDH3 zJY)6iq*nGZR7rJe`EYn5VlOe}l_a>R&e6g5iCQ5>(~}0#|7roI7+QMDpFDeDr>Q>q z%X#by&VO|9?RUZ(wCE>hGmpNa*B86N$HY$#g(e|~>ethC?2d^}rC2Q5`J?C`uyR$b zIpPn_VEsUSOur0kN*A_xdlH{orNY|5*JVB=bSqJ&l|6I`w0voh>*%n(LYN$UkGE(~ zH8glK?2}eE)EgMON|c4sylbzJ?qO$r25G_ zDY_;;_P&3&jB;j`fq0R* z!D0>(lX5{mm5}Q_e0O!}0DSoYUCCg?3f^EZet*45-L_Yf#l=!(g9_Hc!W7Xkt9Ks$>29~)H)>3zr+Po19r6B2hMK5p9FW}< zseGD^7&@(FfD;Hn1Wq-&*ORr?o5iIW^&kOPi3(JrOoQPQ;!L~FJ8lD@M0d^S`j3mG z;xbe$^hB-ae>3bGBu`qjKgWqjdx<@4*i8o-9Nhm=xwkLAN3!Tp1NQXraW?SL($Aam zOw(_Lid4BMfvhSW_hj>wolPbWz~V;@lNj5sq;Wi^@fsm}ExLVGO)I{~wZ-El*1!pc zWPub1I`rvk?>Kj5XBtia_6JE-4F9-jA$|Su=={20`u@y$*H?Oi{=1QOv$M7R)V~d$ zLyhUF#6ATj*x=rMg=sH=h++j$)=2JK4Fi(Zw6S-hRwD8~*$3rWEY-uTLO^zm1XvJt z8XeC)=dIIVOPZ*MA_?@=0zBjG%TROxXGpn>ea!`#>@mw8{MDE8nWl&NTqJm-#p6JI zSSa}$Z_t4-zb}ZrxYePB7nZ40%g*^O2<8<8LmmSFO^t1Fs4H7C|GS#}+!;vAJ69wA z3_~-J+i*5%5E2%IG_@^U;?pU(aE#85Ulx_KX`%Kz{LybXAFsJmUtSRzzx8V-SX&xP z=fK@8d7X3e3aARXSmPB)ol^Wpz6FdT_;&}r0PPRbBz5D6S?-y#Mr2@g^n60@i%ZdE zhmfI`Y`3Kd%2xC3EwSsS1G70%44_2fP{bPBAJXy_#HpZ}<>QuZ`9A9qJ==BGko{>w z36}Di7Yh^{%mtj5V}YZoqy)pQI0u@BgDDff>RM*rywL)WB>(=*Z$5S3?A&Kq$^0f_ zgXv05mLNBk+Pst8D!1WUP)oST?b6W?o+N~#O9GWZmF<9Olvc#$83$p*b6*WaCPgHi zXZAICwD3~^_pYqZeW^5J&u@8lOG0VlTRZcmHe{ZM*!XXfl^)Pj?dzqG5g5^(I%YTuOBW0BG|M0yX03@ zpCog?K6PjFvhJ=UK-+P>ved)YWSd}AN(>ljzj9DheciTbXxW%ns3N>ACZb+PTUBD4 z?b??^y0~1!m*F<(0C-dG*M*noPyE!bghoWRQ;YhDbd6knd~#D4?b8f>e=K=#eq|qq zI$G8g&8uE*0%rS;3f*o7&UyW5dTB(&N5>EQ(~^9BNK{GRVv3iv_T5R)6lik!6Kc*k z0JMxb$~hM?MxYD|J-1*XrRn1zN7WV}Xdw>cK=Zp*@fIyA_{h0GC|apMrT0xEJM8D9 z=OSyxOVmTnd7G|)2PALK;W*ZHh&9R7bJ$+{xsy9ryf3BISdQ3w7E;4z6FJ-xx%$n7 zSd{zaVgOFMWL{2jhxb=LZa&MCJxrL%}EE=ew`1K=cZosRMWq<-^VG9)y%Us z;bi#$Qynf_Kj#Cd##SkA+d685?}3$nKuGL)OLJy}yO1?s=b=|dx}Z`{vbFDv;x>1# z9f`K}2=e#SO2as&85K&ntrLbU!RAfT7 zl_ZA>29k*irk>|~KO8`XtV!s%@XBNrr3st!K|Tt{623OuB^ZM!D9pXll5D$y6*8M} zur?~Zy2P&(7C0+&4`~AcYeCDA2k% z;kO(0BLM7TRbih25b+jgwmuJ+oamQO{=eWonS>0aZ7a5&0q*325yn*ElkxJkVge(! z$i4hTry7Jw9s^2cA28<#vtc<0>s!-u?qa>&4^Xe=uM~HG7@e&9a66;n+gDUE#CNvr zSk04Q@=OWD_>H~dt37Gw1c?vL#SgRG?K25Ouf)0Cs7f&u zbD1?$!*rTFoZ3(NWtD}tD29jlS^J%u&iI~7f(>?h+Hj2R6V=Z-cNJ^$6}P35{EQ9q z-E$ev(#*uA=XZTmvJRbnql$HVC;^z(ro%x%#$+e683n^6&YC7{9zae9Hm-Bi1RK|j{^0Gj0W_mOwb1U_ zh(LAizJccwMCUp}+M>(J4{I27wDy#VSL^by(6wmuxT|WwR(_KP>1Hh#ZUc>JgI6Vz z_g?v_iw#$n^`kn3;SoJ{!NYFMoAKUf)`-iU>@0{Tt)s{W|1txIaWOgth7}YeG=0%FTcS zDeR~`D|76A+1m{+Vpb;jn7WK^P`{6krfRm_;!(P7`hcYNEwGR?kiYajCrJ+icx^LP z{VqB}kCJUvT$(TozFGd@l61xkVM#td?ym$74ER1hMC(ib;?qJVeK9WAKRP|cfX1H) zx978!doMG1_e>exZqO!X9s#DbCz8NhFCit~jXZ$yeQqE51Mui* z0`&hkU+SCb&_1BNsynE_&8xh?n>`_)HWXvkf+9kGp;)ZqPwAYd{%5~aPkSbTLJROj(yUh#{h>Gc${t-=d%ScIqhEAb-9rKU4X@q z+RpVm*8lVU&i2-OC-;Zj_7FMJczYwuEfB@PEPkk{lA?w|0k!UrLN}_Z4nx$s6-nj$ zmp|*=m7HmC_1`k9Y1#qAWd{DZoG}b)1h|KQT7ZNLYD3=Nc4S*Eml#M*&K)m%ts;)d zo!eZC4IZKtUn^)zV)QN^?(#u-**9mGHAnJLTXQDs_x&puV}W{q^^E-^KSuce_jSa2~xW> z6~D*=J22Una0_>aj&`)1c4OCc04bL>B&i%GQQQ1qkDdT^Aju?io`2oZKeh&hvD{HS zU7>@cy*K1AygKpSbAUEd!f!XnD>rfFSqzv0B7V|l=+uF@s(VX%#(nz>9z_sk9Af&{xVh?IM?q&OJuOzS6Li^*h>=Y+9 zns;->_4ayq8PkkXpx+nwGp%}z z0S4;kneb9Ao4fFqR~o=`LZAz6HM6_SwOT{&^pYAAPo>ole0<_3$blHz$)6potJ-l& zFFLL_VpOOCuh%VA>NsZ&j}3{9T)Nbh@qvBYw~Sb5RT55lEpP)iH(fcbD_eU4(!)}l z?2i$c$**YQgP>WfcXE9XA*BeFw3|DT38-nrtK|Owuf1;%XS)CYUvA}AqFZ-GiPW6W zn_`#|#oA0LUi zoakjd+2WQRZ*;??eqRZ2$9mk!t&Cwjgim}4D9EZRF(C>e7P+M;wEFn@iD1Hcn|B7i z7f6blvcAB!3fy~_1J8vwqQ|nf`-@ha_}7*h(@e)Q&2Xpef&&7>u~F_nl{fM+b66J* zxG1zeZm$oSuFkcieYv?_shv{p|JlvNCxebT^l6F`5LQ<^Rf9T=+}|QjnPP=~c%D2x zXUJmc$NRl4ny8TQQG3`4J$m0p-z66lU9;RPf+AmO3)bjdtTNnIf%iyDxk@JiAa^~d zMwMQZAnr})gm(4HxMoxj%4tiyx9y>68k zTx(hOyBfQ)1aRU}jWy+B%}`tK*af2m)xl&>mARQ&mlO{g?bk5<-OuNbOEgNmZp;E` zc8<6GWZ#Z)(8d~8xGdP<0Unna?E_5A>531C^hqmCCWInfPe1dx?8wuHE~2jXM{QkRn;O zFX{VVy<6hSLeHm!9vP*|BPx)A&mtrxw$=wT-J5HsQKS|p&=3MmH}u=ScVH&mPm+P$y5dMJ`^ zFAG)L*i{b-ez>J{)y*{sa0Fq+K6#dpW0qqr(+?SFAp3`j4qiAVeRRBN&Jz2@xqJG9 zeL;wigm?BefI(;k!F_zUzG{@{An#}V?qRH`>%j6EXMvLGDbA753ya34!yG`OpFz|s z=V5Ddr@8;Q09r+z-8pet=1rwf9MB~x93#)ps8J~vOd06wL?u%bx{lBO zrZ>3Mtgt@M6AGFdl4m-WlfG%+{!kjd#-=7Ya#vl{3zi6M{uBFS!;TGS5-RMi4w_8B zIoU}V^;Uk-Dss<1XObMmakfv*1*Dwnz{~120S|5F?g>YwDBS#6g3b6ltCzHbKn!L* zXKbnEK+tq@JCh`ru^36ap`30Xvx1dNn?nj`n{6^{Xq15uYWi0{@=z`7T$Xnsoxdn4 zyL2-@(msVBAyOyWjHieWm*wP(>MO}sgT{7k_P}8Bh!US7T#45k6vcLlZXZwMF1V9t zx|b+irFvNJb`w>#XCI! z;0Axi>smLw!@87$Z1UF73v0>=joDHk0wFkgU^YGxvE1nbj)VRBA30Lmj>Ud;#)Z0kg zxUdG^xyF^xwps_agWL1?l=|5)CR+h;L8B1|MqNk}vI>;h4`H{?sT0IIPR%>Uq80^p z_I%qWilW@hp_E5ss^STva(OOM>UZ<<8eI@HKjj46z4Ao~RdYA#{Yo!i5BO$Axs8zj zHY>ei%X}+&${~0ZSUYg8@_0!W>NEH#_ss3H7@hj-W&!xQJg97Mm;t%|bMjh}kLe3+ zS!$EcAktsvM8H<;j_c0f#SWGx*{RRM#VH{;C`5fYaW|HwGMEPXsn&swUV&E*!IuH(E}Znsr0A+EJqwx)e`zejfX$&#>2{{- zjGm73b0$}0af4+}bZEVIt2)X|x@c5rW#yd84eB2CYJan`ALMYg(nu?w2|7K0nrx== z{^E#nq18DwfqAqht{heKB?QGz&7E?$ayni;w86=y90^VKF6^wvJqhcqz`+(>o~pLL zpfH$kUL>&s+ZO(ckcYnJ0%q00)U_g>A}@1jUnsqOLu|7CXkqx78rJBI!7F|YUVNnO zdb}!Gqd+dCfgXl{(Do_cxqIUIV12|fjkw9~h)ZmmDlfKk%j^RaiBaM}l!Jh~X! zW(sXPbxrTm+r={vFemi4c$79lr2*JPz^?<~S1a@ShC`y3c|fcu^!M34auY$5ilGRM(&hq36wGCS}GB)sH>0NSk-NumFf+ooqiA&g;o=R|hnaUIjehd&P$*uRXtelG`0JT6 z!O8CQkde4@bxcRt(AD`o6$@EyJ88QhE7pOYML;OybUMh#ugh_sxUa`!W})1*#eQb#Y>dX77iz97PMs6|I8Q z5SwcWj*JxeuL4amr7g9sp-Fi27DP4zbzjLg#73z}je9g#?qrsM=P{oHq1nj@ot2$b zRSl_e88AwTK$pg^W`@Wxf5nH!Nf=8J+82uNW5&~LLt~oUcV|nIBm5c_E%QvQ+(bf5 zyG|s8=0JBI4*e_VCat%yiMu1jokFeoyLv%Q6^S1 z&l2afxuHp4xO`R2MzW=a^!8y(i;i5HSK?h76#X~R{6b7yl1+C!1+~<3`wK6u@rLI@Go4vGKy1D z=c48!qBACl*uz@k|6#bCKAoc08MyBAIfh20e)Ch_)4pQdg0Afs3F`x1Q+3yZGX5Z#kzhXdLQN$1b#)H=d4tfSBQ4{--q*^ji3!ezR)merGV}Oj-QZ zhB%v2IxR^pyl>MVYV<^dnn{la4dxBN5xR_Engq~`gXiG&jvv}#c`p_XGtNkhg+mYVoC+Vp@|yIFS#s#sDEKqo=RxJ@;=TY9`-t6~Q_WifNoFITQS!$RaX z>|LaRI2osLf|gQ40ESs12?>H^8~P;t|$qrDBvV z9hpztri5=nNUfkxQyn(~G2LA>6?P&KtJ&f0y!=VLt)fipE<6Mwd zcjdPUAwW&c+i80}>^&5@-)6-L#c#JIX59*+BYBbVEgvFXF9e-0S419w>6pbN35hi` zM^(&7l0Z^lr&saNR$8}z3+Br+>dE-y9&^80o}YSn{(9i zNNt$cmaO$9*n^p-RXWxBv5YH?831Y2V22S$qdzU$7r5@%CA7R)*9?= zI=cjW_`GALb%Nnl5gjCy^iz}GXRIbpPdd?A+vbA8tNGTtxxM~~MMMV|+6$X|ABhD0m)x=b7h!lwcGmNe zhX2h4IH<3QPZ&8IRvHU~`XxZ)LZ#X_ep8ytcezjCn#oosgC%L^}(rCsTmQ|U1~ zg;zxT^)WfRd3)pRg61nD8jr2ba;qVdR9{A%_{@4=I0aMi`5lu|cF-X4sDmudQKe72 z_UCi#f%E*=uMcE)LpL<-sUVDhKXF@ob`#^jj5Yt9@SqF`ceY33mb%qg>`u9ox zU?fJu2j5VuWF*K!pL8%?n^3pDM$XgZ2?>uK{EUk|L5)*m@KYKr)e!7dsOs*GCF5+N zSH;$_AD&5L>E0u%t#HOXm_meqf(jDM^cayuX^5@hyq^muMu|t3Yc}a#yGV*?1E|h2 zE{meptVcix#mt&^x9G;g?mm~BqrCIs^zwZv99q~vHfcDLe_?z?_rt|w>@e35Dfzsi z&FNt0Quf~`8o#UaQ99@@k}8^`Q-3MnsbOg;Md9clo_e~nP|f4wv-VqXV6pV;RcK+i zt&{l=G1Ra33hSHQ+MX)Wp5dmsm>t^H0Yk|m6L%_g^Y>IQ{;>90Ol@3cqU6EQT*|kx zQ~KuQNEX!cW#I&}zSfV0%-eBOISM$(&mi!Wrngwfw3MpreOg-n;sltJt9r}ig{(QO z?C7=Tov)_6PmH809k@T^pw7R4@W$6ZeqC<}Z4e#I!7aPV5w7MG zJWLezSX!Wk=bwFpJHw2Wxna_0tk?>_*b0B?+1)8>TOa4=h<(|bA3p{-9RsCBG6}G3 zeJ{PZBs|FKjL*5PhgjC)*LE8!KT92Dt=j6QLr<5qh7=dbVKjL9tjmQ=LZ_Y%>l8^| zjfbY!2TZ2;&HBi$BtlFWj_GZoLde8NA!K!mL&+^TIeG9*tY@w(2Z7$klJRwclXjln%pZ^O)Je-3V5Z7O#G>14GJPrwpM^x;fskUs~GBW|X4N+6c*Y z$Ivg&=ZmCoCuCV#H=TJ~7@l|h#?t3=9(B9O5@AX@8uI;nhRi!F^GF%(6NELb+fgqo z^I@(coluFXL+YczU{k!3lWnf$BD+0d`+V7#IVj3Ly;~2vwRW`UiI8vsS2(u~eJw$j z@31$lGrB|Fd@0BMcSN+q$veu@*qjmeFQ^$6U6y)Fc_Rf90uHpiS#z9U3{$jK{6Pso zN-^9%cG!-E{j?fqv4X@r9Tk2N)SHxRBy1MCSWFeIAN!wXCM`F zeT%zP+baVv&hF#}?0Iw*pW+iGNcJnKffn@srbe#_@;RCq++aY)eZgE>7Hp`9$ICTB z&xl32msA8slagyj#D*cIiP zOYQVIOfjBDq~Y%;mw0(YeSOeU8_r*gGVQh{sf3WBfZ1@7sZ9ct z96d7Jd6)_U@{tK(yz<5*bS9arBmORA1N;2i5Qm0Hu)MeqtN<;BlW&o*v;~Y*ZEbkZ zA^eq+ceL-M5zTtXeI3N{m8^EYSd(XF6cE{*+VblaYrG$$ug$rPi3_GD7C;*T;J|X!vB6e85L_Ad+Y8oDqE??lEsFJ5sM! zPkv75xaqveLHH=_{h`-R%uUv3Cas}++`B)bTNYXH4rq>&d0&VSfHf|^@fMX~q= z-`-1Z3Uta5_)c0x`;-R476s1G!M&WLuBqTywjM)Q5yg+Ups2ekwiPCw^G|x4ODJ<= zCDy(5v!PV2dUQw$3(mT9e5mMVlAy;RY_5yq)0B^k-}WsUea&PtZSJ0?);Qd4zn)f} zc;LyXDD}GLl!OC}KtS+xIq6h>O%6?Z8XBpLSl=;oQ#lQLsH#W#dR8&VgEsNpg#5Sv zu!9OCiXG9O+hGFd2DC{u={tpqK=wX^Atob#|KO?T8iYP({b0-EdzQaJG+|Cxb-09F zX01DCPg?g!! z9#{qhqB|&&1v&3YI-lSQB+3m!C>8?oKlyyif8mR7=m|*w2vIOIe6GY5e-Td{dumWJ z1P0!&dU~Z$l z!Uu--{XfVdKwky%$-hII@_}^WmYxu;G*$>{Q>2>$TAi52(*@yQzMU4bxKsvm^dXRA zHUxdh)^ag=0APh9m|Nwn-177C!EC zQ9g~;&G=R`db!-lf++28Dun&|Eu=Vx(U{8-*>eCJ4cdejB?Ysz7g)>AOnMZ}FU{~G}@^+-i==D!?yy2zOcSLLZ+}&Y= z$yd5fi-asI>4XB-dmv<8YANISJuqMIN{E@hT=f>{0+v$*sL9~=@+|Z&D86Sd0zQ3Y zL-}En*Plzf;w1{&SU`1oS&+mK){>1yMdOKMYmYrvfB50+{xc^}^*M3BczMg_NB=wY zn|AzeGv0RJBj)p)cN+{_Uqq{KA=EM$0EPO@N9$5qn@UUIjyBfePEGx?SZnoup;ckS z^YPk%koU5mpI-w)N}it?;rAyQh1T{zHgzSdCGz<_Asej-Z7JkpPcTB;C;J}MaGTQp zAHoqUO#JTbTm=ePx}9GqQ&-0sOhc96M9T-Swn97?^sbA@V3hX^f!Q=lP3x0TN9NVT z?o(>G%Ab2P^&UhxycM2eR_kG~jtx9NA!dCovxHYszrpXO<>_`u#3tK4UQ^&zCw-T? z6NfoR@K_YMR2AS3)pNUz-_m9sOara?gD(BsziM1{W!2sA({jS$xBB|E8sIA(3*td3L zEe=CKdOxsSX7X87R3Yt??jv*V$<@-0(-Vgle%t(DAEI|eihR4CYzxVx0@T?M7MTOx z-{Bn-_ALXwr!yf&l&PiiN6~$`MN*@W&t2M^gzSBreG)%P*_~z?=XB-8Xhek8fdLFb zHObQA@!FhxDpZD4lvep9cF7myFhJ`)*^2>$!K>$!$IYBOgu_Ms@ww*OUX=t}-OL&h zX{gErA?=8&7F~LO=O*8A;!-2r?xGX&2)(p+>Msm93JpueL2Euq^se;+hMBYod|;yv zHF!ov8LS0_!j#e5HxFq~zwj3S{KHCa!O|eX=3BR^((lR9$LFkE*ooaLgwf|3P?n6eb87xkfy*y>}#(U%8k{RY+B4A-?(Q>zYFg+ z9P0*AVV=;7RvAwHW)SZO!)*xZK)KNUuxK;Fap)Giw-bIp+GvxysXha1>x&=!6aB59 zGlh9MYfV0UBh=(SjB%2pg$7Jcsa2`{Ua&F|SgL>rTPXUB87$HRGUEo*ZiKNjCrC(e zITbEgo%v*?fFyi15q;6UGlBU>-)}z-2%w(0vaHVsVSeWLE+L^Won1aH`CCAu_X7#P zw&3=%hrvA5!`xoBVtck+Dxs!5U2Y!)BGWpmE^=OO>g*orHeQ5VIE@(o=Jr01$Y3;{ zk}MUgBrBa1zq{wl?ZwTx*hGwV{B09@_|x)7&6P+%?668{opkF7dHy$H{a)~Lqp3Ff ziv$KXUM*oH1%U`TJcW{{H)W!0?x{@G&M}F5KdZhlxUeU%M9)}}e)`5@0V*m8TT!_TwM(DkkfsK`Fi401GsrC{(_F!tj&u*pv zgs)qgz(w{V2nBp$g@QLQtOuQi4(-xbW_UXMnE(-n!HayatmX;2BORT#m&;VGSQ5l? z3oPXelu#B&J4QibzAHBIeA-l4>cA+9yFImqI&yxL^x;kMDl=RuQMY}44lvK=l3EeU zcL*BT5Lf4yyU!8MKp=q`fE)MN(s=~TyJ99x=(&#ptA{Z;IhioKfi~?&{wcj{Gh^OB zsc?;<);6TWD4^?De39xJ(tbAJp)Ki9alcdY1)KoR52qG{n!o zndYVYw0twfW{gJ(Bc;0(IzXveh2&|?kE`84<2FfJsUh7Ix#D=t4%zj@v-%vvSMEs< zwJYew5r8KLS~0Ah<3VdriX*?*#}s`ZYxt{iSmxO1>MO8enEKD<+1sLz_=5&tRCeKR zW!%z3)tjnlsA)QtkAH;8`oz%cf_ip65bbh0`Ye0!S@t{65}%Pro5X|7Y#0zpBs?82 znAL&>Zjk4zffBe|o>^^4uo{O;A;uH@tdQDT-uFl(|2AIUIjp>2S{x|Hg#||wRF^0u zOdAUIz>{Ek&?|Z919|;)&y@36>BC4UAqq#Z&fQ8}n)=L`2qk@Oo9i{Aeuo6uDeTQv z88}_bD_}-Bo3E*s&!8ir?d5^MfU|s~PqGb^ZokBU2~z)>!VS>vZ|3~XRLieJ$8}=e zuD#!CuqL$G{4SJ6Tt!|lz}^&xG<|qG0ATUrKRy2-0Lgc_jjnSoEj4dXpEC-#D+{8x zC7589%diyTK=*NODi5Y2ps>0l@qHSHij&cn#=^ImgWk*}At|TcQ z0K&=Xwq<7BH5KJi^3>fwZY%OrVa)Go){Sq9Y zRIfq$bxlbG5=baA+CnpyZT4tcCw?W)vOr zzJI_g%RGIyyNQD7j9N3X*b3H#Mqf&~P`LrBnKDq#&}xKz!jDZ2ZNhEMiW*=?8d3ZH zt?*yp^Qr)|I%hSI4g0hpbHwQtVz*&|Y_l)@;fWEUe diff --git a/src/main/asciidoc/images/epub-cover.svg b/src/main/asciidoc/images/epub-cover.svg deleted file mode 100644 index 2feb05be1d..0000000000 --- a/src/main/asciidoc/images/epub-cover.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Spring Data JPA - Reference Guide - Oliver Gierke, Thomas Darimont, - Christoph Strobl, Mark Paluch, - Jay Bryant - diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc deleted file mode 100644 index 3e3d7ec919..0000000000 --- a/src/main/asciidoc/index.adoc +++ /dev/null @@ -1,43 +0,0 @@ -= Spring Data JPA - Reference Documentation -Oliver Gierke; Thomas Darimont; Christoph Strobl; Mark Paluch; Jay Bryant; Greg Turnquist -:revnumber: {version} -:revdate: {localdate} -ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]] -:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc -:spring-framework-docs: https://docs.spring.io/spring-framework/docs/{springVersion}/spring-framework-reference/ -:feature-scroll: true - -(C) 2008-2023 The original authors. - -NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. - -include::preface.adoc[] - -include::{spring-data-commons-docs}/upgrade.adoc[leveloffset=+1] - -include::{spring-data-commons-docs}/dependencies.adoc[leveloffset=+1] - -include::{spring-data-commons-docs}/repositories.adoc[leveloffset=+1] - -[[reference]] -== Reference Documentation - -include::jpa.adoc[leveloffset=+2] - -include::envers.adoc[leveloffset=+2] - -[[appendix]] -== Appendix - -:numbered!: -include::{spring-data-commons-docs}/repository-namespace-reference.adoc[leveloffset=+1] - -include::{spring-data-commons-docs}/repository-populator-namespace-reference.adoc[leveloffset=+1] - -include::{spring-data-commons-docs}/repository-query-keywords-reference.adoc[leveloffset=+1] - -include::{spring-data-commons-docs}/repository-query-return-types-reference.adoc[leveloffset=+1] - -include::faq.adoc[leveloffset=+1] - -include::glossary.adoc[leveloffset=+1] diff --git a/src/main/asciidoc/jpa.adoc b/src/main/asciidoc/jpa.adoc deleted file mode 100644 index e8178c49df..0000000000 --- a/src/main/asciidoc/jpa.adoc +++ /dev/null @@ -1,1491 +0,0 @@ -[[jpa.repositories]] -= JPA Repositories - -This chapter points out the specialties for repository support for JPA. This builds on the core repository support explained in "`<>`". Make sure you have a sound understanding of the basic concepts explained there. - -[[jpa.introduction]] -== Introduction - -This section describes the basics of configuring Spring Data JPA through either: - -* "`<>`" (XML configuration) -* "`<>`" (Java configuration) - -[[jpa.java-config]] -=== Annotation-based Configuration -The Spring Data JPA repositories support can be activated through both JavaConfig as well as a custom XML namespace, as shown in the following example: - -.Spring Data JPA repositories using JavaConfig -==== -[source, java] ----- -@Configuration -@EnableJpaRepositories -@EnableTransactionManagement -class ApplicationConfig { - - @Bean - public DataSource dataSource() { - - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.HSQL).build(); - } - - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - - HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); - vendorAdapter.setGenerateDdl(true); - - LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); - factory.setJpaVendorAdapter(vendorAdapter); - factory.setPackagesToScan("com.acme.domain"); - factory.setDataSource(dataSource()); - return factory; - } - - @Bean - public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { - - JpaTransactionManager txManager = new JpaTransactionManager(); - txManager.setEntityManagerFactory(entityManagerFactory); - return txManager; - } -} ----- -==== -NOTE: You must create `LocalContainerEntityManagerFactoryBean` and not `EntityManagerFactory` directly, since the former also participates in exception translation mechanisms in addition to creating `EntityManagerFactory`. - -The preceding configuration class sets up an embedded HSQL database by using the `EmbeddedDatabaseBuilder` API of `spring-jdbc`. Spring Data then sets up an `EntityManagerFactory` and uses Hibernate as the sample persistence provider. The last infrastructure component declared here is the `JpaTransactionManager`. Finally, the example activates Spring Data JPA repositories by using the `@EnableJpaRepositories` annotation, which essentially carries the same attributes as the XML namespace. If no base package is configured, it uses the one in which the configuration class resides. - -[[jpa.namespace]] -=== Spring Namespace - -The JPA module of Spring Data contains a custom namespace that allows defining repository beans. It also contains certain features and element attributes that are special to JPA. Generally, the JPA repositories can be set up by using the `repositories` element, as shown in the following example: - -.Setting up JPA repositories by using the namespace -==== -[source, xml] ----- - - - - - - ----- -==== - -TIP: Which is better, JavaConfig or XML? XML is how Spring was configured long ago. In today's era of fast-growing Java, record types, annotations, and more, new projects typically use as much pure Java as possible. While there is no immediate plan to remove XML support, some of the newest features MAY not be available through XML. - -Using the `repositories` element looks up Spring Data repositories as described in "`<>`". Beyond that, it activates persistence exception translation for all beans annotated with `@Repository`, to let exceptions being thrown by the JPA persistence providers be converted into Spring's `DataAccessException` hierarchy. - -[[jpa.namespace.custom-namespace-attributes]] -==== Custom Namespace Attributes -Beyond the default attributes of the `repositories` element, the JPA namespace offers additional attributes to let you gain more detailed control over the setup of the repositories: - -.Custom JPA-specific attributes of the `repositories` element -[options = "autowidth"] -|=============== -|`entity-manager-factory-ref`|Explicitly wire the `EntityManagerFactory` to be used with the repositories being detected by the `repositories` element. Usually used if multiple `EntityManagerFactory` beans are used within the application. If not configured, Spring Data automatically looks up the `EntityManagerFactory` bean with the name `entityManagerFactory` in the `ApplicationContext`. -|`transaction-manager-ref`|Explicitly wire the `PlatformTransactionManager` to be used with the repositories being detected by the `repositories` element. Usually only necessary if multiple transaction managers or `EntityManagerFactory` beans have been configured. Default to a single defined `PlatformTransactionManager` inside the current `ApplicationContext`. -|=============== - -NOTE: Spring Data JPA requires a `PlatformTransactionManager` bean named `transactionManager` to be present if no explicit `transaction-manager-ref` is defined. - -[[jpa.bootstrap-mode]] -=== Bootstrap Mode - -By default, Spring Data JPA repositories are default Spring beans. -They are singleton scoped and eagerly initialized. -During startup, they already interact with the JPA `EntityManager` for verification and metadata analysis purposes. -Spring Framework supports the initialization of the JPA `EntityManagerFactory` in a background thread because that process usually takes up a significant amount of startup time in a Spring application. -To make use of that background initialization effectively, we need to make sure that JPA repositories are initialized as late as possible. - -As of Spring Data JPA 2.1 you can now configure a `BootstrapMode` (either via the `@EnableJpaRepositories` annotation or the XML namespace) that takes the following values: - -* `DEFAULT` (default) -- Repositories are instantiated eagerly unless explicitly annotated with `@Lazy`. -The lazification only has effect if no client bean needs an instance of the repository as that will require the initialization of the repository bean. -* `LAZY` -- Implicitly declares all repository beans lazy and also causes lazy initialization proxies to be created to be injected into client beans. -That means, that repositories will not get instantiated if the client bean is simply storing the instance in a field and not making use of the repository during initialization. -Repository instances will be initialized and verified upon first interaction with the repository. -* `DEFERRED` -- Fundamentally the same mode of operation as `LAZY`, but triggering repository initialization in response to an `ContextRefreshedEvent` so that repositories are verified before the application has completely started. - -[[jpa.bootstrap-mode.recommendations]] -==== Recommendations - -If you're not using asynchronous JPA bootstrap stick with the default bootstrap mode. - -In case you bootstrap JPA asynchronously, `DEFERRED` is a reasonable default as it will make sure the Spring Data JPA bootstrap only waits for the `EntityManagerFactory` setup if that itself takes longer than initializing all other application components. -Still, it makes sure that repositories are properly initialized and validated before the application signals it's up. - -`LAZY` is a decent choice for testing scenarios and local development. -Once you are pretty sure that repositories can properly bootstrap, or in cases where you are testing other parts of the application, running verification for all repositories might unnecessarily increase the startup time. -The same applies to local development in which you only access parts of the application that might need to have a single repository initialized. - -[[jpa.entity-persistence]] -== Persisting Entities - -This section describes how to persist (save) entities with Spring Data JPA. - -[[jpa.entity-persistence.saving-entites]] -=== Saving Entities - -Saving an entity can be performed with the `CrudRepository.save(…)` method. It persists or merges the given entity by using the underlying JPA `EntityManager`. If the entity has not yet been persisted, Spring Data JPA saves the entity with a call to the `entityManager.persist(…)` method. Otherwise, it calls the `entityManager.merge(…)` method. - -[[jpa.entity-persistence.saving-entites.strategies]] -==== Entity State-detection Strategies -Spring Data JPA offers the following strategies to detect whether an entity is new or not: - -1. Version-Property and Id-Property inspection (*default*): - By default Spring Data JPA inspects first if there is a Version-property of non-primitive type. - If there is, the entity is considered new if the value of that property is `null`. - Without such a Version-property Spring Data JPA inspects the identifier property of the given entity. - If the identifier property is `null`, then the entity is assumed to be new. - Otherwise, it is assumed to be not new. -2. Implementing `Persistable`: If an entity implements `Persistable`, Spring Data JPA delegates the new detection to the `isNew(…)` method of the entity. See the link:$$https://docs.spring.io/spring-data/data-commons/docs/current/api/index.html?org/springframework/data/domain/Persistable.html$$[JavaDoc] for details. -3. Implementing `EntityInformation`: You can customize the `EntityInformation` abstraction used in the `SimpleJpaRepository` implementation by creating a subclass of `JpaRepositoryFactory` and overriding the `getEntityInformation(…)` method accordingly. You then have to register the custom implementation of `JpaRepositoryFactory` as a Spring bean. Note that this should be rarely necessary. See the link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/index.html?org/springframework/data/jpa/repository/support/JpaRepositoryFactory.html$$[JavaDoc] for details. - -Option 1 is not an option for entities that use manually assigned identifiers and no version attribute as with those the identifier will always be non-`null`. -A common pattern in that scenario is to use a common base class with a transient flag defaulting to indicate a new instance and using JPA lifecycle callbacks to flip that flag on persistence operations: - -.A base class for entities with manually assigned identifiers -==== -[source, java] ----- -@MappedSuperclass -public abstract class AbstractEntity implements Persistable { - - @Transient - private boolean isNew = true; <1> - - @Override - public boolean isNew() { - return isNew; <2> - } - - @PrePersist <3> - @PostLoad - void markNotNew() { - this.isNew = false; - } - - // More code… -} ----- -<1> Declare a flag to hold the new state. Transient so that it's not persisted to the database. -<2> Return the flag in the implementation of `Persistable.isNew()` so that Spring Data repositories know whether to call `EntityManager.persist()` or `….merge()`. -<3> Declare a method using JPA entity callbacks so that the flag is switched to indicate an existing entity after a repository call to `save(…)` or an instance creation by the persistence provider. -==== - -[[jpa.query-methods]] -== Query Methods - -This section describes the various ways to create a query with Spring Data JPA. - -[[jpa.sample-app.finders.strategies]] -=== Query Lookup Strategies - -The JPA module supports defining a query manually as a String or having it being derived from the method name. - -Derived queries with the predicates `IsStartingWith`, `StartingWith`, `StartsWith`, `IsEndingWith`, `EndingWith`, `EndsWith`, -`IsNotContaining`, `NotContaining`, `NotContains`, `IsContaining`, `Containing`, `Contains` the respective arguments for these queries will get sanitized. -This means if the arguments actually contain characters recognized by `LIKE` as wildcards these will get escaped so they match only as literals. -The escape character used can be configured by setting the `escapeCharacter` of the `@EnableJpaRepositories` annotation. -Compare with <>. - -[[jpa.query-methods.declared-queries]] -==== Declared Queries -Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention (see <> for more information) or rather annotate your query method with `@Query` (see <> for details). - -[[jpa.query-methods.query-creation]] -=== Query Creation - -Generally, the query creation mechanism for JPA works as described in "`<>`". The following example shows what a JPA query method translates into: - -.Query creation from method names -==== ----- -public interface UserRepository extends Repository { - - List findByEmailAddressAndLastname(String emailAddress, String lastname); -} ----- -We create a query using the JPA criteria API from this, but, essentially, this translates into the following query: `select u from User u where u.emailAddress = ?1 and u.lastname = ?2`. Spring Data JPA does a property check and traverses nested properties, as described in "`<>`". -==== - -The following table describes the keywords supported for JPA and what a method containing that keyword translates to: - -.Supported keywords inside method names -[options = "header, autowidth"] -|=============== -|Keyword|Sample|JPQL snippet -|`Distinct`|`findDistinctByLastnameAndFirstname`|`select distinct ... where x.lastname = ?1 and x.firstname = ?2` -|`And`|`findByLastnameAndFirstname`|`… where x.lastname = ?1 and x.firstname = ?2` -|`Or`|`findByLastnameOrFirstname`|`… where x.lastname = ?1 or x.firstname = ?2` -|`Is`, `Equals`|`findByFirstname`,`findByFirstnameIs`,`findByFirstnameEquals`|`… where x.firstname = ?1` -|`Between`|`findByStartDateBetween`|`… where x.startDate between ?1 and ?2` -|`LessThan`|`findByAgeLessThan`|`… where x.age < ?1` -|`LessThanEqual`|`findByAgeLessThanEqual`|`… where x.age \<= ?1` -|`GreaterThan`|`findByAgeGreaterThan`|`… where x.age > ?1` -|`GreaterThanEqual`|`findByAgeGreaterThanEqual`|`… where x.age >= ?1` -|`After`|`findByStartDateAfter`|`… where x.startDate > ?1` -|`Before`|`findByStartDateBefore`|`… where x.startDate < ?1` -|`IsNull`, `Null`|`findByAge(Is)Null`|`… where x.age is null` -|`IsNotNull`, `NotNull`|`findByAge(Is)NotNull`|`… where x.age not null` -|`Like`|`findByFirstnameLike`|`… where x.firstname like ?1` -|`NotLike`|`findByFirstnameNotLike`|`… where x.firstname not like ?1` -|`StartingWith`|`findByFirstnameStartingWith`|`… where x.firstname like ?1` (parameter bound with appended `%`) -|`EndingWith`|`findByFirstnameEndingWith`|`… where x.firstname like ?1` (parameter bound with prepended `%`) -|`Containing`|`findByFirstnameContaining`|`… where x.firstname like ?1` (parameter bound wrapped in `%`) -|`OrderBy`|`findByAgeOrderByLastnameDesc`|`… where x.age = ?1 order by x.lastname desc` -|`Not`|`findByLastnameNot`|`… where x.lastname <> ?1` -|`In`|`findByAgeIn(Collection ages)`|`… where x.age in ?1` -|`NotIn`|`findByAgeNotIn(Collection ages)`|`… where x.age not in ?1` -|`True`|`findByActiveTrue()`|`… where x.active = true` -|`False`|`findByActiveFalse()`|`… where x.active = false` -|`IgnoreCase`|`findByFirstnameIgnoreCase`|`… where UPPER(x.firstname) = UPPER(?1)` -|=============== - -NOTE: `In` and `NotIn` also take any subclass of `Collection` as a parameter as well as arrays or varargs. For other syntactical versions of the same logical operator, check "`<>`". - -[WARNING] -==== -`DISTINCT` can be tricky and not always producing the results you expect. -For example, `select distinct u from User u` will produce a complete different result than `select distinct u.lastname from User u`. -In the first case, since you are including `User.id`, nothing will duplicated, hence you'll get the whole table, and it would be of `User` objects. - -However, that latter query would narrow the focus to just `User.lastname` and find all unique last names for that table. -This would also yield a `List` result set instead of a `List result set. - - -`countDistinctByLastname(String lastname)` can also produce unexpected results. -Spring Data JPA will derive `select count(distinct u.id) from User u where u.lastname = ?1`. -Again, since `u.id` won't hit any duplicates, this query will count up all the users that had the binding last name. -Which would the same as `countByLastname(String lastname)`! - -What is the point of this query anyway? To find the number of people with a given last name? To find the number of _distinct_ people with that binding last name? -To find the number of _distinct last names_? (That last one is an entirely different query!) -Using `distinct` sometimes requires writing the query by hand and using `@Query` to best capture the information you seek, since you also may be needing a projection -to capture the result set. -==== - -[[jpa.query-methods.named-queries.annotation-based-configuration]] -==== Annotation-based Configuration -Annotation-based configuration has the advantage of not needing another configuration file to be edited, lowering maintenance effort. You pay for that benefit by the need to recompile your domain class for every new query declaration. - -.Annotation-based named query configuration -==== -[source, java] ----- -@Entity -@NamedQuery(name = "User.findByEmailAddress", - query = "select u from User u where u.emailAddress = ?1") -public class User { - -} ----- -==== - -[[jpa.query-methods.named-queries]] -=== Using JPA Named Queries - -NOTE: The examples use the `` element and `@NamedQuery` annotation. The queries for these configuration elements have to be defined in the JPA query language. Of course, you can use `` or `@NamedNativeQuery` too. These elements let you define the query in native SQL by losing the database platform independence. - -[[jpa.query-methods.named-queries.xml-named-query-definition]] -==== XML Named Query Definition -To use XML configuration, add the necessary `` element to the `orm.xml` JPA configuration file located in the `META-INF` folder of your classpath. Automatic invocation of named queries is enabled by using some defined naming convention. For more details, see below. - -.XML named query configuration -==== -[source, xml] ----- - - select u from User u where u.lastname = ?1 - ----- -==== - -The query has a special name that is used to resolve it at runtime. - -[[jpa.query-methods.named-queries.declaring-interfaces]] -==== Declaring Interfaces -To allow these named queries, specify the `UserRepositoryWithRewriter` as follows: - -.Query method declaration in UserRepository -==== -[source, java] ----- -public interface UserRepository extends JpaRepository { - - List findByLastname(String lastname); - - User findByEmailAddress(String emailAddress); -} ----- -==== - -Spring Data tries to resolve a call to these methods to a named query, starting with the simple name of the configured domain class, followed by the method name separated by a dot. -So the preceding example would use the named queries defined earlier instead of trying to create a query from the method name. - -[[jpa.query-methods.at-query]] -=== Using `@Query` - -Using named queries to declare queries for entities is a valid approach and works fine for a small number of queries. As the queries themselves are tied to the Java method that runs them, you can actually bind them directly by using the Spring Data JPA `@Query` annotation rather than annotating them to the domain class. This frees the domain class from persistence specific information and co-locates the query to the repository interface. - -Queries annotated to the query method take precedence over queries defined using `@NamedQuery` or named queries declared in `orm.xml`. - -The following example shows a query created with the `@Query` annotation: - -.Declare query at the query method using `@Query` -==== -[source, java] ----- -public interface UserRepository extends JpaRepository { - - @Query("select u from User u where u.emailAddress = ?1") - User findByEmailAddress(String emailAddress); -} ----- -==== - -[[jpa.query-methods.query-rewriter]] -==== Applying a QueryRewriter - -Sometimes, no matter how many features you try to apply, it seems impossible to get Spring Data JPA to apply every thing -you'd like to a query before it is sent to the `EntityManager`. - -You have the ability to get your hands on the query, right before it's sent to the `EntityManager` and "rewrite" it. That is, -you can make any alterations at the last moment. - -.Declare a QueryRewriter using `@Query` -==== -[source, java] ----- -public interface MyRepository extends JpaRepository { - - @Query(value = "select original_user_alias.* from SD_USER original_user_alias", - nativeQuery = true, - queryRewriter = MyQueryRewriter.class) - List findByNativeQuery(String param); - - @Query(value = "select original_user_alias from User original_user_alias", - queryRewriter = MyQueryRewriter.class) - List findByNonNativeQuery(String param); -} ----- -==== - -This example shows both a native (pure SQL) rewriter as well as a JPQL query, both leveraging the same `QueryRewriter`. -In this scenario, Spring Data JPA will look for a bean registered in the application context of the corresponding type. - -You can write a query rewriter like this: - -.Example `QueryRewriter` -==== -[source, java] ----- -public class MyQueryRewriter implements QueryRewriter { - - @Override - public String rewrite(String query, Sort sort) { - return query.replaceAll("original_user_alias", "rewritten_user_alias"); - } -} ----- -==== - -You have to ensure your `QueryRewriter` is registered in the application context, whether it's by applying one of Spring Framework's -`@Component`-based annotations, or having it as part of a `@Bean` method inside an `@Configuration` class. - -Another option is to have the repository itself implement the interface. - -.Repository that provides the `QueryRewriter` -==== -[source, java] ----- -public interface MyRepository extends JpaRepository, QueryRewriter { - - @Query(value = "select original_user_alias.* from SD_USER original_user_alias", - nativeQuery = true, - queryRewriter = MyRepository.class) - List findByNativeQuery(String param); - - @Query(value = "select original_user_alias from User original_user_alias", - queryRewriter = MyRepository.class) - List findByNonNativeQuery(String param); - - @Override - default String rewrite(String query, Sort sort) { - return query.replaceAll("original_user_alias", "rewritten_user_alias"); - } -} ----- -==== - -Depending on what you're doing with your `QueryRewriter`, it may be advisable to have more than one, each registered with the -application context. - -NOTE: In a CDI-based environment, Spring Data JPA will search the `BeanManager` for instances of your implementation of -`QueryRewriter`. - - -[[jpa.query-methods.at-query.advanced-like]] -==== Using Advanced `LIKE` Expressions - -The query running mechanism for manually defined queries created with `@Query` allows the definition of advanced `LIKE` expressions inside the query definition, as shown in the following example: - -.Advanced `like` expressions in @Query -==== -[source, java] ----- -public interface UserRepository extends JpaRepository { - - @Query("select u from User u where u.firstname like %?1") - List findByFirstnameEndsWith(String firstname); -} ----- -==== - -In the preceding example, the `LIKE` delimiter character (`%`) is recognized, and the query is transformed into a valid JPQL query (removing the `%`). Upon running the query, the parameter passed to the method call gets augmented with the previously recognized `LIKE` pattern. - -[[jpa.query-methods.at-query.native]] -==== Native Queries - -The `@Query` annotation allows for running native queries by setting the `nativeQuery` flag to true, as shown in the following example: - -.Declare a native query at the query method using @Query -==== -[source, java] ----- -public interface UserRepository extends JpaRepository { - - @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true) - User findByEmailAddress(String emailAddress); -} ----- - -==== - -NOTE: Spring Data JPA does not currently support dynamic sorting for native queries, because it would have to manipulate the actual query declared, which it cannot do reliably for native SQL. You can, however, use native queries for pagination by specifying the count query yourself, as shown in the following example: - -.Declare native count queries for pagination at the query method by using `@Query` -==== -[source, java] ----- -public interface UserRepository extends JpaRepository { - - @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1", - countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1", - nativeQuery = true) - Page findByLastname(String lastname, Pageable pageable); -} ----- - -==== - -A similar approach also works with named native queries, by adding the `.count` suffix to a copy of your query. You probably need to register a result set mapping for your count query, though. - -[[jpa.query-methods.sorting]] -=== Using Sort - -Sorting can be done by either providing a `PageRequest` or by using `Sort` directly. The properties actually used within the `Order` instances of `Sort` need to match your domain model, which means they need to resolve to either a property or an alias used within the query. The JPQL defines this as a state field path expression. - -NOTE: Using any non-referenceable path expression leads to an `Exception`. - -However, using `Sort` together with <> lets you sneak in non-path-checked `Order` instances containing functions within the `ORDER BY` clause. This is possible because the `Order` is appended to the given query string. By default, Spring Data JPA rejects any `Order` instance containing function calls, but you can use `JpaSort.unsafe` to add potentially unsafe ordering. - -The following example uses `Sort` and `JpaSort`, including an unsafe option on `JpaSort`: - -.Using `Sort` and `JpaSort` -==== -[source, java] ----- -public interface UserRepository extends JpaRepository { - - @Query("select u from User u where u.lastname like ?1%") - List findByAndSort(String lastname, Sort sort); - - @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%") - List findByAsArrayAndSort(String lastname, Sort sort); -} - -repo.findByAndSort("lannister", Sort.by("firstname")); <1> -repo.findByAndSort("stark", Sort.by("LENGTH(firstname)")); <2> -repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); <3> -repo.findByAsArrayAndSort("bolton", Sort.by("fn_len")); <4> ----- - -<1> Valid `Sort` expression pointing to property in domain model. -<2> Invalid `Sort` containing function call. -Throws Exception. -<3> Valid `Sort` containing explicitly _unsafe_ `Order`. -<4> Valid `Sort` expression pointing to aliased function. -==== - -[[jpa.query-methods.scroll]] -=== Scrolling Large Query Results - -When working with large data sets, <> can help to process those results efficiently without loading all results into memory. - -You have multiple options to consume large query results: - -1. <>. -You have learned in the previous chapter about `Pageable` and `PageRequest`. -2. <>. -This is a lighter variant than paging because it does not require the total result count. -3. <>. -This method avoids https://use-the-index-luke.com/no-offset[the shortcomings of offset-based result retrieval by leveraging database indexes]. - -Read more on <> for your particular arrangement. - -You can use the Scroll API with query methods, <>, and <>. - -NOTE: Scrolling with String-based query methods is not yet supported. -Scrolling is also not supported using stored `@Procedure` query methods. - -[[jpa.named-parameters]] -=== Using Named Parameters - -By default, Spring Data JPA uses position-based parameter binding, as described in all the preceding examples. -This makes query methods a little error-prone when refactoring regarding the parameter position. -To solve this issue, you can use `@Param` annotation to give a method parameter a concrete name and bind the name in the query, as shown in the following example: - -.Using named parameters -==== -[source,java] ----- -public interface UserRepository extends JpaRepository { - - @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname") - User findByLastnameOrFirstname(@Param("lastname") String lastname, - @Param("firstname") String firstname); -} ----- -==== - -NOTE: The method parameters are switched according to their order in the defined query. - -NOTE: As of version 4, Spring fully supports Java 8’s parameter name discovery based on the `-parameters` compiler flag. By using this flag in your build as an alternative to debug information, you can omit the `@Param` annotation for named parameters. - -[[jpa.query.spel-expressions]] -=== Using SpEL Expressions - -As of Spring Data JPA release 1.4, we support the usage of restricted SpEL template expressions in manually defined queries that are defined with `@Query`. Upon the query being run, these expressions are evaluated against a predefined set of variables. Spring Data JPA supports a variable called `entityName`. Its usage is `select x from #{#entityName} x`. It inserts the `entityName` of the domain type associated with the given repository. The `entityName` is resolved as follows: If the domain type has set the name property on the `@Entity` annotation, it is used. Otherwise, the simple class-name of the domain type is used. - -The following example demonstrates one use case for the `+#{#entityName}+` expression in a query string where you want to define a repository interface with a query method and a manually defined query: - -.Using SpEL expressions in repository query methods - entityName -==== -[source, java] ----- -@Entity -public class User { - - @Id - @GeneratedValue - Long id; - - String lastname; -} - -public interface UserRepository extends JpaRepository { - - @Query("select u from #{#entityName} u where u.lastname = ?1") - List findByLastname(String lastname); -} ----- -==== - -To avoid stating the actual entity name in the query string of a `@Query` annotation, you can use the `+#{#entityName}+` variable. - -NOTE: The `entityName` can be customized by using the `@Entity` annotation. Customizations in `orm.xml` are not supported for the SpEL expressions. - -Of course, you could have just used `User` in the query declaration directly, but that would require you to change the query as well. The reference to `#entityName` picks up potential future remappings of the `User` class to a different entity name (for example, by using `@Entity(name = "MyUser")`. - -Another use case for the `#{#entityName}` expression in a query string is if you want to define a generic repository interface with specialized repository interfaces for a concrete domain type. To not repeat the definition of custom query methods on the concrete interfaces, you can use the entity name expression in the query string of the `@Query` annotation in the generic repository interface, as shown in the following example: - -.Using SpEL expressions in repository query methods - entityName with inheritance -==== -[source, java] ----- -@MappedSuperclass -public abstract class AbstractMappedType { - … - String attribute -} - -@Entity -public class ConcreteType extends AbstractMappedType { … } - -@NoRepositoryBean -public interface MappedTypeRepository - extends Repository { - - @Query("select t from #{#entityName} t where t.attribute = ?1") - List findAllByAttribute(String attribute); -} - -public interface ConcreteRepository - extends MappedTypeRepository { … } ----- -==== - -In the preceding example, the `MappedTypeRepository` interface is the common parent interface for a few domain types extending `AbstractMappedType`. It also defines the generic `findAllByAttribute(…)` method, which can be used on instances of the specialized repository interfaces. If you now invoke `findByAllAttribute(…)` on `ConcreteRepository`, the query becomes `select t from ConcreteType t where t.attribute = ?1`. - -SpEL expressions to manipulate arguments may also be used to manipulate method arguments. -In these SpEL expressions the entity name is not available, but the arguments are. -They can be accessed by name or index as demonstrated in the following example. - -.Using SpEL expressions in repository query methods - accessing arguments. -==== -[source, java] ----- -@Query("select u from User u where u.firstname = ?1 and u.firstname=?#{[0]} and u.emailAddress = ?#{principal.emailAddress}") -List findByFirstnameAndCurrentUserWithCustomQuery(String firstname); ----- -==== - -For `like`-conditions one often wants to append `%` to the beginning or the end of a String valued parameter. -This can be done by appending or prefixing a bind parameter marker or a SpEL expression with `%`. -Again the following example demonstrates this. - -.Using SpEL expressions in repository query methods - wildcard shortcut. -==== -[source, java] ----- -@Query("select u from User u where u.lastname like %:#{[0]}% and u.lastname like %:lastname%") -List findByLastnameWithSpelExpression(@Param("lastname") String lastname); ----- -==== - -When using `like`-conditions with values that are coming from a not secure source the values should be sanitized so they can't contain any wildcards and thereby allow attackers to select more data than they should be able to. -For this purpose the `escape(String)` method is made available in the SpEL context. -It prefixes all instances of `_` and `%` in the first argument with the single character from the second argument. -In combination with the `escape` clause of the `like` expression available in JPQL and standard SQL this allows easy cleaning of bind parameters. - - -.Using SpEL expressions in repository query methods - sanitizing input values. -==== -[source, java] ----- -@Query("select u from User u where u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}") -List findContainingEscaped(String namePart); ----- -==== - -Given this method declaration in a repository interface `findContainingEscaped("Peter_")` will find `Peter_Parker` but not `Peter Parker`. -The escape character used can be configured by setting the `escapeCharacter` of the `@EnableJpaRepositories` annotation. -Note that the method `escape(String)` available in the SpEL context will only escape the SQL and JPQL standard wildcards `_` and `%`. -If the underlying database or the JPA implementation supports additional wildcards these will not get escaped. - -[[jpa.query.other-methods]] -=== Other Methods - -Spring Data JPA offers many ways to build queries. -But sometimes, your query may simply be too complicated for the techniques offered. -In that situation, consider: - -* If you haven't already, simply write the query yourself using <>. -* If that doesn't fit your needs, consider implementing a <>. This lets you register a method in your repository while leaving the implementation completely up to you. This gives you the ability to: -** Talk directly to the `EntityManager` (writing pure HQL/JPQL/EQL/native SQL or using the *Criteria API*) -** Leverage Spring Framework's `JdbcTemplate` (native SQL) -** Use another 3rd-party database toolkit. -* Another option is putting your query inside the database and then using either Spring Data JPA's <> or if it's a database function using the <> and invoking it with a `CALL`. - -These tactics may be most effective when you need maximum control of your query, while still letting Spring Data JPA provide resource management. - -[[jpa.modifying-queries]] -=== Modifying Queries - -All the previous sections describe how to declare queries to access a given entity or collection of entities. -You can add custom modifying behavior by using the custom method facilities described in "`<>`". -As this approach is feasible for comprehensive custom functionality, you can modify queries that only need parameter binding by annotating the query method with `@Modifying`, as shown in the following example: - -.Declaring manipulating queries -==== -[source, java] ----- -@Modifying -@Query("update User u set u.firstname = ?1 where u.lastname = ?2") -int setFixedFirstnameFor(String firstname, String lastname); ----- -==== - -Doing so triggers the query annotated to the method as an updating query instead of a selecting one. As the `EntityManager` might contain outdated entities after the execution of the modifying query, we do not automatically clear it (see the https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/entitymanager[JavaDoc] of `EntityManager.clear()` for details), since this effectively drops all non-flushed changes still pending in the `EntityManager`. -If you wish the `EntityManager` to be cleared automatically, you can set the `@Modifying` annotation's `clearAutomatically` attribute to `true`. - -The `@Modifying` annotation is only relevant in combination with the `@Query` annotation. -Derived query methods or custom methods do not require this annotation. - -[[jpa.modifying-queries.derived-delete]] -==== Derived Delete Queries -Spring Data JPA also supports derived delete queries that let you avoid having to declare the JPQL query explicitly, as shown in the following example: - -.Using a derived delete query -==== -[source, java] ----- -interface UserRepository extends Repository { - - void deleteByRoleId(long roleId); - - @Modifying - @Query("delete from User u where u.role.id = ?1") - void deleteInBulkByRoleId(long roleId); -} ----- -==== - -Although the `deleteByRoleId(…)` method looks like it basically produces the same result as the `deleteInBulkByRoleId(…)`, there is an important difference between the two method declarations in terms of the way they are run. -As the name suggests, the latter method issues a single JPQL query (the one defined in the annotation) against the database. -This means even currently loaded instances of `User` do not see lifecycle callbacks invoked. - -To make sure lifecycle queries are actually invoked, an invocation of `deleteByRoleId(…)` runs a query and then deletes the returned instances one by one, so that the persistence provider can actually invoke `@PreRemove` callbacks on those entities. - -In fact, a derived delete query is a shortcut for running the query and then calling `CrudRepository.delete(Iterable users)` on the result and keeping behavior in sync with the implementations of other `delete(…)` methods in `CrudRepository`. - -[[jpa.query-hints]] -=== Applying Query Hints -To apply JPA query hints to the queries declared in your repository interface, you can use the `@QueryHints` annotation. It takes an array of JPA `@QueryHint` annotations plus a boolean flag to potentially disable the hints applied to the additional count query triggered when applying pagination, as shown in the following example: - -.Using QueryHints with a repository method -==== -[source, java] ----- -public interface UserRepository extends Repository { - - @QueryHints(value = { @QueryHint(name = "name", value = "value")}, - forCounting = false) - Page findByLastname(String lastname, Pageable pageable); -} ----- -==== -The preceding declaration would apply the configured `@QueryHint` for that actually query but omit applying it to the count query triggered to calculate the total number of pages. - -[[jpa.query-hints.comments]] -==== Adding Comments to Queries -Sometimes, you need to debug a query based upon database performance. -The query your database administrator shows you may look VERY different than what you wrote using `@Query`, or it may look -nothing like what you presume Spring Data JPA has generated regarding a custom finder or if you used query by example. - -To make this process easier, you can insert custom comments into almost any JPA operation, whether its a query or other operation -by applying the `@Meta` annotation. - -.Apply `@Meta` annotation to repository operations -==== -[source, java] ----- -public interface RoleRepository extends JpaRepository { - - @Meta(comment = "find roles by name") - List findByName(String name); - - @Override - @Meta(comment = "find roles using QBE") - List findAll(Example example); - - @Meta(comment = "count roles for a given name") - long countByName(String name); - - @Override - @Meta(comment = "exists based on QBE") - boolean exists(Example example); -} ----- -==== - -This sample repository has a mixture of custom finders as well as overriding the inherited operations from `JpaRepository`. -Either way, the `@Meta` annotation lets you add a `comment` that will be inserted into queries before they are sent to the database. - -It's also important to note that this feature isn't confined solely to queries. It extends to the `count` and `exists` operations. -And while not shown, it also extends to certain `delete` operations. - -IMPORTANT: While we have attempted to apply this feature everywhere possible, some operations of the underlying `EntityManager` don't support comments. For example, `entityManager.createQuery()` is clearly documented as supporting comments, but `entityManager.find()` operations do not. - -Neither JPQL logging nor SQL logging is a standard in JPA, so each provider requires custom configuration, as shown the sections below. - -===== Activating Hibernate comments -To activate query comments in Hibernate, you must set `hibernate.use_sql_comments` to `true`. - -If you are using Java-based configuration settings, this can be done like this: - -.Java-based JPA configuration -==== -[source, java] ----- -@Bean -public Properties jpaProperties() { - - Properties properties = new Properties(); - properties.setProperty("hibernate.use_sql_comments", "true"); - return properties; -} ----- -==== - -If you have a `persistence.xml` file, you can apply it there: - -.`persistence.xml`-based configuration -==== -[source, xml] ----- - - - ...registered classes... - - - - - ----- -==== - -Finally, if you are using Spring Boot, then you can set it up inside your `application.properties` file: - -.Spring Boot property-based configuration -==== ----- -spring.jpa.properties.hibernate.use_sql_comments=true ----- -==== - -===== Activating EclipseLink comments -To activate query comments in EclipseLink, you must set `eclipselink.logging.level.sql` to `FINE`. - -If you are using Java-based configuration settings, this can be done like this: - -.Java-based JPA configuration -==== -[source, java] ----- -@Bean -public Properties jpaProperties() { - - Properties properties = new Properties(); - properties.setProperty("eclipselink.logging.level.sql", "FINE"); - return properties; -} ----- -==== - -If you have a `persistence.xml` file, you can apply it there: - -.`persistence.xml`-based configuration -==== -[source, xml] ----- - - - ...registered classes... - - - - - ----- -==== - -Finally, if you are using Spring Boot, then you can set it up inside your `application.properties` file: - -.Spring Boot property-based configuration -==== ----- -spring.jpa.properties.eclipselink.logging.level.sql=FINE ----- -==== - - -[[jpa.entity-graph]] -=== Configuring Fetch- and LoadGraphs - -The JPA 2.1 specification introduced support for specifying Fetch- and LoadGraphs that we also support with the `@EntityGraph` annotation, which lets you reference a `@NamedEntityGraph` definition. You can use that annotation on an entity to configure the fetch plan of the resulting query. The type (`Fetch` or `Load`) of the fetching can be configured by using the `type` attribute on the `@EntityGraph` annotation. See the JPA 2.1 Spec 3.7.4 for further reference. - -The following example shows how to define a named entity graph on an entity: - -.Defining a named entity graph on an entity. -==== -[source, java] ----- -@Entity -@NamedEntityGraph(name = "GroupInfo.detail", - attributeNodes = @NamedAttributeNode("members")) -public class GroupInfo { - - // default fetch mode is lazy. - @ManyToMany - List members = new ArrayList(); - - … -} ----- -==== - -The following example shows how to reference a named entity graph on a repository query method: - -.Referencing a named entity graph definition on a repository query method. -==== -[source, java] ----- -public interface GroupRepository extends CrudRepository { - - @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD) - GroupInfo getByGroupName(String name); - -} ----- -==== - -It is also possible to define ad hoc entity graphs by using `@EntityGraph`. The provided `attributePaths` are translated into the according `EntityGraph` without needing to explicitly add `@NamedEntityGraph` to your domain types, as shown in the following example: - -.Using AD-HOC entity graph definition on an repository query method. -==== -[source, java] ----- -public interface GroupRepository extends CrudRepository { - - @EntityGraph(attributePaths = { "members" }) - GroupInfo getByGroupName(String name); - -} ----- -==== - -:repository-projections-trailing-dto-fragment: ../../../../spring-data-jpa/src/main/asciidoc/repository-projections-dto-limitations.adoc - -include::{spring-data-commons-docs}/repository-projections.adoc[leveloffset=+2] - -[[jpa.stored-procedures]] -== Stored Procedures -The JPA 2.1 specification introduced support for calling stored procedures by using the JPA criteria query API. -We Introduced the `@Procedure` annotation for declaring stored procedure metadata on a repository method. - -The examples to follow use the following stored procedure: - -.The definition of the `plus1inout` procedure in HSQL DB. -==== -[source, sql] ----- -/; -DROP procedure IF EXISTS plus1inout -/; -CREATE procedure plus1inout (IN arg int, OUT res int) -BEGIN ATOMIC - set res = arg + 1; -END -/; ----- -==== - -Metadata for stored procedures can be configured by using the `NamedStoredProcedureQuery` annotation on an entity type. - -[[jpa.stored-procedure-entity-metadata]] -.StoredProcedure metadata definitions on an entity. -==== -[source, java] ----- -@Entity -@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = { - @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), - @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }) -public class User {} ----- -==== - -Note that `@NamedStoredProcedureQuery` has two different names for the stored procedure. -`name` is the name JPA uses. `procedureName` is the name the stored procedure has in the database. - -You can reference stored procedures from a repository method in multiple ways. -The stored procedure to be called can either be defined directly by using the `value` or `procedureName` attribute of the `@Procedure` annotation. -This refers directly to the stored procedure in the database and ignores any configuration via `@NamedStoredProcedureQuery`. - -Alternatively you may specify the `@NamedStoredProcedureQuery.name` attribute as the `@Procedure.name` attribute. -If neither `value`, `procedureName` nor `name` is configured, the name of the repository method is used as the `name` attribute. - -The following example shows how to reference an explicitly mapped procedure: - -[[jpa.stored-procedure-reference]] -.Referencing explicitly mapped procedure with name "plus1inout" in database. -==== -[source, java] ----- -@Procedure("plus1inout") -Integer explicitlyNamedPlus1inout(Integer arg); ----- -==== - -The following example is equivalent to the previous one but uses the `procedureName` alias: - -.Referencing implicitly mapped procedure with name "plus1inout" in database via `procedureName` alias. -==== -[source, java] ----- -@Procedure(procedureName = "plus1inout") -Integer callPlus1InOut(Integer arg); ----- -==== - -The following is again equivalent to the previous two but using the method name instead of an explicite annotation attribute. - -.Referencing implicitly mapped named stored procedure "User.plus1" in `EntityManager` by using the method name. -==== -[source, java] ----- -@Procedure -Integer plus1inout(@Param("arg") Integer arg); ----- -==== - -The following example shows how to reference a stored procedure by referencing the `@NamedStoredProcedureQuery.name` attribute. - -.Referencing explicitly mapped named stored procedure "User.plus1IO" in `EntityManager`. -==== -[source, java] ----- -@Procedure(name = "User.plus1IO") -Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg); ----- -==== - -If the stored procedure getting called has a single out parameter that parameter may be returned as the return value of the method. -If there are multiple out parameters specified in a `@NamedStoredProcedureQuery` annotation those can be returned as a `Map` with the key being the parameter name given in the `@NamedStoredProcedureQuery` annotation. - - -[[specifications]] -== Specifications - -JPA 2 introduces a criteria API that you can use to build queries programmatically. By writing a `criteria`, you define the where clause of a query for a domain class. Taking another step back, these criteria can be regarded as a predicate over the entity that is described by the JPA criteria API constraints. - -Spring Data JPA takes the concept of a specification from Eric Evans' book, "`Domain Driven Design`", following the same semantics and providing an API to define such specifications with the JPA criteria API. To support specifications, you can extend your repository interface with the `JpaSpecificationExecutor` interface, as follows: - -[source, java] ----- -public interface CustomerRepository extends CrudRepository, JpaSpecificationExecutor { - … -} ----- - -The additional interface has methods that let you run specifications in a variety of ways. For example, the `findAll` method returns all entities that match the specification, as shown in the following example: - -[source, java] ----- -List findAll(Specification spec); ----- - -The `Specification` interface is defined as follows: - -[source, java] ----- -public interface Specification { - Predicate toPredicate(Root root, CriteriaQuery query, - CriteriaBuilder builder); -} ----- - -Specifications can easily be used to build an extensible set of predicates on top of an entity that then can be combined and used with `JpaRepository` without the need to declare a query (method) for every needed combination, as shown in the following example: - -.Specifications for a Customer -==== -[source, java] ----- -public class CustomerSpecs { - - - public static Specification isLongTermCustomer() { - return (root, query, builder) -> { - LocalDate date = LocalDate.now().minusYears(2); - return builder.lessThan(root.get(Customer_.createdAt), date); - }; - } - - public static Specification hasSalesOfMoreThan(MonetaryAmount value) { - return (root, query, builder) -> { - // build query here - }; - } -} ----- -==== - -The `Customer_` type is a metamodel type generated using the JPA Metamodel generator (see the link:$$https://docs.jboss.org/hibernate/jpamodelgen/1.0/reference/en-US/html_single/#whatisit$$[Hibernate implementation's documentation for an example]). -So the expression, `Customer_.createdAt`, assumes the `Customer` has a `createdAt` attribute of type `Date`. -Besides that, we have expressed some criteria on a business requirement abstraction level and created executable `Specifications`. -So a client might use a `Specification` as follows: - -.Using a simple Specification -==== -[source, java] ----- -List customers = customerRepository.findAll(isLongTermCustomer()); ----- -==== - -Why not create a query for this kind of data access? Using a single `Specification` does not gain a lot of benefit over a plain query declaration. The power of specifications really shines when you combine them to create new `Specification` objects. You can achieve this through the default methods of `Specification` we provide to build expressions similar to the following: - -.Combined Specifications -==== -[source, java] ----- -MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR); -List customers = customerRepository.findAll( - isLongTermCustomer().or(hasSalesOfMoreThan(amount))); ----- - -`Specification` offers some "`glue-code`" default methods to chain and combine `Specification` instances. These methods let you extend your data access layer by creating new `Specification` implementations and combining them with already existing implementations. -==== - -And with JPA 2.1, the `CriteriaBuilder` API introduced `CriteriaDelete`. This is provided through `JpaSpecificationExecutor`'s `delete(Specification)` API. - -.Using a `Specification` to delete entries. -==== -[source, java] ----- -Specification ageLessThan18 = (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), 18) - -userRepository.delete(ageLessThan18); ----- -The `Specification` builds up a criteria where the `age` field (cast as an integer) is less than `18`. -Passed on to the `userRepository`, it will use JPA's `CriteriaDelete` feature to generate the right `DELETE` operation. -It then returns the number of entities deleted. -==== - -include::{spring-data-commons-docs}/query-by-example.adoc[leveloffset=+1] -include::query-by-example.adoc[leveloffset=+1] - -[[transactions]] -== Transactionality - -By default, methods inherited from `CrudRepository` inherit the transactional configuration from link:$$https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html$$[`SimpleJpaRepository`]. -For read operations, the transaction configuration `readOnly` flag is set to `true`. -All others are configured with a plain `@Transactional` so that default transaction configuration applies. -Repository methods that are backed by transactional repository fragments inherit the transactional attributes from the actual fragment method. - -If you need to tweak transaction configuration for one of the methods declared in a repository, redeclare the method in your repository interface, as follows: - -.Custom transaction configuration for CRUD -==== -[source, java] ----- -public interface UserRepository extends CrudRepository { - - @Override - @Transactional(timeout = 10) - public List findAll(); - - // Further query method declarations -} ----- -Doing so causes the `findAll()` method to run with a timeout of 10 seconds and without the `readOnly` flag. -==== - -Another way to alter transactional behaviour is to use a facade or service implementation that (typically) covers more than one repository. Its purpose is to define transactional boundaries for non-CRUD operations. The following example shows how to use such a facade for more than one repository: - -.Using a facade to define transactions for multiple repository calls -==== -[source, java] ----- -@Service -public class UserManagementImpl implements UserManagement { - - private final UserRepository userRepository; - private final RoleRepository roleRepository; - - public UserManagementImpl(UserRepository userRepository, - RoleRepository roleRepository) { - this.userRepository = userRepository; - this.roleRepository = roleRepository; - } - - @Transactional - public void addRoleToAllUsers(String roleName) { - - Role role = roleRepository.findByName(roleName); - - for (User user : userRepository.findAll()) { - user.addRole(role); - userRepository.save(user); - } - } -} ----- -This example causes call to `addRoleToAllUsers(…)` to run inside a transaction (participating in an existing one or creating a new one if none are already running). The transaction configuration at the repositories is then neglected, as the outer transaction configuration determines the actual one used. Note that you must activate `` or use `@EnableTransactionManagement` explicitly to get annotation-based configuration of facades to work. -This example assumes you use component scanning. - -Note that the call to `save` is not strictly necessary from a JPA point of view, but should still be there in order to stay consistent to the repository abstraction offered by Spring Data. -==== - -[[transactional-query-methods]] -=== Transactional query methods - -Declared query methods (including default methods) do not get any transaction configuration applied by default. -To run those methods transactionally, use `@Transactional` at the repository interface you define, as shown in the following example: - -.Using @Transactional at query methods -==== -[source, java] ----- -@Transactional(readOnly = true) -interface UserRepository extends JpaRepository { - - List findByLastname(String lastname); - - @Modifying - @Transactional - @Query("delete from User u where u.active = false") - void deleteInactiveUsers(); -} ----- -Typically, you want the `readOnly` flag to be set to `true`, as most of the query methods only read data. In contrast to that, `deleteInactiveUsers()` makes use of the `@Modifying` annotation and overrides the transaction configuration. Thus, the method runs with the `readOnly` flag set to `false`. -==== - -[NOTE] -==== -You can use transactions for read-only queries and mark them as such by setting the `readOnly` flag. Doing so does not, however, act as a check that you do not trigger a manipulating query (although some databases reject `INSERT` and `UPDATE` statements inside a read-only transaction). The `readOnly` flag is instead propagated as a hint to the underlying JDBC driver for performance optimizations. Furthermore, Spring performs some optimizations on the underlying JPA provider. For example, when used with Hibernate, the flush mode is set to `NEVER` when you configure a transaction as `readOnly`, which causes Hibernate to skip dirty checks (a noticeable improvement on large object trees). -==== - -[[locking]] -== Locking -To specify the lock mode to be used, you can use the `@Lock` annotation on query methods, as shown in the following example: - -.Defining lock metadata on query methods -==== -[source, java] ----- -interface UserRepository extends Repository { - - // Plain query method - @Lock(LockModeType.READ) - List findByLastname(String lastname); -} ----- -==== - -This method declaration causes the query being triggered to be equipped with a `LockModeType` of `READ`. You can also define locking for CRUD methods by redeclaring them in your repository interface and adding the `@Lock` annotation, as shown in the following example: - -.Defining lock metadata on CRUD methods -==== -[source, java] ----- -interface UserRepository extends Repository { - - // Redeclaration of a CRUD method - @Lock(LockModeType.READ) - List findAll(); -} ----- -==== - -:leveloffset: +1 - -include::{spring-data-commons-docs}/auditing.adoc[] - -:leveloffset: -1 - -There is also a convenience base class, `AbstractAuditable`, which you can extend to avoid the need to manually implement the interface methods. Doing so increases the coupling of your domain classes to Spring Data, which might be something you want to avoid. Usually, the annotation-based way of defining auditing metadata is preferred as it is less invasive and more flexible. - -[[jpa.auditing]] -== JPA Auditing - -[[jpa.auditing.configuration]] -=== General Auditing Configuration - -Spring Data JPA ships with an entity listener that can be used to trigger the capturing of auditing information. First, you must register the `AuditingEntityListener` to be used for all entities in your persistence contexts inside your `orm.xml` file, as shown in the following example: - -.Auditing configuration orm.xml -==== -[source, xml] ----- - - - - - - - ----- -==== - -You can also enable the `AuditingEntityListener` on a per-entity basis by using the `@EntityListeners` annotation, as follows: - -==== -[source, java] ----- -@Entity -@EntityListeners(AuditingEntityListener.class) -public class MyEntity { - -} ----- -==== - -NOTE: The auditing feature requires `spring-aspects.jar` to be on the classpath. - -With `orm.xml` suitably modified and `spring-aspects.jar` on the classpath, activating auditing functionality is a matter of adding the Spring Data JPA `auditing` namespace element to your configuration, as follows: - -.Activating auditing using XML configuration -==== -[source, xml] ----- - ----- -==== - -As of Spring Data JPA 1.5, you can enable auditing by annotating a configuration class with the `@EnableJpaAuditing` annotation. You must still modify the `orm.xml` file and have `spring-aspects.jar` on the classpath. The following example shows how to use the `@EnableJpaAuditing` annotation: - -.Activating auditing with Java configuration -==== -[source, java] ----- -@Configuration -@EnableJpaAuditing -class Config { - - @Bean - public AuditorAware auditorProvider() { - return new AuditorAwareImpl(); - } -} ----- -==== - -If you expose a bean of type `AuditorAware` to the `ApplicationContext`, the auditing infrastructure automatically picks it up and uses it to determine the current user to be set on domain types. If you have multiple implementations registered in the `ApplicationContext`, you can select the one to be used by explicitly setting the `auditorAwareRef` attribute of `@EnableJpaAuditing`. - -[[jpa.misc]] -= Miscellaneous Considerations - -[[jpa.misc.jpa-context]] -== Using `JpaContext` in Custom Implementations - -When working with multiple `EntityManager` instances and <>, you need to wire the correct `EntityManager` into the repository implementation class. You can do so by explicitly naming the `EntityManager` in the `@PersistenceContext` annotation or, if the `EntityManager` is `@Autowired`, by using `@Qualifier`. - -As of Spring Data JPA 1.9, Spring Data JPA includes a class called `JpaContext` that lets you obtain the `EntityManager` by managed domain class, assuming it is managed by only one of the `EntityManager` instances in the application. The following example shows how to use `JpaContext` in a custom repository: - -.Using `JpaContext` in a custom repository implementation -==== -[source, java] ----- -class UserRepositoryImpl implements UserRepositoryCustom { - - private final EntityManager em; - - @Autowired - public UserRepositoryImpl(JpaContext context) { - this.em = context.getEntityManagerByManagedType(User.class); - } - - … -} ----- -==== - -The advantage of this approach is that, if the domain type gets assigned to a different persistence unit, the repository does not have to be touched to alter the reference to the persistence unit. - -[[jpa.misc.merging-persistence-units]] -== Merging persistence units - -Spring supports having multiple persistence units. Sometimes, however, you might want to modularize your application but still make sure that all these modules run inside a single persistence unit. To enable that behavior, Spring Data JPA offers a `PersistenceUnitManager` implementation that automatically merges persistence units based on their name, as shown in the following example: - -.Using MergingPersistenceUnitmanager -==== -[source, xml] ----- - - - - - ----- -==== - -[[jpa.misc.entity-scanning]] -=== Classpath Scanning for @Entity Classes and JPA Mapping Files - -A plain JPA setup requires all annotation-mapped entity classes to be listed in `orm.xml`. The same applies to XML mapping files. Spring Data JPA provides a `ClasspathScanningPersistenceUnitPostProcessor` that gets a base package configured and optionally takes a mapping filename pattern. It then scans the given package for classes annotated with `@Entity` or `@MappedSuperclass`, loads the configuration files that match the filename pattern, and hands them to the JPA configuration. The post-processor must be configured as follows: - -.Using ClasspathScanningPersistenceUnitPostProcessor -==== -[source, xml] ----- - - - - - - - - - - ----- -==== - -NOTE: As of Spring 3.1, a package to scan can be configured on the `LocalContainerEntityManagerFactoryBean` directly to enable classpath scanning for entity classes. See the link:{springJavadocUrl}org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setPackagesToScan(java.lang.String...)$$[JavaDoc] for details. - -[[jpd.misc.cdi-integration]] -== CDI Integration - -Instances of the repository interfaces are usually created by a container, for which Spring is the most natural choice when working with Spring Data. Spring offers sophisticated support for creating bean instances, as documented in <>. As of version 1.1.0, Spring Data JPA ships with a custom CDI extension that allows using the repository abstraction in CDI environments. The extension is part of the JAR. To activate it, include the Spring Data JPA JAR on your classpath. - -You can now set up the infrastructure by implementing a CDI Producer for the `EntityManagerFactory` and `EntityManager`, as shown in the following example: - -[source, java] ----- -class EntityManagerFactoryProducer { - - @Produces - @ApplicationScoped - public EntityManagerFactory createEntityManagerFactory() { - return Persistence.createEntityManagerFactory("my-persistence-unit"); - } - - public void close(@Disposes EntityManagerFactory entityManagerFactory) { - entityManagerFactory.close(); - } - - @Produces - @RequestScoped - public EntityManager createEntityManager(EntityManagerFactory entityManagerFactory) { - return entityManagerFactory.createEntityManager(); - } - - public void close(@Disposes EntityManager entityManager) { - entityManager.close(); - } -} ----- - -The necessary setup can vary depending on the JavaEE environment. You may need to do nothing more than redeclare a `EntityManager` as a CDI bean, as follows: - -[source, java] ----- -class CdiConfig { - - @Produces - @RequestScoped - @PersistenceContext - public EntityManager entityManager; -} ----- - -In the preceding example, the container has to be capable of creating JPA `EntityManagers` itself. All the configuration does is re-export the JPA `EntityManager` as a CDI bean. - -The Spring Data JPA CDI extension picks up all available `EntityManager` instances as CDI beans and creates a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container. Thus, obtaining an instance of a Spring Data repository is a matter of declaring an `@Injected` property, as shown in the following example: - -[source, java] ----- -class RepositoryClient { - - @Inject - PersonRepository repository; - - public void businessMethod() { - List people = repository.findAll(); - } -} ----- diff --git a/src/main/asciidoc/preface.adoc b/src/main/asciidoc/preface.adoc deleted file mode 100644 index 8b30257482..0000000000 --- a/src/main/asciidoc/preface.adoc +++ /dev/null @@ -1,12 +0,0 @@ -[[preface]] -== Preface - -Spring Data JPA provides repository support for the Jakarta Persistence API (JPA). It eases development of applications that need to access JPA data sources. - -[[project]] -=== Project Metadata - -* Version control: https://github.com/spring-projects/spring-data-jpa -* Bugtracker: https://github.com/spring-projects/spring-data-jpa/issues -* Milestone repository: https://repo.spring.io/milestone -* Snapshot repository: https://repo.spring.io/snapshot diff --git a/src/main/asciidoc/repository-projections-dto-limitations.adoc b/src/main/asciidoc/repository-projections-dto-limitations.adoc deleted file mode 100644 index bc2a33df14..0000000000 --- a/src/main/asciidoc/repository-projections-dto-limitations.adoc +++ /dev/null @@ -1 +0,0 @@ -NOTE: Class-based projections with JPQL is limited to *constructor expressions* in your JPQL expression, e.g. `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. (Note the usage of a FQDN for the DTO type!) This JPQL expression can be used in `@Query` annotations as well where you define any named queries. And it's important to point out that class-based projections do not work with native queries AT ALL. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] diff --git a/src/main/resources/antora-resources/antora.yml b/src/main/resources/antora-resources/antora.yml new file mode 100644 index 0000000000..059abf1920 --- /dev/null +++ b/src/main/resources/antora-resources/antora.yml @@ -0,0 +1,12 @@ +version: ${project.version} + +asciidoc: + attributes: + version: ${project.version} + springversionshort: 6.1 + springversion: ${spring} + attribute-missing: 'warn' + spring-data-commons-docs-url: https://docs.spring.io/spring-data-commons/reference + spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/data-commons/docs/current/api/ + springdocsurl: https://docs.spring.io/spring-framework/reference/{springversionshort} + springjavadocurl: https://docs.spring.io/spring-framework/docs/${spring}/javadoc-api \ No newline at end of file From c0a6ee72dd930747269b7eafc581e4d586882f06 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 13 Jul 2023 11:52:42 +0200 Subject: [PATCH 437/821] Use unique named parameter bindings for like parameters. We now replace LIKE expressions according to their type with individual bindings if an existing binding cannot be used because of how the bound value is being transformed. WHERE foo like %:name or bar like :name becomes WHERE foo like :name (i.e. '%' + :name) or bar like :name_1 (i.e. :name) See #3041 --- .../jpa/provider/PersistenceProvider.java | 7 +- .../query/ParameterBinderFactory.java | 8 +- .../query/QueryParameterSetterFactory.java | 104 +++++++++--- .../jpa/repository/query/StringQuery.java | 154 ++++++++++++------ .../jpa/repository/UserRepositoryTests.java | 7 +- .../query/LikeBindingUnitTests.java | 22 ++- .../query/StringQueryUnitTests.java | 70 +++++++- .../jpa/repository/sample/UserRepository.java | 4 +- 8 files changed, 284 insertions(+), 92 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index e7bdb6c272..ea378384eb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -327,14 +327,15 @@ public boolean canExtractQuery() { } /** - * Because Hibernate's {@literal TypedParameterValue} is only used to wrap a {@literal null}, swap it out with an - * empty string for query creation. + * Because Hibernate's {@literal TypedParameterValue} is only used to wrap a {@literal null}, swap it out with + * {@code null} for query creation. * * @param value * @return the original value or null. * @since 3.0 */ - public static Object unwrapTypedParameterValue(Object value) { + @Nullable + public static Object unwrapTypedParameterValue(@Nullable Object value) { return typedParameterValueClass != null && typedParameterValueClass.isInstance(value) // ? null // diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java index 2ff2bc46dd..6cdd559f30 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java @@ -46,10 +46,11 @@ static ParameterBinder createBinder(JpaParameters parameters) { Assert.notNull(parameters, "JpaParameters must not be null"); + QueryParameterSetterFactory likeFactory = QueryParameterSetterFactory.forLikeRewrite(parameters); QueryParameterSetterFactory setterFactory = QueryParameterSetterFactory.basic(parameters); List bindings = getBindings(parameters); - return new ParameterBinder(parameters, createSetters(bindings, setterFactory)); + return new ParameterBinder(parameters, createSetters(bindings, likeFactory, setterFactory)); } /** @@ -95,9 +96,12 @@ static ParameterBinder createQueryAwareBinder(JpaParameters parameters, Declared List bindings = query.getParameterBindings(); QueryParameterSetterFactory expressionSetterFactory = QueryParameterSetterFactory.parsing(parser, evaluationContextProvider, parameters); + + QueryParameterSetterFactory like = QueryParameterSetterFactory.forLikeRewrite(parameters); QueryParameterSetterFactory basicSetterFactory = QueryParameterSetterFactory.basic(parameters); - return new ParameterBinder(parameters, createSetters(bindings, query, expressionSetterFactory, basicSetterFactory), + return new ParameterBinder(parameters, + createSetters(bindings, query, expressionSetterFactory, like, basicSetterFactory), !query.usesPaging()); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index 5cc137d820..f2d35b84e5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -24,6 +24,7 @@ import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; import org.springframework.data.jpa.repository.query.QueryParameterSetter.NamedOrIndexedQueryParameterSetter; +import org.springframework.data.jpa.repository.query.StringQuery.LikeParameterBinding; import org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; @@ -62,6 +63,20 @@ static QueryParameterSetterFactory basic(JpaParameters parameters) { return new BasicQueryParameterSetterFactory(parameters); } + /** + * Creates a new {@link QueryParameterSetterFactory} for the given {@link JpaParameters} applying LIKE rewrite for + * renamed {@code :foo%} or {@code %:bar} bindings. + * + * @param parameters must not be {@literal null}. + * @return a basic {@link QueryParameterSetterFactory} that can handle named parameters. + */ + static QueryParameterSetterFactory forLikeRewrite(JpaParameters parameters) { + + Assert.notNull(parameters, "JpaParameters must not be null"); + + return new LikeRewritingQueryParameterSetterFactory(parameters); + } + /** * Creates a new {@link QueryParameterSetterFactory} using the given {@link JpaParameters} and * {@link ParameterMetadata}. @@ -117,6 +132,29 @@ private static QueryParameterSetter createSetter(Function parameters, String name) { + + JpaParameters bindableParameters = parameters.getBindableParameters(); + + for (JpaParameter bindableParameter : bindableParameters) { + if (name.equals(getRequiredName(bindableParameter))) { + return bindableParameter; + } + } + + return null; + } + + private static String getRequiredName(JpaParameter p) { + return p.getName().orElseThrow(() -> new IllegalStateException(ParameterBinder.PARAMETER_NEEDS_TO_BE_NAMED)); + } + + @Nullable + static Object getValue(JpaParametersParameterAccessor accessor, Parameter parameter) { + return accessor.getValue(parameter); + } + /** * Handles bindings that are SpEL expressions by evaluating the expression to obtain a value. * @@ -176,6 +214,46 @@ private Object evaluateExpression(Expression expression, JpaParametersParameterA } } + /** + * Handles bindings that use Like-rewriting. + * + * @author Mark Paluch + * @since 3.1.2 + */ + private static class LikeRewritingQueryParameterSetterFactory extends QueryParameterSetterFactory { + + private final Parameters parameters; + + /** + * @param parameters must not be {@literal null}. + */ + LikeRewritingQueryParameterSetterFactory(Parameters parameters) { + + Assert.notNull(parameters, "Parameters must not be null"); + + this.parameters = parameters; + } + + @Nullable + @Override + public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery declaredQuery) { + + if (binding.isExpression() || !(binding instanceof LikeParameterBinding likeBinding) + || !declaredQuery.hasNamedParameter()) { + return null; + } + JpaParameter parameter = QueryParameterSetterFactory.findParameterForBinding((JpaParameters) parameters, + likeBinding.getDeclaredName()); + + if (parameter == null) { + return null; + } + + return createSetter(values -> values.getValue(parameter), binding, parameter); + } + + } + /** * Extracts values for parameter bindings from method parameters. It handles named as well as indexed parameters. * @@ -205,7 +283,7 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla JpaParameter parameter; if (declaredQuery.hasNamedParameter()) { - parameter = findParameterForBinding(binding); + parameter = findParameterForBinding(parameters, binding.getRequiredName()); } else { int parameterIndex = binding.getRequiredPosition() - 1; @@ -228,28 +306,6 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla : createSetter(values -> getValue(values, parameter), binding, parameter); } - @Nullable - private JpaParameter findParameterForBinding(ParameterBinding binding) { - - JpaParameters bindableParameters = parameters.getBindableParameters(); - - for (JpaParameter bindableParameter : bindableParameters) { - if (binding.getRequiredName().equals(getName(bindableParameter))) { - return bindableParameter; - } - } - - return null; - } - - @Nullable - private Object getValue(JpaParametersParameterAccessor accessor, Parameter parameter) { - return accessor.getValue(parameter); - } - - private static String getName(JpaParameter p) { - return p.getName().orElseThrow(() -> new IllegalStateException(ParameterBinder.PARAMETER_NEEDS_TO_BE_NAMED)); - } } /** @@ -366,7 +422,7 @@ public Class getParameterType() { @Nullable private static String getName(@Nullable JpaParameter parameter, ParameterBinding binding) { - if (parameter == null) { + if (binding.hasName() || parameter == null) { return binding.getName(); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index e56185ab1e..f12f6aff7d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -28,11 +28,14 @@ import java.util.regex.Pattern; import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.SpelQueryContext; import org.springframework.data.repository.query.SpelQueryContext.SpelExtractor; import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -233,6 +236,8 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St int expressionParameterIndex = parametersShouldBeAccessedByIndex ? greatestParameterIndex : 0; + LikeParameterBindings likeParameterBindings = new LikeParameterBindings(); + boolean usesJpaStyleParameters = false; while (matcher.find()) { @@ -273,9 +278,11 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St if (parameterIndex != null) { checkAndRegister(new LikeParameterBinding(parameterIndex, likeType, expression), bindings); } else { - checkAndRegister(new LikeParameterBinding(parameterName, likeType, expression), bindings); - replacement = ":" + parameterName; + LikeParameterBinding binding = likeParameterBindings.getOrCreate(parameterName, likeType, expression); + checkAndRegister(binding, bindings); + + replacement = ":" + binding.getRequiredName(); } break; @@ -334,43 +341,7 @@ private static String replaceFirst(String text, String substring, String replace return text; } - return text.substring(0, index) + potentiallyWrapWithWildcards(replacement, substring) - + text.substring(index + substring.length()); - } - - /** - * If there are any pre- or post-wildcards ({@literal %}), replace them with a {@literal CONCAT} function and proper - * wildcards as string literals. NOTE: {@literal CONCAT} appears to be a standard function across relational - * databases as well as JPA providers. - * - * @param replacement - * @param substring - * @return the replacement string properly wrapped in a {@literal CONCAT} function with wildcards applied. - * @since 3.1 - */ - private static String potentiallyWrapWithWildcards(String replacement, String substring) { - - boolean wildcards = substring.startsWith("%") || substring.endsWith("%"); - - if (!wildcards) { - return replacement; - } - - StringBuilder concatWrapper = new StringBuilder("CONCAT("); - - if (substring.startsWith("%")) { - concatWrapper.append("'%',"); - } - - concatWrapper.append(replacement); - - if (substring.endsWith("%")) { - concatWrapper.append(",'%'"); - } - - concatWrapper.append(")"); - - return concatWrapper.toString(); + return text.substring(0, index) + replacement + text.substring(index + substring.length()); } @Nullable @@ -460,6 +431,60 @@ static ParameterBindingType of(String typeSource) { } } + /** + * Utility to create unique parameter bindings for LIKE that can be evaluated by + * {@code LikeRewritingQueryParameterSetterFactory}. + * + * @author Mark Paluch + * @since 3.1.2 + */ + static class LikeParameterBindings { + + private final MultiValueMap likeBindings = new LinkedMultiValueMap<>(); + + /** + * Get an existing or create a new {@link LikeParameterBinding} if a previously bound {@code LIKE} expression cannot + * be reused. + * + * @param parameterName the parameter name as declared in the actual JPQL query. + * @param likeType type of the LIKE expression. + * @param expression expression content if the LIKE comparison value is provided by a SpEL expression. + * @return the Like binding. Can return an already existing binding. + */ + LikeParameterBinding getOrCreate(String parameterName, Type likeType, @Nullable String expression) { + + List likeParameterBindings = likeBindings.computeIfAbsent(parameterName, + s -> new ArrayList<>()); + LikeParameterBinding reuse = null; + + // unique parameters only required for literals as expressions create unique parameter names + if (expression == null) { + for (LikeParameterBinding likeParameterBinding : likeParameterBindings) { + + if (likeParameterBinding.type == likeType) { + reuse = likeParameterBinding; + break; + } + } + } + + String declaredParameterName = parameterName; + if (reuse != null) { + return reuse; + } + + if (!likeParameterBindings.isEmpty()) { + parameterName = parameterName + "_" + likeParameterBindings.size(); + } + + LikeParameterBinding binding = new LikeParameterBinding(parameterName, declaredParameterName, likeType, + expression); + likeParameterBindings.add(binding); + + return binding; + } + } + /** * A generic parameter binding with name or position information. * @@ -511,6 +536,10 @@ boolean hasName(@Nullable String name) { return this.position == null && this.name != null && this.name.equals(name); } + boolean hasName() { + return this.position == null && !ObjectUtils.isEmpty(this.name); + } + /** * Returns whether the binding has the given position. Will always be {@literal false} in case the * {@link ParameterBinding} has been set up from a name. @@ -519,6 +548,10 @@ boolean hasPosition(@Nullable Integer position) { return position != null && this.name == null && position.equals(this.position); } + boolean hasPosition() { + return position != null && this.name == null; + } + /** * @return the name */ @@ -665,6 +698,7 @@ public Object prepare(@Nullable Object value) { * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ static class LikeParameterBinding extends ParameterBinding { @@ -673,35 +707,45 @@ static class LikeParameterBinding extends ParameterBinding { private final Type type; + private final @Nullable String declaredName; + /** * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type}. * - * @param name must not be {@literal null} or empty. + * @param name parameter name in the final query, must not be {@literal null} or empty. + * @param declaredName name of the declared parameter from the original query, referring to a + * {@link JpaParameter#getName()}, must not be {@literal null} or empty. * @param type must not be {@literal null}. */ - LikeParameterBinding(String name, Type type) { - this(name, type, null); + LikeParameterBinding(String name, String declaredName, Type type) { + this(name, declaredName, type, null); } /** * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type} and parameter * binding input. * - * @param name must not be {@literal null} or empty. + * @param name parameter name in the final query, must not be {@literal null} or empty. + * @param declaredName name of the declared parameter from the original query, referring to a + * {@link JpaParameter#getName()}, must not be {@literal null} or empty. * @param type must not be {@literal null}. * @param expression may be {@literal null}. */ - LikeParameterBinding(String name, Type type, @Nullable String expression) { + LikeParameterBinding(String name, String declaredName, Type type, @Nullable String expression) { super(name, null, expression); Assert.hasText(name, "Name must not be null or empty"); + if (expression == null && !StringUtils.hasText(declaredName)) { + throw new IllegalArgumentException("Declared name must not be null or empty"); + } Assert.notNull(type, "Type must not be null"); Assert.isTrue(SUPPORTED_TYPES.contains(type), String.format("Type must be one of %s", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); this.type = type; + this.declaredName = declaredName; } /** @@ -732,6 +776,7 @@ static class LikeParameterBinding extends ParameterBinding { String.format("Type must be one of %s", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); this.type = type; + this.declaredName = null; } /** @@ -743,13 +788,29 @@ public Type getType() { return type; } + @Nullable + public String getDeclaredName() { + return declaredName; + } + /** - * Extracts the raw value properly. + * Prepares the given raw keyword according to the like type. */ @Nullable @Override public Object prepare(@Nullable Object value) { - return PersistenceProvider.unwrapTypedParameterValue(value); + + Object unwrapped = PersistenceProvider.unwrapTypedParameterValue(value); + if (unwrapped == null) { + return null; + } + + return switch (type) { + case STARTING_WITH -> String.format("%s%%", unwrapped); + case ENDING_WITH -> String.format("%%%s", unwrapped); + case CONTAINING -> String.format("%%%s%%", unwrapped); + default -> unwrapped; + }; } @Override @@ -802,6 +863,7 @@ private static Type getLikeTypeFrom(String expression) { return Type.LIKE; } + } static class Metadata { 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 00a1750a59..04577804a6 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 @@ -971,14 +971,13 @@ void executesManualQueryWithPositionLikeExpressionCorrectly() { assertThat(result).containsOnly(thirdUser); } - @Test // DATAJPA-292 + @Test // DATAJPA-292, GH-3041 void executesManualQueryWithNamedLikeExpressionCorrectly() { flushTestUsers(); - List result = repository.findByFirstnameLikeNamed("Da"); - - assertThat(result).containsOnly(thirdUser); + assertThat(repository.findByFirstnameLikeNamed("Da")).containsOnly(thirdUser); + assertThat(repository.findByFirstnameLikeNamed("in")).containsOnly(fourthUser); } @Test // DATAJPA-231 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java index bcd0556bff..d94779b2c0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java @@ -32,28 +32,28 @@ class LikeBindingUnitTests { private static void assertAugmentedValue(Type type, Object value) { - LikeParameterBinding binding = new LikeParameterBinding("foo", type); + LikeParameterBinding binding = new LikeParameterBinding("foo", "foo", type); assertThat(binding.prepare("value")).isEqualTo(value); } @Test void rejectsNullName() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding(null, Type.CONTAINING)); + assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding(null, "", Type.CONTAINING)); } @Test void rejectsEmptyName() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("", Type.CONTAINING)); + assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("", "", Type.CONTAINING)); } @Test void rejectsNullType() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("foo", null)); + assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("foo", "foo", null)); } @Test void rejectsInvalidType() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("foo", Type.SIMPLE_PROPERTY)); + assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("foo", "foo", Type.SIMPLE_PROPERTY)); } @Test @@ -64,7 +64,7 @@ void rejectsInvalidPosition() { @Test void setsUpInstanceForName() { - LikeParameterBinding binding = new LikeParameterBinding("foo", Type.CONTAINING); + LikeParameterBinding binding = new LikeParameterBinding("foo", "foo", Type.CONTAINING); assertThat(binding.hasName("foo")).isTrue(); assertThat(binding.hasName("bar")).isFalse(); @@ -84,4 +84,14 @@ void setsUpInstanceForIndex() { assertThat(binding.hasPosition(1)).isTrue(); assertThat(binding.getType()).isEqualTo(Type.CONTAINING); } + + @Test + void augmentsValueCorrectly() { + + assertAugmentedValue(Type.CONTAINING, "%value%"); + assertAugmentedValue(Type.ENDING_WITH, "%value"); + assertAugmentedValue(Type.STARTING_WITH, "value%"); + + assertThat(new LikeParameterBinding(1, Type.CONTAINING).prepare(null)).isNull(); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 3c7ece5c4b..d36579bb63 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -36,6 +36,7 @@ * @author Nils Borrmann * @author Andriy Redko * @author Diego Krupitza + * @author Mark Paluch */ class StringQueryUnitTests { @@ -65,7 +66,7 @@ void detectsPositionalLikeBindings() { assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()) - .isEqualTo("select u from User u where u.firstname like CONCAT('%',?1,'%') or u.lastname like CONCAT('%',?2)"); + .isEqualTo("select u from User u where u.firstname like ?1 or u.lastname like ?2"); List bindings = query.getParameterBindings(); assertThat(bindings).hasSize(2); @@ -87,7 +88,7 @@ void detectsNamedLikeBindings() { StringQuery query = new StringQuery("select u from User u where u.firstname like %:firstname", true); assertThat(query.hasParameterBindings()).isTrue(); - assertThat(query.getQueryString()).isEqualTo("select u from User u where u.firstname like CONCAT('%',:firstname)"); + assertThat(query.getQueryString()).isEqualTo("select u from User u where u.firstname like :firstname"); List bindings = query.getParameterBindings(); assertThat(bindings).hasSize(1); @@ -98,6 +99,55 @@ void detectsNamedLikeBindings() { assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); } + @Test // DATAJPA-292 + void rewritesNamedLikeToUniqueParametersIfNecessary() { + + StringQuery query = new StringQuery( + "select u from User u where u.firstname like %:firstname or u.firstname like :firstname%", true); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()) + .isEqualTo("select u from User u where u.firstname like :firstname or u.firstname like :firstname_1"); + + List bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(2); + + LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); + assertThat(binding).isNotNull(); + assertThat(binding.hasName("firstname")).isTrue(); + assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); + + binding = (LikeParameterBinding) bindings.get(1); + assertThat(binding).isNotNull(); + assertThat(binding.hasName("firstname_1")).isTrue(); + assertThat(binding.getType()).isEqualTo(Type.STARTING_WITH); + } + + @Test // DATAJPA-292 + void reusesLikeBindingsWherePossible() { + + StringQuery query = new StringQuery( + "select u from User u where u.firstname like %:firstname or u.firstname like %:firstname% or u.firstname like %:firstname% or u.firstname like %:firstname", + true); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()).isEqualTo( + "select u from User u where u.firstname like :firstname or u.firstname like :firstname_1 or u.firstname like :firstname_1 or u.firstname like :firstname"); + + List bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(2); + + LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); + assertThat(binding).isNotNull(); + assertThat(binding.hasName("firstname")).isTrue(); + assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); + + binding = (LikeParameterBinding) bindings.get(1); + assertThat(binding).isNotNull(); + assertThat(binding.hasName("firstname_1")).isTrue(); + assertThat(binding.getType()).isEqualTo(Type.CONTAINING); + } + @Test // DATAJPA-461 void detectsNamedInParameterBindings() { @@ -199,9 +249,8 @@ void removesLikeBindingsFromQueryIfQueryContainsSimpleBinding() { assertNamedBinding(LikeParameterBinding.class, "escapedWord", bindings.get(0)); assertNamedBinding(ParameterBinding.class, "word", bindings.get(1)); - assertThat(query.getQueryString()) - .isEqualTo("SELECT a FROM Article a WHERE a.overview LIKE CONCAT('%',:escapedWord,'%') ESCAPE '~'" - + " OR a.content LIKE CONCAT('%',:escapedWord,'%') ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC"); + assertThat(query.getQueryString()).isEqualTo("SELECT a FROM Article a WHERE a.overview LIKE :escapedWord ESCAPE '~'" + + " OR a.content LIKE :escapedWord ESCAPE '~' OR a.title = :word ORDER BY a.articleId DESC"); } @Test // DATAJPA-483 @@ -275,6 +324,17 @@ void shouldReplaceAllNamedExpressionParametersWithInClause() { assertThat(queryString).isEqualTo("select a from A a where a.b in :__$synthetic$__1 and a.c in :__$synthetic$__2"); } + @Test // DATAJPA-712 + void shouldReplaceExpressionWithLikeParameters() { + + StringQuery query = new StringQuery( + "select a from A a where a.b LIKE :#{#filter.login}% and a.c LIKE %:#{#filter.login}", true); + String queryString = query.getQueryString(); + + assertThat(queryString) + .isEqualTo("select a from A a where a.b LIKE :__$synthetic$__1 and a.c LIKE :__$synthetic$__2"); + } + @Test // DATAJPA-712 void shouldReplaceAllPositionExpressionParametersWithInClause() { 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 b79680a577..edbcd9150d 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 @@ -165,8 +165,8 @@ Window findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(S @Query("select u from User u where u.firstname like ?1%") List findByFirstnameLike(String firstname); - // DATAJPA-292 - @Query("select u from User u where u.firstname like :firstname%") + // DATAJPA-292, GH-3041 + @Query("select u from User u where u.firstname like :firstname% or u.firstname like %:firstname") List findByFirstnameLikeNamed(@Param("firstname") String firstname); /** From 55813a867a1376e9ecd68fabb5770324efdeac46 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 21 Jul 2023 10:53:19 +0200 Subject: [PATCH 438/821] Revise String Query `ParameterBinding`. We now distinguish between the binding parameter target and its origin. The parameter target represents how the binding is bound to the query, the origin points to where the binding parameter comes from (method invocation argument or an expression). The revised design removes the assumption that binding parameters must match their indices/names of the method call to introduce synthetic parameters for different binding variants while using the same underlying invocation parameters. See #3041 --- .../jpa/repository/query/DeclaredQuery.java | 4 +- .../repository/query/EmptyDeclaredQuery.java | 2 +- .../query/ParameterBinderFactory.java | 16 +- .../repository/query/ParameterBinding.java | 582 ++++++++++++++++++ .../query/QueryParameterSetter.java | 2 +- .../query/QueryParameterSetterFactory.java | 172 +----- .../jpa/repository/query/StringQuery.java | 441 +------------ .../query/LikeBindingUnitTests.java | 49 +- .../QueryParameterSetterFactoryUnitTests.java | 7 +- .../query/StringQueryUnitTests.java | 32 +- 10 files changed, 695 insertions(+), 612 deletions(-) create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinding.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java index 4821865def..670e030fb2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DeclaredQuery.java @@ -71,9 +71,9 @@ static DeclaredQuery of(@Nullable String query, boolean nativeQuery) { boolean isDefaultProjection(); /** - * Returns the {@link StringQuery.ParameterBinding}s registered. + * Returns the {@link ParameterBinding}s registered. */ - List getParameterBindings(); + List getParameterBindings(); /** * Creates a new {@literal DeclaredQuery} representing a count query, i.e. a query returning the number of rows to be diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java index dd37c45081..dbb3be8ba6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EmptyDeclaredQuery.java @@ -60,7 +60,7 @@ public boolean isDefaultProjection() { } @Override - public List getParameterBindings() { + public List getParameterBindings() { return Collections.emptyList(); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java index 6cdd559f30..7d10ead1e6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java @@ -19,8 +19,9 @@ import java.util.List; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; +import org.springframework.data.jpa.repository.query.ParameterBinding.BindingIdentifier; +import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterOrigin; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; -import org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.Assert; @@ -46,11 +47,10 @@ static ParameterBinder createBinder(JpaParameters parameters) { Assert.notNull(parameters, "JpaParameters must not be null"); - QueryParameterSetterFactory likeFactory = QueryParameterSetterFactory.forLikeRewrite(parameters); QueryParameterSetterFactory setterFactory = QueryParameterSetterFactory.basic(parameters); List bindings = getBindings(parameters); - return new ParameterBinder(parameters, createSetters(bindings, likeFactory, setterFactory)); + return new ParameterBinder(parameters, createSetters(bindings, setterFactory)); } /** @@ -97,11 +97,9 @@ static ParameterBinder createQueryAwareBinder(JpaParameters parameters, Declared QueryParameterSetterFactory expressionSetterFactory = QueryParameterSetterFactory.parsing(parser, evaluationContextProvider, parameters); - QueryParameterSetterFactory like = QueryParameterSetterFactory.forLikeRewrite(parameters); QueryParameterSetterFactory basicSetterFactory = QueryParameterSetterFactory.basic(parameters); - return new ParameterBinder(parameters, - createSetters(bindings, query, expressionSetterFactory, like, basicSetterFactory), + return new ParameterBinder(parameters, createSetters(bindings, query, expressionSetterFactory, basicSetterFactory), !query.usesPaging()); } @@ -113,7 +111,11 @@ private static List getBindings(JpaParameters parameters) { for (JpaParameter parameter : parameters) { if (parameter.isBindable()) { - result.add(new ParameterBinding(++bindableParameterIndex)); + int index = ++bindableParameterIndex; + BindingIdentifier bindingIdentifier = parameter.getName().map(it -> BindingIdentifier.of(it, index)) + .orElseGet(() -> BindingIdentifier.of(index)); + + result.add(new ParameterBinding(bindingIdentifier, ParameterOrigin.ofParameter(bindingIdentifier))); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinding.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinding.java new file mode 100644 index 0000000000..bc0086fab1 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinding.java @@ -0,0 +1,582 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.util.ObjectUtils.*; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; +import org.springframework.data.repository.query.parser.Part.Type; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +/** + * A generic parameter binding with name or position information. + * + * @author Thomas Darimont + * @author Mark Paluch + */ +class ParameterBinding { + + private final BindingIdentifier identifier; + private final ParameterOrigin origin; + + /** + * Creates a new {@link ParameterBinding} for the parameter with the given identifier and origin. + * + * @param identifier of the parameter, must not be {@literal null}. + * @param origin the origin of the parameter (expression or method argument) + */ + ParameterBinding(BindingIdentifier identifier, ParameterOrigin origin) { + + Assert.notNull(identifier, "BindingIdentifier must not be null"); + Assert.notNull(origin, "ParameterOrigin must not be null"); + + this.identifier = identifier; + this.origin = origin; + } + + public BindingIdentifier getIdentifier() { + return identifier; + } + + public ParameterOrigin getOrigin() { + return origin; + } + + /** + * @return the name if available or {@literal null}. + */ + @Nullable + public String getName() { + return identifier.hasName() ? identifier.getName() : null; + } + + /** + * @return the name + * @throws IllegalStateException if the name is not available. + * @since 2.0 + */ + String getRequiredName() throws IllegalStateException { + + String name = getName(); + + if (name != null) { + return name; + } + + throw new IllegalStateException(String.format("Required name for %s not available", this)); + } + + /** + * @return the position if available or {@literal null}. + */ + @Nullable + Integer getPosition() { + return identifier.hasPosition() ? identifier.getPosition() : null; + } + + /** + * @return the position + * @throws IllegalStateException if the position is not available. + * @since 2.0 + */ + int getRequiredPosition() throws IllegalStateException { + + Integer position = getPosition(); + + if (position != null) { + return position; + } + + throw new IllegalStateException(String.format("Required position for %s not available", this)); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + ParameterBinding that = (ParameterBinding) o; + + if (!nullSafeEquals(identifier, that.identifier)) { + return false; + } + return nullSafeEquals(origin, that.origin); + } + + @Override + public int hashCode() { + int result = nullSafeHashCode(identifier); + result = 31 * result + nullSafeHashCode(origin); + return result; + } + + @Override + public String toString() { + return String.format("ParameterBinding [identifier: %s, origin: %s]", identifier, origin); + } + + /** + * @param valueToBind value to prepare + */ + @Nullable + public Object prepare(@Nullable Object valueToBind) { + return valueToBind; + } + + /** + * Check whether the {@code other} binding uses the same bind target. + * + * @param other + * @return + */ + public boolean bindsTo(ParameterBinding other) { + + if (identifier.hasName() && other.identifier.hasName()) { + if (identifier.getName().equals(other.identifier.getName())) { + return true; + } + } + + if (identifier.hasPosition() && other.identifier.hasPosition()) { + if (identifier.getPosition() == other.identifier.getPosition()) { + return true; + } + } + + return false; + } + + /** + * Represents a {@link ParameterBinding} in a JPQL query augmented with instructions of how to apply a parameter as an + * {@code IN} parameter. + * + * @author Thomas Darimont + */ + static class InParameterBinding extends ParameterBinding { + + /** + * Creates a new {@link InParameterBinding} for the parameter with the given name. + */ + InParameterBinding(BindingIdentifier identifier, ParameterOrigin origin) { + super(identifier, origin); + } + + @Override + public Object prepare(@Nullable Object value) { + + if (!ObjectUtils.isArray(value)) { + return value; + } + + int length = Array.getLength(value); + Collection result = new ArrayList<>(length); + + for (int i = 0; i < length; i++) { + result.add(Array.get(value, i)); + } + + return result; + } + } + + /** + * Represents a parameter binding in a JPQL query augmented with instructions of how to apply a parameter as LIKE + * parameter. This allows expressions like {@code …like %?1} in the JPQL query, which is not allowed by plain JPA. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ + static class LikeParameterBinding extends ParameterBinding { + + private static final List SUPPORTED_TYPES = Arrays.asList(Type.CONTAINING, Type.STARTING_WITH, + Type.ENDING_WITH, Type.LIKE); + + private final Type type; + + /** + * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type} and parameter + * binding input. + * + * @param identifier must not be {@literal null} or empty. + * @param type must not be {@literal null}. + */ + LikeParameterBinding(BindingIdentifier identifier, ParameterOrigin origin, Type type) { + + super(identifier, origin); + + Assert.notNull(type, "Type must not be null"); + + Assert.isTrue(SUPPORTED_TYPES.contains(type), + String.format("Type must be one of %s", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); + + this.type = type; + } + + /** + * Returns the {@link Type} of the binding. + * + * @return the type + */ + public Type getType() { + return type; + } + + /** + * Extracts the raw value properly. + */ + @Nullable + @Override + public Object prepare(@Nullable Object value) { + + Object unwrapped = PersistenceProvider.unwrapTypedParameterValue(value); + if (unwrapped == null) { + return null; + } + + return switch (type) { + case STARTING_WITH -> String.format("%s%%", unwrapped); + case ENDING_WITH -> String.format("%%%s", unwrapped); + case CONTAINING -> String.format("%%%s%%", unwrapped); + default -> unwrapped; + }; + } + + @Override + public boolean equals(Object obj) { + + if (!(obj instanceof LikeParameterBinding)) { + return false; + } + + LikeParameterBinding that = (LikeParameterBinding) obj; + + return super.equals(obj) && this.type.equals(that.type); + } + + @Override + public int hashCode() { + + int result = super.hashCode(); + + result += nullSafeHashCode(this.type); + + return result; + } + + @Override + public String toString() { + return String.format("LikeBinding [identifier: %s, origin: %s, type: %s]", getIdentifier(), getOrigin(), + getType()); + } + + /** + * Extracts the like {@link Type} from the given JPA like expression. + * + * @param expression must not be {@literal null} or empty. + */ + static Type getLikeTypeFrom(String expression) { + + Assert.hasText(expression, "Expression must not be null or empty"); + + if (expression.matches("%.*%")) { + return Type.CONTAINING; + } + + if (expression.startsWith("%")) { + return Type.ENDING_WITH; + } + + if (expression.endsWith("%")) { + return Type.STARTING_WITH; + } + + return Type.LIKE; + } + } + + static class ParameterImpl implements jakarta.persistence.Parameter { + + private final BindingIdentifier identifier; + private final Class parameterType; + + /** + * Creates a new {@link ParameterImpl} for the given {@link JpaParameter} and {@link ParameterBinding}. + * + * @param parameter can be {@literal null}. + * @param binding must not be {@literal null}. + * @return a {@link jakarta.persistence.Parameter} object based on the information from the arguments. + */ + static jakarta.persistence.Parameter of(@Nullable JpaParameter parameter, ParameterBinding binding) { + + Class type = parameter == null ? Object.class : parameter.getType(); + + return new ParameterImpl<>(binding.getIdentifier(), type); + } + + public ParameterImpl(BindingIdentifier identifier, Class parameterType) { + this.identifier = identifier; + this.parameterType = parameterType; + } + + @Nullable + @Override + public String getName() { + return identifier.hasName() ? identifier.getName() : null; + } + + @Nullable + @Override + public Integer getPosition() { + return identifier.hasPosition() ? identifier.getPosition() : null; + } + + @Override + public Class getParameterType() { + return parameterType; + } + + } + + /** + * Identifies a binding parameter by name, position or both. Used to bind parameters to a query or to describe a + * {@link MethodInvocationArgument} origin. + */ + sealed interface BindingIdentifier permits Named,Indexed,NamedAndIndexed { + + /** + * Creates an identifier for the given {@code name}. + * + * @param name + * @return + */ + static BindingIdentifier of(String name) { + + Assert.hasText(name, "Name must not be empty"); + + return new Named(name); + } + + /** + * Creates an identifier for the given {@code position}. + * + * @param position 1-based index. + * @return + */ + static BindingIdentifier of(int position) { + + Assert.isTrue(position > 0, "Index position must be greater zero"); + + return new Indexed(position); + } + + /** + * Creates an identifier for the given {@code name} and {@code position}. + * + * @param name + * @return + */ + static BindingIdentifier of(String name, int position) { + + Assert.hasText(name, "Name must not be empty"); + + return new NamedAndIndexed(name, position); + } + + /** + * @return {@code true} if the binding is associated with a name. + */ + default boolean hasName() { + return false; + } + + /** + * @return {@code true} if the binding is associated with a position index. + */ + default boolean hasPosition() { + return false; + } + + /** + * Returns the binding name {@link #hasName() if present} or throw {@link IllegalStateException} if no name + * associated. + * + * @return the binding name. + */ + default String getName() { + throw new IllegalStateException("No name associated"); + } + + /** + * Returns the binding name {@link #hasPosition() if present} or throw {@link IllegalStateException} if no position + * associated. + * + * @return the binding position. + */ + default int getPosition() { + throw new IllegalStateException("No position associated"); + } + } + + private record Named(String name) implements BindingIdentifier { + + @Override + public boolean hasName() { + return true; + } + + @Override + public String getName() { + return name(); + } + } + + private record Indexed(int position) implements BindingIdentifier { + + @Override + public boolean hasPosition() { + return true; + } + + @Override + public int getPosition() { + return position(); + } + } + + private record NamedAndIndexed(String name, int position) implements BindingIdentifier { + + @Override + public boolean hasName() { + return true; + } + + @Override + public String getName() { + return name(); + } + + @Override + public boolean hasPosition() { + return true; + } + + @Override + public int getPosition() { + return position(); + } + } + + /** + * Value type hierarchy to describe where a binding parameter comes from, either method call or an expression. + */ + sealed interface ParameterOrigin permits Expression,MethodInvocationArgument { + + /** + * Creates a {@link Expression} for the given {@code expression} string. + * + * @param expression must not be {@literal null}. + * @return {@link Expression} for the given {@code expression} string. + */ + static Expression ofExpression(String expression) { + return new Expression(expression); + } + + /** + * Creates a {@link MethodInvocationArgument} object for {@code name} and {@code position}. Either the name or the + * position must be given, + * + * @param name the parameter name from the method invocation, can be {@literal null}. + * @param position the parameter position (1-based) from the method invocation, can be {@literal null}. + * @return {@link MethodInvocationArgument} object for {@code name} and {@code position} + */ + static MethodInvocationArgument ofParameter(@Nullable String name, @Nullable Integer position) { + + BindingIdentifier identifier; + if (!ObjectUtils.isEmpty(name) && position != null) { + identifier = BindingIdentifier.of(name, position); + } else if (!ObjectUtils.isEmpty(name)) { + identifier = BindingIdentifier.of(name); + } else { + identifier = BindingIdentifier.of(position); + } + + return ofParameter(identifier); + } + + /** + * Creates a {@link MethodInvocationArgument} using {@link BindingIdentifier}. + * + * @param identifier must not be {@literal null}. + * @return {@link MethodInvocationArgument} for {@link BindingIdentifier}. + */ + static MethodInvocationArgument ofParameter(BindingIdentifier identifier) { + + return new MethodInvocationArgument(identifier); + } + + boolean isMethodArgument(); + + boolean isExpression(); + } + + /** + * Value object capturing the expression of which a binding parameter originates. + * + * @param expression + */ + public record Expression(String expression) implements ParameterOrigin { + + @Override + public boolean isMethodArgument() { + return false; + } + + @Override + public boolean isExpression() { + return true; + } + } + + /** + * Value object capturing the method invocation parameter reference. + * + * @param identifier + */ + public record MethodInvocationArgument(BindingIdentifier identifier) implements ParameterOrigin { + + @Override + public boolean isMethodArgument() { + return true; + } + + @Override + public boolean isExpression() { + return false; + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index a1991f1a30..261deb4b2a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -109,7 +109,7 @@ public void setParameter(BindableQuery query, JpaParametersParameterAccessor acc } else { - final Object value = valueExtractor.apply(accessor); + Object value = valueExtractor.apply(accessor); if (parameter instanceof ParameterExpression) { errorHandling.execute(() -> query.setParameter((Parameter) parameter, value)); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index f2d35b84e5..0ea048f32b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -22,10 +22,11 @@ import java.util.function.Function; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; +import org.springframework.data.jpa.repository.query.ParameterBinding.BindingIdentifier; +import org.springframework.data.jpa.repository.query.ParameterBinding.MethodInvocationArgument; +import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterImpl; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; import org.springframework.data.jpa.repository.query.QueryParameterSetter.NamedOrIndexedQueryParameterSetter; -import org.springframework.data.jpa.repository.query.StringQuery.LikeParameterBinding; -import org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; @@ -63,20 +64,6 @@ static QueryParameterSetterFactory basic(JpaParameters parameters) { return new BasicQueryParameterSetterFactory(parameters); } - /** - * Creates a new {@link QueryParameterSetterFactory} for the given {@link JpaParameters} applying LIKE rewrite for - * renamed {@code :foo%} or {@code %:bar} bindings. - * - * @param parameters must not be {@literal null}. - * @return a basic {@link QueryParameterSetterFactory} that can handle named parameters. - */ - static QueryParameterSetterFactory forLikeRewrite(JpaParameters parameters) { - - Assert.notNull(parameters, "JpaParameters must not be null"); - - return new LikeRewritingQueryParameterSetterFactory(parameters); - } - /** * Creates a new {@link QueryParameterSetterFactory} using the given {@link JpaParameters} and * {@link ParameterMetadata}. @@ -133,7 +120,7 @@ private static QueryParameterSetter createSetter(Function parameters, String name) { + static JpaParameter findParameterForBinding(Parameters parameters, String name) { JpaParameters bindableParameters = parameters.getBindableParameters(); @@ -150,9 +137,20 @@ private static String getRequiredName(JpaParameter p) { return p.getName().orElseThrow(() -> new IllegalStateException(ParameterBinder.PARAMETER_NEEDS_TO_BE_NAMED)); } - @Nullable - static Object getValue(JpaParametersParameterAccessor accessor, Parameter parameter) { - return accessor.getValue(parameter); + static JpaParameter findParameterForBinding(Parameters parameters, int parameterIndex) { + + JpaParameters bindableParameters = parameters.getBindableParameters(); + + Assert.isTrue( // + parameterIndex < bindableParameters.getNumberOfParameters(), // + () -> String.format( // + "At least %s parameter(s) provided but only %s parameter(s) present in query", // + parameterIndex + 1, // + bindableParameters.getNumberOfParameters() // + ) // + ); + + return bindableParameters.getParameter(parameterIndex); } /** @@ -189,11 +187,11 @@ private static class ExpressionBasedQueryParameterSetterFactory extends QueryPar @Override public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery declaredQuery) { - if (!binding.isExpression()) { + if (!(binding.getOrigin()instanceof ParameterBinding.Expression e)) { return null; } - Expression expression = parser.parseExpression(binding.getExpression()); + Expression expression = parser.parseExpression(e.expression()); return createSetter(values -> evaluateExpression(expression, values), binding, null); } @@ -214,51 +212,12 @@ private Object evaluateExpression(Expression expression, JpaParametersParameterA } } - /** - * Handles bindings that use Like-rewriting. - * - * @author Mark Paluch - * @since 3.1.2 - */ - private static class LikeRewritingQueryParameterSetterFactory extends QueryParameterSetterFactory { - - private final Parameters parameters; - - /** - * @param parameters must not be {@literal null}. - */ - LikeRewritingQueryParameterSetterFactory(Parameters parameters) { - - Assert.notNull(parameters, "Parameters must not be null"); - - this.parameters = parameters; - } - - @Nullable - @Override - public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery declaredQuery) { - - if (binding.isExpression() || !(binding instanceof LikeParameterBinding likeBinding) - || !declaredQuery.hasNamedParameter()) { - return null; - } - JpaParameter parameter = QueryParameterSetterFactory.findParameterForBinding((JpaParameters) parameters, - likeBinding.getDeclaredName()); - - if (parameter == null) { - return null; - } - - return createSetter(values -> values.getValue(parameter), binding, parameter); - } - - } - /** * Extracts values for parameter bindings from method parameters. It handles named as well as indexed parameters. * * @author Jens Schauder * @author Oliver Gierke + * @author Mark Paluch * @since 2.0 */ private static class BasicQueryParameterSetterFactory extends QueryParameterSetterFactory { @@ -281,24 +240,16 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla Assert.notNull(binding, "Binding must not be null"); JpaParameter parameter; + if (!(binding.getOrigin()instanceof MethodInvocationArgument mia)) { + return QueryParameterSetter.NOOP; + } + + BindingIdentifier identifier = mia.identifier(); if (declaredQuery.hasNamedParameter()) { - parameter = findParameterForBinding(parameters, binding.getRequiredName()); + parameter = findParameterForBinding(parameters, identifier.getName()); } else { - - int parameterIndex = binding.getRequiredPosition() - 1; - JpaParameters bindableParameters = parameters.getBindableParameters(); - - Assert.isTrue( // - parameterIndex < bindableParameters.getNumberOfParameters(), // - () -> String.format( // - "At least %s parameter(s) provided but only %s parameter(s) present in query", // - binding.getRequiredPosition(), // - bindableParameters.getNumberOfParameters() // - ) // - ); - - parameter = bindableParameters.getParameter(binding.getRequiredPosition() - 1); + parameter = findParameterForBinding(parameters, identifier.getPosition() - 1); } return parameter == null // @@ -306,6 +257,10 @@ public QueryParameterSetter create(ParameterBinding binding, DeclaredQuery decla : createSetter(values -> getValue(values, parameter), binding, parameter); } + @Nullable + private Object getValue(JpaParametersParameterAccessor accessor, Parameter parameter) { + return accessor.getValue(parameter); + } } /** @@ -368,67 +323,4 @@ private Object getAndPrepare(JpaParameter parameter, ParameterMetadata metada } } - private static class ParameterImpl implements jakarta.persistence.Parameter { - - private final Class parameterType; - private final @Nullable String name; - private final @Nullable Integer position; - - /** - * Creates a new {@link ParameterImpl} for the given {@link JpaParameter} and {@link ParameterBinding}. - * - * @param parameter can be {@literal null}. - * @param binding must not be {@literal null}. - * @return a {@link jakarta.persistence.Parameter} object based on the information from the arguments. - */ - static jakarta.persistence.Parameter of(@Nullable JpaParameter parameter, ParameterBinding binding) { - - Class type = parameter == null ? Object.class : parameter.getType(); - - return new ParameterImpl<>(type, getName(parameter, binding), binding.getPosition()); - } - - /** - * Creates a new {@link ParameterImpl} for the given name, position and parameter type. - * - * @param parameterType must not be {@literal null}. - * @param name can be {@literal null}. - * @param position can be {@literal null}. - */ - private ParameterImpl(Class parameterType, @Nullable String name, @Nullable Integer position) { - - this.name = name; - this.position = position; - this.parameterType = parameterType; - } - - @Nullable - @Override - public String getName() { - return name; - } - - @Nullable - @Override - public Integer getPosition() { - return position; - } - - @Override - public Class getParameterType() { - return parameterType; - } - - @Nullable - private static String getName(@Nullable JpaParameter parameter, ParameterBinding binding) { - - if (binding.hasName() || parameter == null) { - return binding.getName(); - } - - return parameter.isNamedParameter() // - ? parameter.getName().orElseThrow(() -> new IllegalArgumentException("o_O parameter needs to have a name")) // - : null; - } - } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index f12f6aff7d..60a2498f9b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -16,19 +16,17 @@ package org.springframework.data.jpa.repository.query; import static java.util.regex.Pattern.*; -import static org.springframework.util.ObjectUtils.*; -import java.lang.reflect.Array; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.function.BiFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.springframework.data.jpa.provider.PersistenceProvider; -import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; +import org.springframework.data.jpa.repository.query.ParameterBinding.BindingIdentifier; +import org.springframework.data.jpa.repository.query.ParameterBinding.InParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.LikeParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterOrigin; import org.springframework.data.repository.query.SpelQueryContext; import org.springframework.data.repository.query.SpelQueryContext.SpelExtractor; import org.springframework.data.repository.query.parser.Part.Type; @@ -142,7 +140,7 @@ public boolean isDefaultProjection() { @Override public boolean hasNamedParameter() { - return bindings.stream().anyMatch(b -> b.getName() != null); + return bindings.stream().anyMatch(b -> b.getIdentifier().hasName()); } @Override @@ -268,18 +266,28 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St throw new IllegalArgumentException("Mixing of ? parameters and other forms like ?1 is not supported"); } + BindingIdentifier identifier; + if (parameterIndex != null) { + identifier = BindingIdentifier.of(parameterIndex); + } else { + identifier = BindingIdentifier.of(parameterName); + } + ParameterOrigin origin = ObjectUtils.isEmpty(expression) + ? ParameterOrigin.ofParameter(parameterName, parameterIndex) + : ParameterOrigin.ofExpression(expression); + switch (ParameterBindingType.of(typeSource)) { case LIKE: - Type likeType = LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); + Type likeType = ParameterBinding.LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); replacement = matcher.group(3); if (parameterIndex != null) { - checkAndRegister(new LikeParameterBinding(parameterIndex, likeType, expression), bindings); + checkAndRegister(new LikeParameterBinding(identifier, origin, likeType), bindings); } else { - LikeParameterBinding binding = likeParameterBindings.getOrCreate(parameterName, likeType, expression); + LikeParameterBinding binding = likeParameterBindings.getOrCreate(parameterName, likeType, origin); checkAndRegister(binding, bindings); replacement = ":" + binding.getRequiredName(); @@ -289,20 +297,14 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St case IN: - if (parameterIndex != null) { - checkAndRegister(new InParameterBinding(parameterIndex, expression), bindings); - } else { - checkAndRegister(new InParameterBinding(parameterName, expression), bindings); - } + checkAndRegister(new InParameterBinding(identifier, origin), bindings); break; case AS_IS: // fall-through we don't need a special parameter binding for the given parameter. default: - bindings.add(parameterIndex != null // - ? new ParameterBinding(null, parameterIndex, expression) // - : new ParameterBinding(parameterName, null, expression)); + bindings.add(new ParameterBinding(identifier, origin)); } if (replacement != null) { @@ -373,7 +375,7 @@ private static int tryFindGreatestParameterIndexIn(String query) { private static void checkAndRegister(ParameterBinding binding, List bindings) { bindings.stream() // - .filter(it -> it.hasName(binding.getName()) || it.hasPosition(binding.getPosition())) // + .filter(it -> it.bindsTo(binding)) // .forEach(it -> Assert.isTrue(it.equals(binding), String.format(MESSAGE, it, binding))); if (!bindings.contains(binding)) { @@ -431,6 +433,10 @@ static ParameterBindingType of(String typeSource) { } } + private static class Metadata { + private boolean usesJdbcStyleParameters = false; + } + /** * Utility to create unique parameter bindings for LIKE that can be evaluated by * {@code LikeRewritingQueryParameterSetterFactory}. @@ -448,27 +454,26 @@ static class LikeParameterBindings { * * @param parameterName the parameter name as declared in the actual JPQL query. * @param likeType type of the LIKE expression. - * @param expression expression content if the LIKE comparison value is provided by a SpEL expression. + * @param origin origin of the parameter. * @return the Like binding. Can return an already existing binding. */ - LikeParameterBinding getOrCreate(String parameterName, Type likeType, @Nullable String expression) { + LikeParameterBinding getOrCreate(String parameterName, Type likeType, ParameterOrigin origin) { List likeParameterBindings = likeBindings.computeIfAbsent(parameterName, s -> new ArrayList<>()); LikeParameterBinding reuse = null; // unique parameters only required for literals as expressions create unique parameter names - if (expression == null) { + if (origin.isMethodArgument()) { for (LikeParameterBinding likeParameterBinding : likeParameterBindings) { - if (likeParameterBinding.type == likeType) { + if (likeParameterBinding.getType() == likeType) { reuse = likeParameterBinding; break; } } } - String declaredParameterName = parameterName; if (reuse != null) { return reuse; } @@ -477,396 +482,10 @@ LikeParameterBinding getOrCreate(String parameterName, Type likeType, @Nullable parameterName = parameterName + "_" + likeParameterBindings.size(); } - LikeParameterBinding binding = new LikeParameterBinding(parameterName, declaredParameterName, likeType, - expression); + LikeParameterBinding binding = new LikeParameterBinding(BindingIdentifier.of(parameterName), origin, likeType); likeParameterBindings.add(binding); return binding; } } - - /** - * A generic parameter binding with name or position information. - * - * @author Thomas Darimont - */ - static class ParameterBinding { - - private final @Nullable String name; - private final @Nullable String expression; - private final @Nullable Integer position; - - /** - * Creates a new {@link ParameterBinding} for the parameter with the given position. - * - * @param position must not be {@literal null}. - */ - ParameterBinding(Integer position) { - this(null, position, null); - } - - /** - * Creates a new {@link ParameterBinding} for the parameter with the given name, position and expression - * information. Either {@literal name} or {@literal position} must be not {@literal null}. - * - * @param name of the parameter may be {@literal null}. - * @param position of the parameter may be {@literal null}. - * @param expression the expression to apply to any value for this parameter. - */ - ParameterBinding(@Nullable String name, @Nullable Integer position, @Nullable String expression) { - - if (name == null) { - Assert.notNull(position, "Position must not be null"); - } - - if (position == null) { - Assert.notNull(name, "Name must not be null"); - } - - this.name = name; - this.position = position; - this.expression = expression; - } - - /** - * Returns whether the binding has the given name. Will always be {@literal false} in case the - * {@link ParameterBinding} has been set up from a position. - */ - boolean hasName(@Nullable String name) { - return this.position == null && this.name != null && this.name.equals(name); - } - - boolean hasName() { - return this.position == null && !ObjectUtils.isEmpty(this.name); - } - - /** - * Returns whether the binding has the given position. Will always be {@literal false} in case the - * {@link ParameterBinding} has been set up from a name. - */ - boolean hasPosition(@Nullable Integer position) { - return position != null && this.name == null && position.equals(this.position); - } - - boolean hasPosition() { - return position != null && this.name == null; - } - - /** - * @return the name - */ - @Nullable - public String getName() { - return name; - } - - /** - * @return the name - * @throws IllegalStateException if the name is not available. - * @since 2.0 - */ - String getRequiredName() throws IllegalStateException { - - String name = getName(); - - if (name != null) { - return name; - } - - throw new IllegalStateException(String.format("Required name for %s not available", this)); - } - - /** - * @return the position - */ - @Nullable - Integer getPosition() { - return position; - } - - /** - * @return the position - * @throws IllegalStateException if the position is not available. - * @since 2.0 - */ - int getRequiredPosition() throws IllegalStateException { - - Integer position = getPosition(); - - if (position != null) { - return position; - } - - throw new IllegalStateException(String.format("Required position for %s not available", this)); - } - - /** - * @return {@literal true} if this parameter binding is a synthetic SpEL expression. - */ - public boolean isExpression() { - return this.expression != null; - } - - @Override - public int hashCode() { - - int result = 17; - - result += nullSafeHashCode(this.name); - result += nullSafeHashCode(this.position); - result += nullSafeHashCode(this.expression); - - return result; - } - - @Override - public boolean equals(Object obj) { - - if (!(obj instanceof ParameterBinding)) { - return false; - } - - ParameterBinding that = (ParameterBinding) obj; - - return nullSafeEquals(this.name, that.name) && nullSafeEquals(this.position, that.position) - && nullSafeEquals(this.expression, that.expression); - } - - @Override - public String toString() { - return String.format("ParameterBinding [name: %s, position: %d, expression: %s]", getName(), getPosition(), - getExpression()); - } - - /** - * @param valueToBind value to prepare - */ - @Nullable - public Object prepare(@Nullable Object valueToBind) { - return valueToBind; - } - - @Nullable - public String getExpression() { - return expression; - } - } - - /** - * Represents a {@link ParameterBinding} in a JPQL query augmented with instructions of how to apply a parameter as an - * {@code IN} parameter. - * - * @author Thomas Darimont - */ - static class InParameterBinding extends ParameterBinding { - - /** - * Creates a new {@link InParameterBinding} for the parameter with the given name. - */ - InParameterBinding(String name, @Nullable String expression) { - super(name, null, expression); - } - - /** - * Creates a new {@link InParameterBinding} for the parameter with the given position. - */ - InParameterBinding(int position, @Nullable String expression) { - super(null, position, expression); - } - - @Override - public Object prepare(@Nullable Object value) { - - if (!ObjectUtils.isArray(value)) { - return value; - } - - int length = Array.getLength(value); - Collection result = new ArrayList<>(length); - - for (int i = 0; i < length; i++) { - result.add(Array.get(value, i)); - } - - return result; - } - } - - /** - * Represents a parameter binding in a JPQL query augmented with instructions of how to apply a parameter as LIKE - * parameter. This allows expressions like {@code …like %?1} in the JPQL query, which is not allowed by plain JPA. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Mark Paluch - */ - static class LikeParameterBinding extends ParameterBinding { - - private static final List SUPPORTED_TYPES = Arrays.asList(Type.CONTAINING, Type.STARTING_WITH, - Type.ENDING_WITH, Type.LIKE); - - private final Type type; - - private final @Nullable String declaredName; - - /** - * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type}. - * - * @param name parameter name in the final query, must not be {@literal null} or empty. - * @param declaredName name of the declared parameter from the original query, referring to a - * {@link JpaParameter#getName()}, must not be {@literal null} or empty. - * @param type must not be {@literal null}. - */ - LikeParameterBinding(String name, String declaredName, Type type) { - this(name, declaredName, type, null); - } - - /** - * Creates a new {@link LikeParameterBinding} for the parameter with the given name and {@link Type} and parameter - * binding input. - * - * @param name parameter name in the final query, must not be {@literal null} or empty. - * @param declaredName name of the declared parameter from the original query, referring to a - * {@link JpaParameter#getName()}, must not be {@literal null} or empty. - * @param type must not be {@literal null}. - * @param expression may be {@literal null}. - */ - LikeParameterBinding(String name, String declaredName, Type type, @Nullable String expression) { - - super(name, null, expression); - - Assert.hasText(name, "Name must not be null or empty"); - if (expression == null && !StringUtils.hasText(declaredName)) { - throw new IllegalArgumentException("Declared name must not be null or empty"); - } - Assert.notNull(type, "Type must not be null"); - - Assert.isTrue(SUPPORTED_TYPES.contains(type), - String.format("Type must be one of %s", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); - - this.type = type; - this.declaredName = declaredName; - } - - /** - * Creates a new {@link LikeParameterBinding} for the parameter with the given position and {@link Type}. - * - * @param position position of the parameter in the query. - * @param type must not be {@literal null}. - */ - LikeParameterBinding(int position, Type type) { - this(position, type, null); - } - - /** - * Creates a new {@link LikeParameterBinding} for the parameter with the given position and {@link Type}. - * - * @param position position of the parameter in the query. - * @param type must not be {@literal null}. - * @param expression may be {@literal null}. - */ - LikeParameterBinding(int position, Type type, @Nullable String expression) { - - super(null, position, expression); - - Assert.isTrue(position > 0, "Position must be greater than zero"); - Assert.notNull(type, "Type must not be null"); - - Assert.isTrue(SUPPORTED_TYPES.contains(type), - String.format("Type must be one of %s", StringUtils.collectionToCommaDelimitedString(SUPPORTED_TYPES))); - - this.type = type; - this.declaredName = null; - } - - /** - * Returns the {@link Type} of the binding. - * - * @return the type - */ - public Type getType() { - return type; - } - - @Nullable - public String getDeclaredName() { - return declaredName; - } - - /** - * Prepares the given raw keyword according to the like type. - */ - @Nullable - @Override - public Object prepare(@Nullable Object value) { - - Object unwrapped = PersistenceProvider.unwrapTypedParameterValue(value); - if (unwrapped == null) { - return null; - } - - return switch (type) { - case STARTING_WITH -> String.format("%s%%", unwrapped); - case ENDING_WITH -> String.format("%%%s", unwrapped); - case CONTAINING -> String.format("%%%s%%", unwrapped); - default -> unwrapped; - }; - } - - @Override - public boolean equals(Object obj) { - - if (!(obj instanceof LikeParameterBinding)) { - return false; - } - - LikeParameterBinding that = (LikeParameterBinding) obj; - - return super.equals(obj) && this.type.equals(that.type); - } - - @Override - public int hashCode() { - - int result = super.hashCode(); - - result += nullSafeHashCode(this.type); - - return result; - } - - @Override - public String toString() { - return String.format("LikeBinding [name: %s, position: %d, type: %s]", getName(), getPosition(), type); - } - - /** - * Extracts the like {@link Type} from the given JPA like expression. - * - * @param expression must not be {@literal null} or empty. - */ - private static Type getLikeTypeFrom(String expression) { - - Assert.hasText(expression, "Expression must not be null or empty"); - - if (expression.matches("%.*%")) { - return Type.CONTAINING; - } - - if (expression.startsWith("%")) { - return Type.ENDING_WITH; - } - - if (expression.endsWith("%")) { - return Type.STARTING_WITH; - } - - return Type.LIKE; - } - - } - - static class Metadata { - private boolean usesJdbcStyleParameters = false; - } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java index d94779b2c0..f07f9d7e80 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/LikeBindingUnitTests.java @@ -18,7 +18,9 @@ import static org.assertj.core.api.Assertions.*; import org.junit.jupiter.api.Test; -import org.springframework.data.jpa.repository.query.StringQuery.LikeParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.BindingIdentifier; +import org.springframework.data.jpa.repository.query.ParameterBinding.LikeParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterOrigin; import org.springframework.data.repository.query.parser.Part.Type; /** @@ -32,57 +34,39 @@ class LikeBindingUnitTests { private static void assertAugmentedValue(Type type, Object value) { - LikeParameterBinding binding = new LikeParameterBinding("foo", "foo", type); + LikeParameterBinding binding = new LikeParameterBinding(BindingIdentifier.of("foo"), + ParameterOrigin.ofExpression("foo"), type); assertThat(binding.prepare("value")).isEqualTo(value); } @Test void rejectsNullName() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding(null, "", Type.CONTAINING)); + assertThatIllegalArgumentException() + .isThrownBy(() -> new LikeParameterBinding(null, ParameterOrigin.ofExpression(""), Type.CONTAINING)); } @Test void rejectsEmptyName() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("", "", Type.CONTAINING)); + assertThatIllegalArgumentException().isThrownBy( + () -> new LikeParameterBinding(BindingIdentifier.of(""), ParameterOrigin.ofExpression(""), Type.CONTAINING)); } @Test void rejectsNullType() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("foo", "foo", null)); + assertThatIllegalArgumentException().isThrownBy( + () -> new LikeParameterBinding(BindingIdentifier.of("foo"), ParameterOrigin.ofExpression("foo"), null)); } @Test void rejectsInvalidType() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding("foo", "foo", Type.SIMPLE_PROPERTY)); + assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding(BindingIdentifier.of("foo"), + ParameterOrigin.ofExpression("foo"), Type.SIMPLE_PROPERTY)); } @Test void rejectsInvalidPosition() { - assertThatIllegalArgumentException().isThrownBy(() -> new LikeParameterBinding(0, Type.CONTAINING)); - } - - @Test - void setsUpInstanceForName() { - - LikeParameterBinding binding = new LikeParameterBinding("foo", "foo", Type.CONTAINING); - - assertThat(binding.hasName("foo")).isTrue(); - assertThat(binding.hasName("bar")).isFalse(); - assertThat(binding.hasName(null)).isFalse(); - assertThat(binding.hasPosition(0)).isFalse(); - assertThat(binding.getType()).isEqualTo(Type.CONTAINING); - } - - @Test - void setsUpInstanceForIndex() { - - LikeParameterBinding binding = new LikeParameterBinding(1, Type.CONTAINING); - - assertThat(binding.hasName("foo")).isFalse(); - assertThat(binding.hasName(null)).isFalse(); - assertThat(binding.hasPosition(0)).isFalse(); - assertThat(binding.hasPosition(1)).isTrue(); - assertThat(binding.getType()).isEqualTo(Type.CONTAINING); + assertThatIllegalArgumentException().isThrownBy( + () -> new LikeParameterBinding(BindingIdentifier.of(0), ParameterOrigin.ofExpression(""), Type.CONTAINING)); } @Test @@ -92,6 +76,7 @@ void augmentsValueCorrectly() { assertAugmentedValue(Type.ENDING_WITH, "%value"); assertAugmentedValue(Type.STARTING_WITH, "value%"); - assertThat(new LikeParameterBinding(1, Type.CONTAINING).prepare(null)).isNull(); + assertThat(new LikeParameterBinding(BindingIdentifier.of(1), ParameterOrigin.ofParameter(null, 1), Type.CONTAINING) + .prepare(null)).isNull(); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java index cf951a8fbe..ca31987720 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java @@ -25,9 +25,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; - import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; -import org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterOrigin; /** * Unit tests for {@link QueryParameterSetterFactory}. @@ -60,6 +59,8 @@ void noExceptionWhenQueryDoesNotContainNamedParameters() { @Test // DATAJPA-1058 void exceptionWhenQueryContainNamedParametersAndMethodParametersAreNotNamed() { + when(binding.getOrigin()).thenReturn(ParameterOrigin.ofParameter("NamedParameter", 1)); + assertThatExceptionOfType(IllegalStateException.class) // .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("from Employee e where e.name = :NamedParameter", false))) // .withMessageContaining("Java 8") // @@ -76,6 +77,7 @@ void exceptionWhenCriteriaQueryContainsInsufficientAmountOfParameters() { // one argument present in the method signature when(binding.getRequiredPosition()).thenReturn(1); + when(binding.getOrigin()).thenReturn(ParameterOrigin.ofParameter(null, 1)); assertThatExceptionOfType(IllegalArgumentException.class) // .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("from Employee e where e.name = :NamedParameter", false))) // @@ -90,6 +92,7 @@ void exceptionWhenBasicQueryContainsInsufficientAmountOfParameters() { // one argument present in the method signature when(binding.getRequiredPosition()).thenReturn(1); + when(binding.getOrigin()).thenReturn(ParameterOrigin.ofParameter(null, 1)); assertThatExceptionOfType(IllegalArgumentException.class) // .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("from Employee e where e.name = ?1", false))) // diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index d36579bb63..0e7546e0b2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -22,9 +22,9 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; -import org.springframework.data.jpa.repository.query.StringQuery.InParameterBinding; -import org.springframework.data.jpa.repository.query.StringQuery.LikeParameterBinding; -import org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.Expression; +import org.springframework.data.jpa.repository.query.ParameterBinding.InParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.LikeParameterBinding; import org.springframework.data.repository.query.parser.Part.Type; /** @@ -55,7 +55,7 @@ void doesNotConsiderPlainLikeABinding() { LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); assertThat(binding.getType()).isEqualTo(Type.LIKE); - assertThat(binding.hasName("firstname")).isTrue(); + assertThat(binding.getName()).isEqualTo("firstname"); } @Test // DATAJPA-292 @@ -73,12 +73,12 @@ void detectsPositionalLikeBindings() { LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); assertThat(binding).isNotNull(); - assertThat(binding.hasPosition(1)).isTrue(); + assertThat(binding.getRequiredPosition()).isEqualTo(1); assertThat(binding.getType()).isEqualTo(Type.CONTAINING); binding = (LikeParameterBinding) bindings.get(1); assertThat(binding).isNotNull(); - assertThat(binding.hasPosition(2)).isTrue(); + assertThat(binding.getRequiredPosition()).isEqualTo(2); assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); } @@ -95,7 +95,7 @@ void detectsNamedLikeBindings() { LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); assertThat(binding).isNotNull(); - assertThat(binding.hasName("firstname")).isTrue(); + assertThat(binding.getRequiredName()).isEqualTo("firstname"); assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); } @@ -114,12 +114,12 @@ void rewritesNamedLikeToUniqueParametersIfNecessary() { LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); assertThat(binding).isNotNull(); - assertThat(binding.hasName("firstname")).isTrue(); + assertThat(binding.getName()).isEqualTo("firstname"); assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); binding = (LikeParameterBinding) bindings.get(1); assertThat(binding).isNotNull(); - assertThat(binding.hasName("firstname_1")).isTrue(); + assertThat(binding.getName()).isEqualTo("firstname_1"); assertThat(binding.getType()).isEqualTo(Type.STARTING_WITH); } @@ -139,12 +139,12 @@ void reusesLikeBindingsWherePossible() { LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); assertThat(binding).isNotNull(); - assertThat(binding.hasName("firstname")).isTrue(); + assertThat(binding.getName()).isEqualTo("firstname"); assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); binding = (LikeParameterBinding) bindings.get(1); assertThat(binding).isNotNull(); - assertThat(binding.hasName("firstname_1")).isTrue(); + assertThat(binding.getName()).isEqualTo("firstname_1"); assertThat(binding.getType()).isEqualTo(Type.CONTAINING); } @@ -342,8 +342,8 @@ void shouldReplaceAllPositionExpressionParametersWithInClause() { String queryString = query.getQueryString(); assertThat(queryString).isEqualTo("select a from A a where a.b in ?1 and a.c in ?2"); - assertThat(query.getParameterBindings().get(0).getExpression()).isEqualTo("#bs"); - assertThat(query.getParameterBindings().get(1).getExpression()).isEqualTo("#cs"); + assertThat(((Expression) query.getParameterBindings().get(0).getOrigin()).expression()).isEqualTo("#bs"); + assertThat(((Expression) query.getParameterBindings().get(1).getOrigin()).expression()).isEqualTo("#cs"); } @Test // DATAJPA-864 @@ -380,7 +380,7 @@ void bindingsMatchQueryForIdenticalSpelExpressions() { for (ParameterBinding binding : bindings) { assertThat(binding.getName()).isNotNull(); assertThat(query.getQueryString()).contains(binding.getName()); - assertThat(binding.getExpression()).isEqualTo("#exp"); + assertThat(((Expression) binding.getOrigin()).expression()).isEqualTo("#exp"); } } @@ -612,7 +612,7 @@ private void assertPositionalBinding(Class bindingTy assertThat(bindingType.isInstance(expectedBinding)).isTrue(); assertThat(expectedBinding).isNotNull(); - assertThat(expectedBinding.hasPosition(position)).isTrue(); + assertThat(expectedBinding.getPosition()).isEqualTo(position); } private void assertNamedBinding(Class bindingType, String parameterName, @@ -620,6 +620,6 @@ private void assertNamedBinding(Class bindingType, S assertThat(bindingType.isInstance(expectedBinding)).isTrue(); assertThat(expectedBinding).isNotNull(); - assertThat(expectedBinding.hasName(parameterName)).isTrue(); + assertThat(expectedBinding.getName()).isEqualTo(parameterName); } } From 2cd3aca078d646f39cb5a4afca26d8d6ca1f6785 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 21 Jul 2023 14:57:34 +0200 Subject: [PATCH 439/821] Ensure correct bindings for parameter reuse. We now verify all bindings to ensure that a like-parameter doesn't mix up with plain bindings (e.g. firstname = %:myparam or lastname = :myparam). We also create unique synthetic parameters for positional bindings. Also, fix Regex to properly detect named, anonymous and positional bind markers. See #3041 --- .../repository/query/ParameterBinding.java | 115 ++++++----- .../query/QueryParameterSetterFactory.java | 44 ++++- .../jpa/repository/query/StringQuery.java | 187 ++++++++++++------ .../jpa/repository/UserRepositoryTests.java | 3 + .../ExpressionBasedStringQueryUnitTests.java | 56 ++++++ .../query/StringQueryUnitTests.java | 127 +++++++++--- .../jpa/repository/sample/UserRepository.java | 4 + 7 files changed, 392 insertions(+), 144 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinding.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinding.java index bc0086fab1..3db2ab15c9 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinding.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinding.java @@ -24,7 +24,6 @@ import java.util.List; import org.springframework.data.jpa.provider.PersistenceProvider; -import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -151,8 +150,8 @@ public Object prepare(@Nullable Object valueToBind) { /** * Check whether the {@code other} binding uses the same bind target. * - * @param other - * @return + * @param other must not be {@literal null}. + * @return {@code true} if the other binding uses the same parameter to bind to as this one. */ public boolean bindsTo(ParameterBinding other) { @@ -171,6 +170,17 @@ public boolean bindsTo(ParameterBinding other) { return false; } + /** + * Check whether this binding can be bound as the {@code other} binding by checking its type and origin. Subclasses + * may override this method to include other properties for the compatibility check. + * + * @param other + * @return {@code true} if the other binding is compatible with this one. + */ + public boolean isCompatibleWith(ParameterBinding other) { + return other.getClass() == getClass() && other.getOrigin().equals(getOrigin()); + } + /** * Represents a {@link ParameterBinding} in a JPQL query augmented with instructions of how to apply a parameter as an * {@code IN} parameter. @@ -294,6 +304,16 @@ public String toString() { getType()); } + @Override + public boolean isCompatibleWith(ParameterBinding binding) { + + if (super.isCompatibleWith(binding) && binding instanceof LikeParameterBinding other) { + return getType() == other.getType(); + } + + return false; + } + /** * Extracts the like {@link Type} from the given JPA like expression. * @@ -319,52 +339,12 @@ static Type getLikeTypeFrom(String expression) { } } - static class ParameterImpl implements jakarta.persistence.Parameter { - - private final BindingIdentifier identifier; - private final Class parameterType; - - /** - * Creates a new {@link ParameterImpl} for the given {@link JpaParameter} and {@link ParameterBinding}. - * - * @param parameter can be {@literal null}. - * @param binding must not be {@literal null}. - * @return a {@link jakarta.persistence.Parameter} object based on the information from the arguments. - */ - static jakarta.persistence.Parameter of(@Nullable JpaParameter parameter, ParameterBinding binding) { - - Class type = parameter == null ? Object.class : parameter.getType(); - - return new ParameterImpl<>(binding.getIdentifier(), type); - } - - public ParameterImpl(BindingIdentifier identifier, Class parameterType) { - this.identifier = identifier; - this.parameterType = parameterType; - } - - @Nullable - @Override - public String getName() { - return identifier.hasName() ? identifier.getName() : null; - } - - @Nullable - @Override - public Integer getPosition() { - return identifier.hasPosition() ? identifier.getPosition() : null; - } - - @Override - public Class getParameterType() { - return parameterType; - } - - } - /** * Identifies a binding parameter by name, position or both. Used to bind parameters to a query or to describe a * {@link MethodInvocationArgument} origin. + * + * @author Mark Paluch + * @since 3.1.2 */ sealed interface BindingIdentifier permits Named,Indexed,NamedAndIndexed { @@ -453,6 +433,11 @@ public boolean hasName() { public String getName() { return name(); } + + @Override + public String toString() { + return name(); + } } private record Indexed(int position) implements BindingIdentifier { @@ -466,6 +451,11 @@ public boolean hasPosition() { public int getPosition() { return position(); } + + @Override + public String toString() { + return "[" + position() + "]"; + } } private record NamedAndIndexed(String name, int position) implements BindingIdentifier { @@ -489,10 +479,18 @@ public boolean hasPosition() { public int getPosition() { return position(); } + + @Override + public String toString() { + return "[" + name() + ", " + position() + "]"; + } } /** * Value type hierarchy to describe where a binding parameter comes from, either method call or an expression. + * + * @author Mark Paluch + * @since 3.1.2 */ sealed interface ParameterOrigin permits Expression,MethodInvocationArgument { @@ -508,11 +506,11 @@ static Expression ofExpression(String expression) { /** * Creates a {@link MethodInvocationArgument} object for {@code name} and {@code position}. Either the name or the - * position must be given, + * position must be given. * * @param name the parameter name from the method invocation, can be {@literal null}. * @param position the parameter position (1-based) from the method invocation, can be {@literal null}. - * @return {@link MethodInvocationArgument} object for {@code name} and {@code position} + * @return {@link MethodInvocationArgument} object for {@code name} and {@code position}. */ static MethodInvocationArgument ofParameter(@Nullable String name, @Nullable Integer position) { @@ -528,6 +526,16 @@ static MethodInvocationArgument ofParameter(@Nullable String name, @Nullable Int return ofParameter(identifier); } + /** + * Creates a {@link MethodInvocationArgument} object for {@code position}. + * + * @param position the parameter position (1-based) from the method invocation. + * @return {@link MethodInvocationArgument} object for {@code position}. + */ + static MethodInvocationArgument ofParameter(int position) { + return ofParameter(BindingIdentifier.of(position)); + } + /** * Creates a {@link MethodInvocationArgument} using {@link BindingIdentifier}. * @@ -535,12 +543,17 @@ static MethodInvocationArgument ofParameter(@Nullable String name, @Nullable Int * @return {@link MethodInvocationArgument} for {@link BindingIdentifier}. */ static MethodInvocationArgument ofParameter(BindingIdentifier identifier) { - return new MethodInvocationArgument(identifier); } + /** + * @return {@code true} if the origin is a method argument reference. + */ boolean isMethodArgument(); + /** + * @return {@code true} if the origin is an expression. + */ boolean isExpression(); } @@ -548,6 +561,8 @@ static MethodInvocationArgument ofParameter(BindingIdentifier identifier) { * Value object capturing the expression of which a binding parameter originates. * * @param expression + * @author Mark Paluch + * @since 3.1.2 */ public record Expression(String expression) implements ParameterOrigin { @@ -566,6 +581,8 @@ public boolean isExpression() { * Value object capturing the method invocation parameter reference. * * @param identifier + * @author Mark Paluch + * @since 3.1.2 */ public record MethodInvocationArgument(BindingIdentifier identifier) implements ParameterOrigin { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index 0ea048f32b..b6c57b7ebf 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -24,7 +24,6 @@ import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.jpa.repository.query.ParameterBinding.BindingIdentifier; import org.springframework.data.jpa.repository.query.ParameterBinding.MethodInvocationArgument; -import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterImpl; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; import org.springframework.data.jpa.repository.query.QueryParameterSetter.NamedOrIndexedQueryParameterSetter; import org.springframework.data.repository.query.Parameter; @@ -323,4 +322,47 @@ private Object getAndPrepare(JpaParameter parameter, ParameterMetadata metada } } + static class ParameterImpl implements jakarta.persistence.Parameter { + + private final BindingIdentifier identifier; + private final Class parameterType; + + /** + * Creates a new {@link ParameterImpl} for the given {@link JpaParameter} and {@link ParameterBinding}. + * + * @param parameter can be {@literal null}. + * @param binding must not be {@literal null}. + * @return a {@link jakarta.persistence.Parameter} object based on the information from the arguments. + */ + static jakarta.persistence.Parameter of(@Nullable JpaParameter parameter, ParameterBinding binding) { + + Class type = parameter == null ? Object.class : parameter.getType(); + + return new ParameterImpl<>(binding.getIdentifier(), type); + } + + public ParameterImpl(BindingIdentifier identifier, Class parameterType) { + this.identifier = identifier; + this.parameterType = parameterType; + } + + @Nullable + @Override + public String getName() { + return identifier.hasName() ? identifier.getName() : null; + } + + @Nullable + @Override + public Integer getPosition() { + return identifier.hasPosition() ? identifier.getPosition() : null; + } + + @Override + public Class getParameterType() { + return parameterType; + } + + } + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 60a2498f9b..cd549ef6f3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -18,14 +18,18 @@ import static java.util.regex.Pattern.*; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.springframework.data.jpa.repository.query.ParameterBinding.BindingIdentifier; import org.springframework.data.jpa.repository.query.ParameterBinding.InParameterBinding; import org.springframework.data.jpa.repository.query.ParameterBinding.LikeParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.MethodInvocationArgument; import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterOrigin; import org.springframework.data.repository.query.SpelQueryContext; import org.springframework.data.repository.query.SpelQueryContext.SpelExtractor; @@ -41,7 +45,7 @@ * Encapsulation of a JPA query String. Offers access to parameters as bindings. The internal query String is cleaned * from decorated parameters like {@literal %:lastname%} and the matching bindings take care of applying the decorations * in the {@link ParameterBinding#prepare(Object)} method. Note that this class also handles replacing SpEL expressions - * with synthetic bind parameters + * with synthetic bind parameters. * * @author Oliver Gierke * @author Thomas Darimont @@ -169,9 +173,9 @@ enum ParameterBindingParser { // .............................................................^ start with a question mark. private static final Pattern PARAMETER_BINDING_BY_INDEX = Pattern.compile(POSITIONAL_OR_INDEXED_PARAMETER); private static final Pattern PARAMETER_BINDING_PATTERN; - private static final Pattern JDBC_STYLE_PARAM = Pattern.compile(" \\?(?!\\d)"); // ?[no digit] - private static final Pattern NUMBERED_STYLE_PARAM = Pattern.compile(" \\?(?=\\d)"); // ?[digit] - private static final Pattern NAMED_STYLE_PARAM = Pattern.compile(" :\\w+"); // :[text] + private static final Pattern JDBC_STYLE_PARAM = Pattern.compile("(?!\\\\)\\?(?!\\d)"); // no \ and [no digit] + private static final Pattern NUMBERED_STYLE_PARAM = Pattern.compile("(?!\\\\)\\?\\d"); // no \ and [digit] + private static final Pattern NAMED_STYLE_PARAM = Pattern.compile("(?!\\\\):\\w+"); // no \ and :[text] private static final String MESSAGE = "Already found parameter binding with same index / parameter name but differing binding type; " + "Already have: %s, found %s; If you bind a parameter multiple times make sure they use the same binding"; @@ -233,10 +237,21 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(resultingQuery); int expressionParameterIndex = parametersShouldBeAccessedByIndex ? greatestParameterIndex : 0; + int syntheticParameterIndex = expressionParameterIndex + spelExtractor.size(); - LikeParameterBindings likeParameterBindings = new LikeParameterBindings(); + ParameterBindings parameterBindings = new ParameterBindings(bindings, it -> checkAndRegister(it, bindings), + syntheticParameterIndex); + int currentIndex = 0; boolean usesJpaStyleParameters = false; + if (JDBC_STYLE_PARAM.matcher(resultingQuery).find()) { + queryMeta.usesJdbcStyleParameters = true; + } + + if (NUMBERED_STYLE_PARAM.matcher(resultingQuery).find() || NAMED_STYLE_PARAM.matcher(resultingQuery).find()) { + usesJpaStyleParameters = true; + } + while (matcher.find()) { if (spelExtractor.isQuoted(matcher.start())) { @@ -253,10 +268,6 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St String expression = spelExtractor.getParameter(parameterName == null ? parameterIndexString : parameterName); String replacement = null; - queryMeta.usesJdbcStyleParameters = JDBC_STYLE_PARAM.matcher(resultingQuery).find(); - usesJpaStyleParameters = NUMBERED_STYLE_PARAM.matcher(resultingQuery).find() - || NAMED_STYLE_PARAM.matcher(resultingQuery).find(); - expressionParameterIndex++; if ("".equals(parameterIndexString)) { parameterIndex = expressionParameterIndex; @@ -266,51 +277,65 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St throw new IllegalArgumentException("Mixing of ? parameters and other forms like ?1 is not supported"); } - BindingIdentifier identifier; + BindingIdentifier queryParameter; if (parameterIndex != null) { - identifier = BindingIdentifier.of(parameterIndex); + queryParameter = BindingIdentifier.of(parameterIndex); } else { - identifier = BindingIdentifier.of(parameterName); + queryParameter = BindingIdentifier.of(parameterName); } ParameterOrigin origin = ObjectUtils.isEmpty(expression) ? ParameterOrigin.ofParameter(parameterName, parameterIndex) : ParameterOrigin.ofExpression(expression); + BindingIdentifier targetBinding = queryParameter; switch (ParameterBindingType.of(typeSource)) { case LIKE: - Type likeType = ParameterBinding.LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); - replacement = matcher.group(3); + Type likeType = LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); - if (parameterIndex != null) { - checkAndRegister(new LikeParameterBinding(identifier, origin, likeType), bindings); + if (origin.isExpression()) { + parameterBindings.register(new LikeParameterBinding(queryParameter, origin, likeType)); } else { - - LikeParameterBinding binding = likeParameterBindings.getOrCreate(parameterName, likeType, origin); - checkAndRegister(binding, bindings); - - replacement = ":" + binding.getRequiredName(); + targetBinding = parameterBindings.register(queryParameter, origin, + (identifier) -> new LikeParameterBinding(identifier, origin, likeType)); } break; case IN: - checkAndRegister(new InParameterBinding(identifier, origin), bindings); + parameterBindings.register(new InParameterBinding(queryParameter, origin)); break; - case AS_IS: // fall-through we don't need a special parameter binding for the given parameter. + case AS_IS: // fall-through we don't need a special parameter queryParameter for the given parameter. default: - bindings.add(new ParameterBinding(identifier, origin)); + if (origin.isExpression()) { + parameterBindings.register(new ParameterBinding(queryParameter, origin)); + } else { + targetBinding = parameterBindings.register(queryParameter, origin, + (identifier) -> new ParameterBinding(identifier, origin)); + } } - if (replacement != null) { - resultingQuery = replaceFirst(resultingQuery, matcher.group(2), replacement); + replacement = targetBinding.hasName() ? ":" + targetBinding.getName() + : ((!usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) ? "?" + : "?" + targetBinding.getPosition()); + String result; + String substring = matcher.group(2); + + int index = resultingQuery.indexOf(substring, currentIndex); + if (index < 0) { + result = resultingQuery; + } else { + currentIndex = index + replacement.length(); + result = resultingQuery.substring(0, index) + replacement + + resultingQuery.substring(index + substring.length()); } + resultingQuery = result; } return resultingQuery; @@ -336,16 +361,6 @@ private static SpelExtractor createSpelExtractor(String queryWithSpel, boolean p return SpelQueryContext.of(indexToParameterName, parameterNameToReplacement).parse(queryWithSpel); } - private static String replaceFirst(String text, String substring, String replacement) { - - int index = text.indexOf(substring); - if (index < 0) { - return text; - } - - return text.substring(0, index) + replacement + text.substring(index + substring.length()); - } - @Nullable private static Integer getParameterIndex(@Nullable String parameterIndexString) { @@ -438,54 +453,96 @@ private static class Metadata { } /** - * Utility to create unique parameter bindings for LIKE that can be evaluated by - * {@code LikeRewritingQueryParameterSetterFactory}. + * Utility to create unique parameter bindings for LIKE that refer to the same underlying method parameter but are + * bound to potentially unique query parameters for {@link LikeParameterBinding#prepare(Object) LIKE rewrite}. * * @author Mark Paluch * @since 3.1.2 */ - static class LikeParameterBindings { + static class ParameterBindings { + + private final MultiValueMap methodArgumentToLikeBindings = new LinkedMultiValueMap<>(); + + private final Consumer registration; + private int syntheticParameterIndex; + + public ParameterBindings(List bindings, Consumer registration, + int syntheticParameterIndex) { - private final MultiValueMap likeBindings = new LinkedMultiValueMap<>(); + for (ParameterBinding binding : bindings) { + this.methodArgumentToLikeBindings.put(binding.getIdentifier(), new ArrayList<>(List.of(binding))); + } + + this.registration = registration; + this.syntheticParameterIndex = syntheticParameterIndex; + } /** - * Get an existing or create a new {@link LikeParameterBinding} if a previously bound {@code LIKE} expression cannot - * be reused. + * Return whether the identifier is already bound. * - * @param parameterName the parameter name as declared in the actual JPQL query. - * @param likeType type of the LIKE expression. - * @param origin origin of the parameter. - * @return the Like binding. Can return an already existing binding. + * @param identifier + * @return */ - LikeParameterBinding getOrCreate(String parameterName, Type likeType, ParameterOrigin origin) { + public boolean isBound(BindingIdentifier identifier) { + return !getBindings(identifier).isEmpty(); + } - List likeParameterBindings = likeBindings.computeIfAbsent(parameterName, - s -> new ArrayList<>()); - LikeParameterBinding reuse = null; + BindingIdentifier register(BindingIdentifier identifier, ParameterOrigin origin, + Function bindingFactory) { - // unique parameters only required for literals as expressions create unique parameter names - if (origin.isMethodArgument()) { - for (LikeParameterBinding likeParameterBinding : likeParameterBindings) { + Assert.isInstanceOf(MethodInvocationArgument.class, origin); - if (likeParameterBinding.getType() == likeType) { - reuse = likeParameterBinding; - break; - } - } + BindingIdentifier methodArgument = ((MethodInvocationArgument) origin).identifier(); + List bindingsForOrigin = getBindings(methodArgument); + + if (!isBound(identifier)) { + + ParameterBinding binding = bindingFactory.apply(identifier); + registration.accept(binding); + bindingsForOrigin.add(binding); + return binding.getIdentifier(); } - if (reuse != null) { - return reuse; + ParameterBinding binding = bindingFactory.apply(identifier); + + for (ParameterBinding existing : bindingsForOrigin) { + + if (existing.isCompatibleWith(binding)) { + return existing.getIdentifier(); + } } - if (!likeParameterBindings.isEmpty()) { - parameterName = parameterName + "_" + likeParameterBindings.size(); + BindingIdentifier syntheticIdentifier; + if (identifier.hasName() && methodArgument.hasName()) { + + int index = 0; + String newName = methodArgument.getName(); + while (existsBoundParameter(newName)) { + index++; + newName = methodArgument.getName() + "_" + index; + } + syntheticIdentifier = BindingIdentifier.of(newName); + } else { + syntheticIdentifier = BindingIdentifier.of(++syntheticParameterIndex); } - LikeParameterBinding binding = new LikeParameterBinding(BindingIdentifier.of(parameterName), origin, likeType); - likeParameterBindings.add(binding); + ParameterBinding newBinding = bindingFactory.apply(syntheticIdentifier); + registration.accept(newBinding); + bindingsForOrigin.add(newBinding); + return newBinding.getIdentifier(); + } + + private boolean existsBoundParameter(String key) { + return methodArgumentToLikeBindings.values().stream().flatMap(Collection::stream) + .anyMatch(it -> key.equals(it.getName())); + } + + private List getBindings(BindingIdentifier identifier) { + return methodArgumentToLikeBindings.computeIfAbsent(identifier, s -> new ArrayList<>()); + } - return binding; + public void register(ParameterBinding parameterBinding) { + registration.accept(parameterBinding); } } } 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 04577804a6..9264185849 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 @@ -978,6 +978,9 @@ void executesManualQueryWithNamedLikeExpressionCorrectly() { assertThat(repository.findByFirstnameLikeNamed("Da")).containsOnly(thirdUser); assertThat(repository.findByFirstnameLikeNamed("in")).containsOnly(fourthUser); + + assertThat(repository.findByFirstnameLikePositional("Da")).containsOnly(thirdUser); + assertThat(repository.findByFirstnameLikePositional("in")).containsOnly(fourthUser); } @Test // DATAJPA-231 diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index 2678f9e2e6..e7210d3cbc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -18,6 +18,8 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,6 +27,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import org.springframework.data.jpa.repository.query.ParameterBinding.LikeParameterBinding; +import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.expression.spel.standard.SpelExpressionParser; /** @@ -101,6 +105,7 @@ void shouldDetectComplexNativeQueriesWithSpelAsNonNative() { + "AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", metadata, SPEL_PARSER, true); + System.out.println(query.getQueryString()); assertThat(query.isNativeQuery()).isFalse(); } @@ -119,4 +124,55 @@ void shouldDetectSimpleNativeQueriesWithoutSpelAsNative() { assertThat(query.isNativeQuery()).isTrue(); } + + @Test // GH-3041 + void namedExpressionsShouldCreateLikeBindings() { + + StringQuery query = new ExpressionBasedStringQuery( + "select u from User u where u.firstname like %:#{foo} or u.firstname like :#{foo}%", metadata, SPEL_PARSER, + false); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()).isEqualTo( + "select u from User u where u.firstname like :__$synthetic$__1 or u.firstname like :__$synthetic$__2"); + + List bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(2); + + LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); + assertThat(binding).isNotNull(); + assertThat(binding.getName()).isEqualTo("__$synthetic$__1"); + assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); + + binding = (LikeParameterBinding) bindings.get(1); + assertThat(binding).isNotNull(); + assertThat(binding.getName()).isEqualTo("__$synthetic$__2"); + assertThat(binding.getType()).isEqualTo(Type.STARTING_WITH); + } + + @Test // GH-3041 + void indexedExpressionsShouldCreateLikeBindings() { + + StringQuery query = new ExpressionBasedStringQuery( + "select u from User u where u.firstname like %?#{foo} or u.firstname like ?#{foo}%", metadata, SPEL_PARSER, + false); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()) + .isEqualTo("select u from User u where u.firstname like ?1 or u.firstname like ?2"); + + List bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(2); + + LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); + assertThat(binding).isNotNull(); + assertThat(binding.getPosition()).isEqualTo(1); + assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); + + binding = (LikeParameterBinding) bindings.get(1); + assertThat(binding).isNotNull(); + assertThat(binding.getPosition()).isEqualTo(2); + assertThat(binding.getType()).isEqualTo(Type.STARTING_WITH); + } + } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 0e7546e0b2..e959f8b46e 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -22,9 +22,12 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import org.springframework.data.jpa.repository.query.ParameterBinding.BindingIdentifier; import org.springframework.data.jpa.repository.query.ParameterBinding.Expression; import org.springframework.data.jpa.repository.query.ParameterBinding.InParameterBinding; import org.springframework.data.jpa.repository.query.ParameterBinding.LikeParameterBinding; +import org.springframework.data.jpa.repository.query.ParameterBinding.MethodInvocationArgument; +import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterOrigin; import org.springframework.data.repository.query.parser.Part.Type; /** @@ -82,7 +85,33 @@ void detectsPositionalLikeBindings() { assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); } - @Test // DATAJPA-292 + @Test // DATAJPA-292, GH-3041 + void detectsAnonymousLikeBindings() { + + StringQuery query = new StringQuery( + "select u from User u where u.firstname like %?% or u.lastname like %? or u.lastname=?", true); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()) + .isEqualTo("select u from User u where u.firstname like ? or u.lastname like ? or u.lastname=?"); + + List bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(3); + + LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); + assertThat(binding).isNotNull(); + assertThat(binding.getOrigin()).isEqualTo(ParameterOrigin.ofParameter(1)); + assertThat(binding.getRequiredPosition()).isEqualTo(1); + assertThat(binding.getType()).isEqualTo(Type.CONTAINING); + + binding = (LikeParameterBinding) bindings.get(1); + assertThat(binding).isNotNull(); + assertThat(binding.getOrigin()).isEqualTo(ParameterOrigin.ofParameter(2)); + assertThat(binding.getRequiredPosition()).isEqualTo(2); + assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); + } + + @Test // DATAJPA-292, GH-3041 void detectsNamedLikeBindings() { StringQuery query = new StringQuery("select u from User u where u.firstname like %:firstname", true); @@ -99,18 +128,20 @@ void detectsNamedLikeBindings() { assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); } - @Test // DATAJPA-292 + @Test // GH-3041 void rewritesNamedLikeToUniqueParametersIfNecessary() { StringQuery query = new StringQuery( - "select u from User u where u.firstname like %:firstname or u.firstname like :firstname%", true); + "select u from User u where u.firstname like %:firstname or u.firstname like :firstname% or u.firstname = :firstname", + true); assertThat(query.hasParameterBindings()).isTrue(); assertThat(query.getQueryString()) - .isEqualTo("select u from User u where u.firstname like :firstname or u.firstname like :firstname_1"); + .isEqualTo( + "select u from User u where u.firstname like :firstname or u.firstname like :firstname_1 or u.firstname = :firstname_2"); List bindings = query.getParameterBindings(); - assertThat(bindings).hasSize(2); + assertThat(bindings).hasSize(3); LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); assertThat(binding).isNotNull(); @@ -123,8 +154,22 @@ void rewritesNamedLikeToUniqueParametersIfNecessary() { assertThat(binding.getType()).isEqualTo(Type.STARTING_WITH); } - @Test // DATAJPA-292 - void reusesLikeBindingsWherePossible() { + @Test // GH-3041 + void rewritesPositionalLikeToUniqueParametersIfNecessary() { + + StringQuery query = new StringQuery( + "select u from User u where u.firstname like %?1 or u.firstname like ?1% or u.firstname = ?1", true); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()) + .isEqualTo("select u from User u where u.firstname like ?1 or u.firstname like ?2 or u.firstname = ?3"); + + List bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(3); + } + + @Test // GH-3041 + void reusesNamedLikeBindingsWherePossible() { StringQuery query = new StringQuery( "select u from User u where u.firstname like %:firstname or u.firstname like %:firstname% or u.firstname like %:firstname% or u.firstname like %:firstname", @@ -134,18 +179,51 @@ void reusesLikeBindingsWherePossible() { assertThat(query.getQueryString()).isEqualTo( "select u from User u where u.firstname like :firstname or u.firstname like :firstname_1 or u.firstname like :firstname_1 or u.firstname like :firstname"); - List bindings = query.getParameterBindings(); - assertThat(bindings).hasSize(2); - LikeParameterBinding binding = (LikeParameterBinding) bindings.get(0); - assertThat(binding).isNotNull(); - assertThat(binding.getName()).isEqualTo("firstname"); - assertThat(binding.getType()).isEqualTo(Type.ENDING_WITH); + query = new StringQuery("select u from User u where u.firstname like %:firstname or u.firstname =:firstname", true); - binding = (LikeParameterBinding) bindings.get(1); - assertThat(binding).isNotNull(); - assertThat(binding.getName()).isEqualTo("firstname_1"); - assertThat(binding.getType()).isEqualTo(Type.CONTAINING); + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()) + .isEqualTo("select u from User u where u.firstname like :firstname or u.firstname =:firstname_1"); + } + + @Test // GH-3041 + void reusesPositionalLikeBindingsWherePossible() { + + StringQuery query = new StringQuery( + "select u from User u where u.firstname like %?1 or u.firstname like %?1% or u.firstname like %?1% or u.firstname like %?1", + false); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()).isEqualTo( + "select u from User u where u.firstname like ?1 or u.firstname like ?2 or u.firstname like ?2 or u.firstname like ?1"); + + query = new StringQuery("select u from User u where u.firstname like %?1 or u.firstname =?1", false); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()).isEqualTo("select u from User u where u.firstname like ?1 or u.firstname =?2"); + } + + @Test // GH-3041 + void shouldRewritePositionalBindingsWithParameterReuse() { + + StringQuery query = new StringQuery( + "select u from User u where u.firstname like ?2 or u.firstname like %?2% or u.firstname like %?1% or u.firstname like %?1 OR u.firstname like ?1", + false); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()).isEqualTo( + "select u from User u where u.firstname like ?2 or u.firstname like ?3 or u.firstname like ?1 or u.firstname like ?4 OR u.firstname like ?5"); + + List bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(5); + + assertThat(bindings).extracting(ParameterBinding::getRequiredPosition).containsOnly(1, 2, 3, 4, 5); + assertThat(bindings).extracting(ParameterBinding::getOrigin) // + .map(MethodInvocationArgument.class::cast) // + .map(MethodInvocationArgument::identifier) // + .map(BindingIdentifier::getPosition) // + .containsOnly(1, 2); } @Test // DATAJPA-461 @@ -220,12 +298,6 @@ void handlesMultipleNamedLikeBindingsCorrectly() { new StringQuery("select u from User u where u.firstname like %:firstname or foo like :bar", true); } - @Test // DATAJPA-292, DATAJPA-362 - void rejectsDifferentBindingsForRepeatedParameter() { - assertThatIllegalArgumentException().isThrownBy( - () -> new StringQuery("select u from User u where u.firstname like %?1 and u.lastname like ?1%", true)); - } - @Test // DATAJPA-461 void treatsGreaterThanBindingAsSimpleBinding() { @@ -309,12 +381,6 @@ void detectsInBindingWithSpecialCharactersAndWordCharactersMixedInParentheses() assertNamedBinding(InParameterBinding.class, "ab1babc생일233", bindings.get(0)); } - @Test // DATAJPA-362 - void rejectsDifferentBindingsForRepeatedParameter2() { - assertThatIllegalArgumentException().isThrownBy( - () -> new StringQuery("select u from User u where u.firstname like ?1 and u.lastname like %?1", true)); - } - @Test // DATAJPA-712 void shouldReplaceAllNamedExpressionParametersWithInClause() { @@ -503,6 +569,8 @@ void makesUsageOfJdbcStyleParameterAvailable() { assertThat(new StringQuery("from Something something where something = ?", false).usesJdbcStyleParameters()) .isTrue(); + assertThat(new StringQuery("from Something something where something =?", false).usesJdbcStyleParameters()) + .isTrue(); List testQueries = Arrays.asList( // "from Something something where something = ?1", // @@ -514,6 +582,7 @@ void makesUsageOfJdbcStyleParameterAvailable() { assertThat(new StringQuery(testQuery, false) // .usesJdbcStyleParameters()) // + .describedAs(testQuery) .describedAs(testQuery) // .isFalse(); } 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 edbcd9150d..c9a342538f 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 @@ -169,6 +169,10 @@ Window findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(S @Query("select u from User u where u.firstname like :firstname% or u.firstname like %:firstname") List findByFirstnameLikeNamed(@Param("firstname") String firstname); + // DATAJPA-292, GH-3041 + @Query("select u from User u where u.firstname like ?1% or u.firstname like %?1") + List findByFirstnameLikePositional(String firstname); + /** * Manipulating query to set all {@link User}'s names to the given one. * From fa5ad90ff7d091af1eba995e05872cd11e0164b0 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 21 Jul 2023 13:53:08 -0500 Subject: [PATCH 440/821] Polishing. See #3041 --- .../data/jpa/provider/PersistenceProvider.java | 3 +-- .../data/jpa/repository/query/ParameterBinder.java | 1 + .../query/ExpressionBasedStringQueryUnitTests.java | 1 - .../query/QueryParameterSetterFactoryUnitTests.java | 6 ++++-- .../jpa/repository/query/StringQueryUnitTests.java | 10 +++++++--- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index ea378384eb..fd9eec9e3a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -196,8 +196,7 @@ public String getCommentHintKey() { Class type; try { - type = ClassUtils.forName("org.hibernate.query.TypedParameterValue", - PersistenceProvider.class.getClassLoader()); + type = ClassUtils.forName("org.hibernate.query.TypedParameterValue", PersistenceProvider.class.getClassLoader()); } catch (ClassNotFoundException e) { type = null; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 4c30d2fd46..892c7a1f30 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -72,6 +72,7 @@ public ParameterBinder(JpaParameters parameters, Iterable public T bind(T jpaQuery, QueryParameterSetter.QueryMetadata metadata, JpaParametersParameterAccessor accessor) { + bind(metadata.withQuery(jpaQuery), accessor, ErrorHandling.STRICT); return jpaQuery; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index e7210d3cbc..0a02266357 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -105,7 +105,6 @@ void shouldDetectComplexNativeQueriesWithSpelAsNonNative() { + "AND (n.updatedAt >= ?#{#networkRequest.updatedTime.startDateTime}) AND (n.updatedAt <=?#{#networkRequest.updatedTime.endDateTime})", metadata, SPEL_PARSER, true); - System.out.println(query.getQueryString()); assertThat(query.isNativeQuery()).isFalse(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java index ca31987720..771ed4e7cc 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactoryUnitTests.java @@ -62,7 +62,8 @@ void exceptionWhenQueryContainNamedParametersAndMethodParametersAreNotNamed() { when(binding.getOrigin()).thenReturn(ParameterOrigin.ofParameter("NamedParameter", 1)); assertThatExceptionOfType(IllegalStateException.class) // - .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("from Employee e where e.name = :NamedParameter", false))) // + .isThrownBy(() -> setterFactory.create(binding, + DeclaredQuery.of("from Employee e where e.name = :NamedParameter", false))) // .withMessageContaining("Java 8") // .withMessageContaining("@Param") // .withMessageContaining("-parameters"); @@ -80,7 +81,8 @@ void exceptionWhenCriteriaQueryContainsInsufficientAmountOfParameters() { when(binding.getOrigin()).thenReturn(ParameterOrigin.ofParameter(null, 1)); assertThatExceptionOfType(IllegalArgumentException.class) // - .isThrownBy(() -> setterFactory.create(binding, DeclaredQuery.of("from Employee e where e.name = :NamedParameter", false))) // + .isThrownBy(() -> setterFactory.create(binding, + DeclaredQuery.of("from Employee e where e.name = :NamedParameter", false))) // .withMessage("At least 1 parameter(s) provided but only 0 parameter(s) present in query"); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index e959f8b46e..8c95b38bfd 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -136,7 +136,7 @@ void rewritesNamedLikeToUniqueParametersIfNecessary() { true); assertThat(query.hasParameterBindings()).isTrue(); - assertThat(query.getQueryString()) + assertThat(query.getQueryString()) // .isEqualTo( "select u from User u where u.firstname like :firstname or u.firstname like :firstname_1 or u.firstname = :firstname_2"); @@ -152,6 +152,11 @@ void rewritesNamedLikeToUniqueParametersIfNecessary() { assertThat(binding).isNotNull(); assertThat(binding.getName()).isEqualTo("firstname_1"); assertThat(binding.getType()).isEqualTo(Type.STARTING_WITH); + + ParameterBinding parameterBinding = bindings.get(2); + assertThat(parameterBinding).isNotNull(); + assertThat(parameterBinding.getName()).isEqualTo("firstname_2"); + assertThat(((MethodInvocationArgument) parameterBinding.getOrigin()).identifier().getName()).isEqualTo("firstname"); } @Test // GH-3041 @@ -179,7 +184,6 @@ void reusesNamedLikeBindingsWherePossible() { assertThat(query.getQueryString()).isEqualTo( "select u from User u where u.firstname like :firstname or u.firstname like :firstname_1 or u.firstname like :firstname_1 or u.firstname like :firstname"); - query = new StringQuery("select u from User u where u.firstname like %:firstname or u.firstname =:firstname", true); assertThat(query.hasParameterBindings()).isTrue(); @@ -582,7 +586,7 @@ void makesUsageOfJdbcStyleParameterAvailable() { assertThat(new StringQuery(testQuery, false) // .usesJdbcStyleParameters()) // - .describedAs(testQuery) + .describedAs(testQuery) // .describedAs(testQuery) // .isFalse(); } From 2b4024a03940d86199f5fd183366c8f1735238b4 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 25 Jul 2023 15:30:45 -0500 Subject: [PATCH 441/821] Upgrade to EclipseLink 4.0.2. See #2900 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4ddfbc8ec6..0bc8bc06d6 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ eclipselink-next - 4.0.0 + 4.0.2 From 90807ffe235fa79b7868bfcd3d57549790ad847b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 26 Jul 2023 15:13:22 -0500 Subject: [PATCH 442/821] Make SpEL behavior consistent. See #1597 --- .../query/ExpressionBasedStringQuery.java | 3 ++- .../ExpressionBasedStringQueryUnitTests.java | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java index 837e22da2d..3bee9c2d48 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQuery.java @@ -37,6 +37,7 @@ * @author Tom Hombergs * @author Michael J. Simons * @author Diego Krupitza + * @author Greg Turnquist */ class ExpressionBasedStringQuery extends StringQuery { @@ -48,7 +49,7 @@ class ExpressionBasedStringQuery extends StringQuery { private static final String ENTITY_NAME = "entityName"; private static final String ENTITY_NAME_VARIABLE = "#" + ENTITY_NAME; - private static final String ENTITY_NAME_VARIABLE_EXPRESSION = "#{" + ENTITY_NAME_VARIABLE + "}"; + private static final String ENTITY_NAME_VARIABLE_EXPRESSION = "#{" + ENTITY_NAME_VARIABLE; /** * Creates a new {@link ExpressionBasedStringQuery} for the given query and {@link EntityMetadata}. diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java index 0a02266357..af1928d506 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ExpressionBasedStringQueryUnitTests.java @@ -40,6 +40,7 @@ * @author Mark Paluch * @author Michael J. Simons * @author Diego Krupitza + * @author Greg Turnquist */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -174,4 +175,30 @@ void indexedExpressionsShouldCreateLikeBindings() { assertThat(binding.getType()).isEqualTo(Type.STARTING_WITH); } + @Test + public void doesTemplatingWhenEntityNameSpelIsPresent() { + + StringQuery query = new ExpressionBasedStringQuery("select #{#entityName + 'Hallo'} from #{#entityName} u", + metadata, SPEL_PARSER, false); + + assertThat(query.getQueryString()).isEqualTo("select UserHallo from User u"); + } + + @Test + public void doesNoTemplatingWhenEntityNameSpelIsNotPresent() { + + StringQuery query = new ExpressionBasedStringQuery("select #{#entityName + 'Hallo'} from User u", metadata, + SPEL_PARSER, false); + + assertThat(query.getQueryString()).isEqualTo("select UserHallo from User u"); + } + + @Test + public void doesTemplatingWhenEntityNameSpelIsPresentForBindParameter() { + + StringQuery query = new ExpressionBasedStringQuery("select u from #{#entityName} u where name = :#{#something}", + metadata, SPEL_PARSER, false); + + assertThat(query.getQueryString()).isEqualTo("select u from User u where name = :__$synthetic$__1"); + } } From 972b26c6f23c7eff5043323ae7578f7690176942 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 26 Jul 2023 16:50:46 -0500 Subject: [PATCH 443/821] Verify that mismatched return types are caught. Verify that when a return type doesn't match the query's type, an exception is thrown. See #1184 --- .../QueryWithNullLikeIntegrationTests.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java index 556f88836d..898975ad8d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java @@ -31,6 +31,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.FilterType; +import org.springframework.core.convert.ConversionFailedException; import org.springframework.data.jpa.domain.sample.EmployeeWithName; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -263,12 +264,30 @@ void derivedQueryLikeWithEmptyStringMatch() { "Bilbo Baggins"); } + @Test // GH-1184 + void mismatchedReturnTypeShouldCauseException() { + assertThatExceptionOfType(ConversionFailedException.class) + .isThrownBy(() -> repository.customQueryWithMismatchedReturnType()); + } + + @Test // GH-1184 + void alignedReturnTypeShouldWork() { + assertThat(repository.customQueryWithAlignedReturnType()).containsExactly(new Object[][] { + { "Frodo Baggins", "Frodo Baggins with suffix" }, { "Bilbo Baggins", "Bilbo Baggins with suffix" } }); + } + @Transactional public interface EmployeeWithNullLikeRepository extends JpaRepository { @Query("select e from EmployeeWithName e where e.name like %:partialName%") List customQueryWithNullableParam(@Nullable @Param("partialName") String partialName); + @Query("select e.name, concat(e.name, ' with suffix') from EmployeeWithName e") + List customQueryWithMismatchedReturnType(); + + @Query("select e.name, concat(e.name, ' with suffix') from EmployeeWithName e") + List customQueryWithAlignedReturnType(); + @Query(value = "select * from EmployeeWithName as e where e.name like %:partialName%", nativeQuery = true) List customQueryWithNullableParamInNative(@Nullable @Param("partialName") String partialName); From 10c26d24ef37bbbfec760d8a173e2eeada39ef9b Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 2 Aug 2023 13:41:19 -0500 Subject: [PATCH 444/821] FROM and DATE should be usable as parameters in JPQL queries. See #3092 --- .../org/springframework/data/jpa/repository/query/Jpql.g4 | 2 ++ .../data/jpa/repository/query/JpqlQueryRendererTests.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index e17a919f4a..4a23f4c14b 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -597,6 +597,8 @@ trim_character identification_variable : IDENTIFICATION_VARIABLE | f=(COUNT + | DATE + | FROM | INNER | KEY | LEFT diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index 368b1379ab..bc0938c50c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -953,4 +953,9 @@ void typeShouldBeAValidParameter() { void alternateNotEqualsOperatorShouldWork() { assertQuery("select e from Employee e where e.firstName != :name"); } + + @Test // GH-3092 + void dateAndFromShouldBeValidNames() { + assertQuery("SELECT e FROM Entity e WHERE e.embeddedId.date BETWEEN :from AND :to"); + } } From 7eeca108f43f173bed359b75a51d647f6d3f2d3f Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 2 Aug 2023 14:20:44 -0500 Subject: [PATCH 445/821] TIME should be valid parameter name for JPQL queries. See #3093 --- .../springframework/data/jpa/repository/query/Jpql.g4 | 1 + .../data/jpa/repository/query/JpqlQueryRenderer.java | 2 ++ .../jpa/repository/query/JpqlQueryRendererTests.java | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 4a23f4c14b..5c63a543b1 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -606,6 +606,7 @@ identification_variable | OUTER | FLOOR | SIGN + | TIME | TYPE | VALUE) ; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java index b5a41fe998..7ff5a6f393 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -544,9 +544,11 @@ public List visitUpdate_clause(JpqlParser.Update_clauseCon ctx.update_item().forEach(updateItemContext -> { tokens.addAll(visit(updateItemContext)); + NOSPACE(tokens); tokens.add(TOKEN_COMMA); }); CLIP(tokens); + SPACE(tokens); return tokens; } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index bc0938c50c..c30dcbf71f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -958,4 +958,13 @@ void alternateNotEqualsOperatorShouldWork() { void dateAndFromShouldBeValidNames() { assertQuery("SELECT e FROM Entity e WHERE e.embeddedId.date BETWEEN :from AND :to"); } + + @Test // GH-3092 + void timeShouldBeAValidParameterName() { + assertQuery(""" + UPDATE Lock L + SET L.isLocked = TRUE, L.forceUnlockTime = :forceUnlockTime + WHERE L.isLocked = FALSE OR L.forceUnlockTime < :time + """); + } } From 7e7b050cf1d244d428bb5e8b6c32229ee0c939d4 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 4 Aug 2023 10:26:14 -0500 Subject: [PATCH 446/821] HQL function names should support schema-based prefixes. See #3099 --- .../data/jpa/repository/query/Hql.g4 | 2 +- .../repository/query/HqlQueryRenderer.java | 12 +++++++- .../query/HqlQueryRendererTests.java | 28 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 7516949148..36205292b6 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -686,7 +686,7 @@ character ; functionName - : reservedWord + : reservedWord ('.' reservedWord)* ; reservedWord diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index e705664a7f..3f5aa00df6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -2503,7 +2503,17 @@ public List visitCharacter(HqlParser.CharacterContext ctx) @Override public List visitFunctionName(HqlParser.FunctionNameContext ctx) { - return visit(ctx.reservedWord()); + + List tokens = new ArrayList<>(); + + ctx.reservedWord().forEach(reservedWordContext -> { + tokens.addAll(visit(reservedWordContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + CLIP(tokens); + + return tokens; } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 48592afbf8..d95edc11e4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1556,4 +1556,32 @@ void typeShouldBeAValidParameter() { assertQuery("select e from Employee e where e.type = :_type"); assertQuery("select te from TestEntity te where te.type = :type"); } + + @Test // GH-3099 + void functionNamesShouldSupportSchemaScoping() { + + assertQuery(""" + SELECT b + FROM MyEntity b + WHERE b.status = :status + AND utl_raw.cast_to_varchar2((nlssort(lower(b.name), 'nls_sort=binary_ai'))) LIKE lower(:name) + ORDER BY utl_raw.cast_to_varchar2((nlssort(lower(b.name), 'nls_sort=binary_ai'))) ASC + """); + + assertQuery(""" + select b + from Bairro b + where b.situacao = :situacao + and utl_raw.cast_to_varchar2((nlssort(lower(b.nome), 'nls_sort=binary_ai'))) like lower(:nome) + order by utl_raw.cast_to_varchar2((nlssort(lower(b.nome), 'nls_sort=binary_ai'))) ASC + """); + + assertQuery(""" + select b + from Bairro b + where b.situacao = :situacao + and CTM_UTLRAW_NLSSORT_LOWER(b.nome) like lower(:nome) + order by CTM_UTLRAW_NLSSORT_LOWER(b.nome) ASC + """); + } } From accc5c31687d47e8ad4a48b7e77cd518e144760c Mon Sep 17 00:00:00 2001 From: Sergey Rukin Date: Fri, 4 Aug 2023 22:04:44 +0500 Subject: [PATCH 447/821] Add 'since' headers to Specification javadocs. See #3102, #3089 --- .../org/springframework/data/jpa/domain/Specification.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java index a5b39ef017..bbbaa2e95e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/Specification.java @@ -37,6 +37,7 @@ * @author Mark Paluch * @author Jens Schauder * @author Daniel Shuy + * @author Sergey Rukin */ public interface Specification extends Serializable { @@ -109,6 +110,7 @@ default Specification or(@Nullable Specification other) { * @param specifications The {@link Specification}s to compose. Can contain {@code null}s. * @return The conjunction of the specifications * @see #and(Specification) + * @since 3.0 */ static Specification allOf(Iterable> specifications) { @@ -118,6 +120,7 @@ static Specification allOf(Iterable> specifications) { /** * @see #allOf(Iterable) + * @since 3.0 */ @SafeVarargs static Specification allOf(Specification... specifications) { @@ -130,6 +133,7 @@ static Specification allOf(Specification... specifications) { * @param specifications The {@link Specification}s to compose. Can contain {@code null}s. * @return The disjunction of the specifications * @see #or(Specification) + * @since 3.0 */ static Specification anyOf(Iterable> specifications) { @@ -139,6 +143,7 @@ static Specification anyOf(Iterable> specifications) { /** * @see #anyOf(Iterable) + * @since 3.0 */ @SafeVarargs static Specification anyOf(Specification... specifications) { From 553467fd6494051c9bb953321a16bd89f2755422 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Wed, 9 Aug 2023 16:35:27 -0500 Subject: [PATCH 448/821] Test against Hibernate 6.3 on CI. See #3106 --- Jenkinsfile | 22 ++++++++++++++++++++-- pom.xml | 6 ++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 415311f002..2ac291af20 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -54,7 +54,7 @@ pipeline { } parallel { - stage("test: baseline (hibernate 6.1)") { + stage("test: java.next (hibernate 6.1)") { agent { label 'data' } @@ -72,7 +72,25 @@ pipeline { } } } - stage("test: baseline (next)") { + stage("test: baseline (hibernate 6.3)") { + agent { + label 'data' + } + options { timeout(time: 30, unit: 'MINUTES')} + environment { + ARTIFACTORY = credentials("${p['artifactory.credentials']}") + TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' + } + steps { + script { + docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { + sh 'PROFILE=all-dbs,hibernate-63 ci/test.sh' + sh "ci/clean.sh" + } + } + } + } + stage("test: java.next (next)") { agent { label 'data' } diff --git a/pom.xml b/pom.xml index 0bc8bc06d6..b709f1f03a 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,12 @@ 6.1.7.Final + + hibernate-63 + + 6.3.0.CR1 + + all-dbs From a82a8cadcc87e64c2998ed5dc72ef6073ac28ae3 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 10 Aug 2023 10:52:38 -0500 Subject: [PATCH 449/821] Polishing. See #3080 --- src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc index 5385c6c063..39be17e2d1 100644 --- a/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc +++ b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc @@ -776,6 +776,6 @@ public interface GroupRepository extends CrudRepository { [[projections]] == Projections -Spring Data JPA supports {spring-data-commons-docs-url}/repository-projects.html[Spring Data Commons Projections]. +Spring Data JPA supports {spring-data-commons-docs-url}/repository-projections.html[Spring Data Commons Projections]. -NOTE: It is important to note that {spring-data-commons-docs-url}/repository-projects.html#projections.dtos[Class-based projections] with JPQL is limited to *constructor expressions* in your JPQL expression, e.g. `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. (Note the usage of a FQDN for the DTO type!) This JPQL expression can be used in `@Query` annotations as well where you define any named queries. And it's important to point out that class-based projections do not work with native queries AT ALL. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] \ No newline at end of file +NOTE: It is important to note that {spring-data-commons-docs-url}/repository-projections.html#projections.dtos[Class-based projections] with JPQL is limited to *constructor expressions* in your JPQL expression, e.g. `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. (Note the usage of a FQDN for the DTO type!) This JPQL expression can be used in `@Query` annotations as well where you define any named queries. And it's important to point out that class-based projections do not work with native queries AT ALL. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] \ No newline at end of file From 231942955e1258e496bafb2df7c1f33304a560d7 Mon Sep 17 00:00:00 2001 From: Julia Lee <5765049+sxhinzvc@users.noreply.github.com> Date: Mon, 14 Aug 2023 08:53:19 -0400 Subject: [PATCH 450/821] Upgrade to Maven Wrapper 3.9.4. See #3114 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index ab824459af..7f90a55adb 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Mon Jul 03 09:49:39 CEST 2023 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip +#Mon Aug 14 08:53:19 EDT 2023 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.4/apache-maven-3.9.4-bin.zip From df6623158a4a100e48c4068b18103b4eb3cd27e4 Mon Sep 17 00:00:00 2001 From: Julia <5765049+sxhinzvc@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:19:00 -0400 Subject: [PATCH 451/821] Update CI properties. See #3071 --- ci/pipeline.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 8a33425f94..0359247e80 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,5 +1,5 @@ # Java versions -java.main.tag=17.0.7_7-jdk-focal +java.main.tag=17.0.8_7-jdk-focal java.next.tag=21-jdk-bullseye # Docker container images - standard @@ -7,12 +7,12 @@ docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/ecli docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/openjdk:${java.next.tag} # Supported versions of MongoDB -docker.mongodb.4.4.version=4.4.22 -docker.mongodb.5.0.version=5.0.18 -docker.mongodb.6.0.version=6.0.7 +docker.mongodb.4.4.version=4.4.23 +docker.mongodb.5.0.version=5.0.19 +docker.mongodb.6.0.version=6.0.8 # Supported versions of Redis -docker.redis.6.version=6.2.12 +docker.redis.6.version=6.2.13 # Supported versions of Cassandra docker.cassandra.3.version=3.11.15 From e48dd1cd09b43d4a8a518249cecc9a7d3cfe769d Mon Sep 17 00:00:00 2001 From: Julia Lee <5765049+sxhinzvc@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:49:38 -0400 Subject: [PATCH 452/821] Prepare 3.2 M2 (2023.1.0). See #3071 --- pom.xml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index b709f1f03a..a963a56b19 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-SNAPSHOT + 3.2.0-M2 @@ -35,7 +35,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-SNAPSHOT + 3.2.0-M2 0.10.3 org.hibernate @@ -308,16 +308,6 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - spring-milestone https://repo.spring.io/milestone From 851ad8b490290be5c8cf551f7870076764b6b121 Mon Sep 17 00:00:00 2001 From: Julia Lee <5765049+sxhinzvc@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:50:31 -0400 Subject: [PATCH 453/821] Release version 3.2 M2 (2023.1.0). See #3071 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index a963a56b19..3e077cb232 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M2 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index f239d6394b..7bd1087988 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-SNAPSHOT + 3.2.0-M2 org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M2 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 95ed749af3..a399b838b8 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M2 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index b21b03c313..55ec40b9f6 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-SNAPSHOT + 3.2.0-M2 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M2 ../pom.xml From f23115b6ebebc4c7ba92927352a7e1f869f9396a Mon Sep 17 00:00:00 2001 From: Julia Lee <5765049+sxhinzvc@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:59:35 -0400 Subject: [PATCH 454/821] Prepare next development iteration. See #3071 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 3e077cb232..a963a56b19 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M2 + 3.2.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 7bd1087988..f239d6394b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-M2 + 3.2.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.2.0-M2 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a399b838b8..95ed749af3 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M2 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 55ec40b9f6..b21b03c313 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-M2 + 3.2.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M2 + 3.2.0-SNAPSHOT ../pom.xml From d6bd5711b7f08de7efa10dfb10a2eb89e97b365e Mon Sep 17 00:00:00 2001 From: Julia Lee <5765049+sxhinzvc@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:59:39 -0400 Subject: [PATCH 455/821] After release cleanups. See #3071 --- pom.xml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a963a56b19..b709f1f03a 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-M2 + 3.2.0-SNAPSHOT @@ -35,7 +35,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-M2 + 3.2.0-SNAPSHOT 0.10.3 org.hibernate @@ -308,6 +308,16 @@ + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + spring-milestone https://repo.spring.io/milestone From 68f714917b3e9b6bcb0a079d4e8f12a451ea7bae Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 2 Aug 2023 11:04:13 -0500 Subject: [PATCH 456/821] Use io.spring.maven.antora plugins. - Switch to io.spring.maven.antora plugins - Move Antora to spring-data-jpa modulen. Placing in parent folder means that each module will also have the Antora plugin applied to it and the antora task will fail on those modules. See #3094 --- pom.xml | 98 +------------------ spring-data-jpa/pom.xml | 24 +++++ .../src}/main/antora/antora-playbook.yml | 4 +- .../src}/main/antora/antora.yml | 4 +- .../src}/main/antora/modules/ROOT/nav.adoc | 0 .../antora/modules/ROOT/pages/envers.adoc | 0 .../main/antora/modules/ROOT/pages/faq.adoc | 0 .../antora/modules/ROOT/pages/glossary.adoc | 0 .../main/antora/modules/ROOT/pages/index.adoc | 0 .../main/antora/modules/ROOT/pages/jpa.adoc | 0 .../modules/ROOT/pages/jpa/auditing.adoc | 0 .../ROOT/pages/jpa/entity-persistence.adoc | 0 .../modules/ROOT/pages/jpa/introduction.adoc | 0 .../pages/jpa/jpd-misc-cdi-integration.adoc | 0 .../modules/ROOT/pages/jpa/locking.adoc | 0 .../modules/ROOT/pages/jpa/misc-context.adoc | 0 .../jpa/misc-merging-persistence-units.adoc | 0 .../ROOT/pages/jpa/query-by-example.adoc | 0 .../modules/ROOT/pages/jpa/query-methods.adoc | 0 .../ROOT/pages/jpa/specifications.adoc | 0 .../ROOT/pages/jpa/stored-procedures.adoc | 0 .../modules/ROOT/pages/jpa/transactions.adoc | 0 .../resources/antora-resources/antora.yml | 3 +- 23 files changed, 31 insertions(+), 102 deletions(-) rename {src => spring-data-jpa/src}/main/antora/antora-playbook.yml (93%) rename {src => spring-data-jpa/src}/main/antora/antora.yml (50%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/nav.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/envers.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/faq.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/glossary.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/index.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/auditing.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/introduction.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/locking.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/misc-context.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/query-methods.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/specifications.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc (100%) rename {src => spring-data-jpa/src}/main/antora/modules/ROOT/pages/jpa/transactions.adoc (100%) rename {src/main => spring-data-jpa/src/main/antora}/resources/antora-resources/antora.yml (86%) diff --git a/pom.xml b/pom.xml index b709f1f03a..d63ed1c14b 100644 --- a/pom.xml +++ b/pom.xml @@ -42,16 +42,6 @@ reuseReports - - v18.12.1 - 8.19.2 - 3.2.0-alpha.2 - 1.0.0-alpha.1 - 1.0.0-alpha.3 - 1.0.0-beta.3 - 1.4.0 - 1.0.0-alpha.9 - @@ -117,93 +107,6 @@ 4.0.2 - - docs - - - - com.github.eirslett - frontend-maven-plugin - 1.12.1 - - - install-antora - - install-node-and-npm - - initialize - - ${node.version} - ${npm.version} - - - - npm install antora - - npm - - initialize - - install @antora/cli@${antora.version} @antora/site-generator-default@${antora.version} @antora/atlas-extension@${antora-atlas.version} @antora/collector-extension@${antora-collector.version} @asciidoctor/tabs@${asciidoctor-tabs.version} @springio/antora-extensions@${spring-antora-extensions.version} @springio/asciidoctor-extensions@${spring-asciidoctor-extensions.version} - - - - - - org.codehaus.mojo - exec-maven-plugin - 3.0.0 - - - antora - - exec - - compile - - - node/node - - node_modules/.bin/antora - src/main/antora/antora-playbook.yml - --to-dir=target/site - - ${project.parent.basedir} - - - - - - org.apache.maven.plugins - maven-clean-plugin - 3.1.0 - - - - node - false - - - node_modules - false - - - build - false - - - - - - - - src/main/resources - true - - - - @@ -304,6 +207,7 @@ + diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index b21b03c313..19bc4279e7 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -21,6 +21,7 @@ spring.data.jpa + 0.0.3 @@ -430,6 +431,29 @@ + + + + io.spring.maven.antora + antora-maven-plugin + ${io.spring.maven.antora-version} + true + + src/main/antora/antora-playbook.yml + + + + io.spring.maven.antora + antora-component-version-maven-plugin + ${io.spring.maven.antora-version} + + + + antora-component-version + + + + diff --git a/src/main/antora/antora-playbook.yml b/spring-data-jpa/src/main/antora/antora-playbook.yml similarity index 93% rename from src/main/antora/antora-playbook.yml rename to spring-data-jpa/src/main/antora/antora-playbook.yml index 643dd938eb..f234b092a3 100644 --- a/src/main/antora/antora-playbook.yml +++ b/spring-data-jpa/src/main/antora/antora-playbook.yml @@ -11,9 +11,9 @@ site: url: https://docs.spring.io/spring-data-jpa/reference/ content: sources: - - url: ./../../.. + - url: ./../../../.. branches: HEAD - start_path: src/main/antora + start_path: spring-data-jpa/src/main/antora worktrees: true asciidoc: attributes: diff --git a/src/main/antora/antora.yml b/spring-data-jpa/src/main/antora/antora.yml similarity index 50% rename from src/main/antora/antora.yml rename to spring-data-jpa/src/main/antora/antora.yml index edf3154af3..b728d86ef6 100644 --- a/src/main/antora/antora.yml +++ b/spring-data-jpa/src/main/antora/antora.yml @@ -6,7 +6,7 @@ nav: ext: collector: - run: - command: mvnw -Pdocs resources:resources + command: mvnw validate resources:resources -pl :spring-data-jpa -am local: true scan: - dir: target/classes/antora-resources + dir: spring-data-jpa/target/classes/antora-resources diff --git a/src/main/antora/modules/ROOT/nav.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/nav.adoc similarity index 100% rename from src/main/antora/modules/ROOT/nav.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/nav.adoc diff --git a/src/main/antora/modules/ROOT/pages/envers.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/envers.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/envers.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/envers.adoc diff --git a/src/main/antora/modules/ROOT/pages/faq.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/faq.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/faq.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/faq.adoc diff --git a/src/main/antora/modules/ROOT/pages/glossary.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/glossary.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/glossary.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/glossary.adoc diff --git a/src/main/antora/modules/ROOT/pages/index.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/index.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/index.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/index.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/auditing.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/introduction.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/locking.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/locking.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/locking.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/locking.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/specifications.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc b/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/jpa/transactions.adoc rename to spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc diff --git a/src/main/resources/antora-resources/antora.yml b/spring-data-jpa/src/main/antora/resources/antora-resources/antora.yml similarity index 86% rename from src/main/resources/antora-resources/antora.yml rename to spring-data-jpa/src/main/antora/resources/antora-resources/antora.yml index 059abf1920..b17d78e0d8 100644 --- a/src/main/resources/antora-resources/antora.yml +++ b/spring-data-jpa/src/main/antora/resources/antora-resources/antora.yml @@ -1,4 +1,5 @@ -version: ${project.version} +version: ${antora-component.version} +prerelease: ${antora-component.prerelease} asciidoc: attributes: From 016be28e79df4673ad128d136adebbd2c1630ab1 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 21 Aug 2023 14:33:56 +0200 Subject: [PATCH 457/821] Use Antora-configuration profiles. Move Antora content to src/main/antora Closes #3123 --- spring-data-jpa-distribution/pom.xml | 25 +++++++++++++++++++ spring-data-jpa/pom.xml | 24 ------------------ spring-data-jpa/src/main/antora/antora.yml | 12 --------- .../main/antora/antora-playbook.yml | 6 ++--- src/main/antora/antora.yml | 12 +++++++++ .../main/antora/modules/ROOT/nav.adoc | 0 .../antora/modules/ROOT/pages/envers.adoc | 0 .../main/antora/modules/ROOT/pages/faq.adoc | 0 .../antora/modules/ROOT/pages/glossary.adoc | 0 .../main/antora/modules/ROOT/pages/index.adoc | 0 .../main/antora/modules/ROOT/pages/jpa.adoc | 0 .../modules/ROOT/pages/jpa/auditing.adoc | 0 .../ROOT/pages/jpa/entity-persistence.adoc | 0 .../modules/ROOT/pages/jpa/introduction.adoc | 0 .../pages/jpa/jpd-misc-cdi-integration.adoc | 0 .../modules/ROOT/pages/jpa/locking.adoc | 0 .../modules/ROOT/pages/jpa/misc-context.adoc | 0 .../jpa/misc-merging-persistence-units.adoc | 0 .../ROOT/pages/jpa/query-by-example.adoc | 0 .../modules/ROOT/pages/jpa/query-methods.adoc | 0 .../ROOT/pages/jpa/specifications.adoc | 0 .../ROOT/pages/jpa/stored-procedures.adoc | 0 .../modules/ROOT/pages/jpa/transactions.adoc | 0 .../resources/antora-resources/antora.yml | 6 ++--- 24 files changed, 43 insertions(+), 42 deletions(-) delete mode 100644 spring-data-jpa/src/main/antora/antora.yml rename {spring-data-jpa/src => src}/main/antora/antora-playbook.yml (91%) create mode 100644 src/main/antora/antora.yml rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/nav.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/envers.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/faq.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/glossary.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/index.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/auditing.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/introduction.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/locking.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/misc-context.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/query-methods.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/specifications.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/modules/ROOT/pages/jpa/transactions.adoc (100%) rename {spring-data-jpa/src => src}/main/antora/resources/antora-resources/antora.yml (81%) diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 95ed749af3..453f49dea0 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -21,15 +21,40 @@ ${basedir}/.. DATAJPA + ${project.basedir}/../src/main/antora/antora-playbook.yml + + + ${project.basedir}/../src/main/antora/resources/antora-resources + true + + org.apache.maven.plugins maven-assembly-plugin + + + io.spring.maven.antora + antora-maven-plugin + + + + + antora + + + + + + + + + diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 19bc4279e7..b21b03c313 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -21,7 +21,6 @@ spring.data.jpa - 0.0.3 @@ -431,29 +430,6 @@ - - - - io.spring.maven.antora - antora-maven-plugin - ${io.spring.maven.antora-version} - true - - src/main/antora/antora-playbook.yml - - - - io.spring.maven.antora - antora-component-version-maven-plugin - ${io.spring.maven.antora-version} - - - - antora-component-version - - - - diff --git a/spring-data-jpa/src/main/antora/antora.yml b/spring-data-jpa/src/main/antora/antora.yml deleted file mode 100644 index b728d86ef6..0000000000 --- a/spring-data-jpa/src/main/antora/antora.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: data-jpa -version: true -title: Spring Data JPA -nav: - - modules/ROOT/nav.adoc -ext: - collector: - - run: - command: mvnw validate resources:resources -pl :spring-data-jpa -am - local: true - scan: - dir: spring-data-jpa/target/classes/antora-resources diff --git a/spring-data-jpa/src/main/antora/antora-playbook.yml b/src/main/antora/antora-playbook.yml similarity index 91% rename from spring-data-jpa/src/main/antora/antora-playbook.yml rename to src/main/antora/antora-playbook.yml index f234b092a3..12ac5adfb4 100644 --- a/spring-data-jpa/src/main/antora/antora-playbook.yml +++ b/src/main/antora/antora-playbook.yml @@ -11,9 +11,9 @@ site: url: https://docs.spring.io/spring-data-jpa/reference/ content: sources: - - url: ./../../../.. + - url: ./../../.. branches: HEAD - start_path: spring-data-jpa/src/main/antora + start_path: src/main/antora worktrees: true asciidoc: attributes: @@ -34,4 +34,4 @@ runtime: ui: bundle: url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.3.3/ui-bundle.zip - snapshot: true \ No newline at end of file + snapshot: true diff --git a/src/main/antora/antora.yml b/src/main/antora/antora.yml new file mode 100644 index 0000000000..68f27da5f3 --- /dev/null +++ b/src/main/antora/antora.yml @@ -0,0 +1,12 @@ +name: data-jpa +version: true +title: Spring Data JPA +nav: + - modules/ROOT/nav.adoc +ext: + collector: + - run: + command: ./mvnw validate process-resources -pl :spring-data-jpa-distribution -am -Pantora-process-resources + local: true + scan: + dir: spring-data-jpa-distribution/target/classes/ diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/nav.adoc b/src/main/antora/modules/ROOT/nav.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/nav.adoc rename to src/main/antora/modules/ROOT/nav.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/envers.adoc b/src/main/antora/modules/ROOT/pages/envers.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/envers.adoc rename to src/main/antora/modules/ROOT/pages/envers.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/faq.adoc b/src/main/antora/modules/ROOT/pages/faq.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/faq.adoc rename to src/main/antora/modules/ROOT/pages/faq.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/glossary.adoc b/src/main/antora/modules/ROOT/pages/glossary.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/glossary.adoc rename to src/main/antora/modules/ROOT/pages/glossary.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/index.adoc b/src/main/antora/modules/ROOT/pages/index.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/index.adoc rename to src/main/antora/modules/ROOT/pages/index.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa.adoc b/src/main/antora/modules/ROOT/pages/jpa.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa.adoc rename to src/main/antora/modules/ROOT/pages/jpa.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc b/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc rename to src/main/antora/modules/ROOT/pages/jpa/auditing.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc b/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc rename to src/main/antora/modules/ROOT/pages/jpa/entity-persistence.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc b/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc rename to src/main/antora/modules/ROOT/pages/jpa/introduction.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc b/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc rename to src/main/antora/modules/ROOT/pages/jpa/jpd-misc-cdi-integration.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/locking.adoc b/src/main/antora/modules/ROOT/pages/jpa/locking.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/locking.adoc rename to src/main/antora/modules/ROOT/pages/jpa/locking.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc b/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc rename to src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc b/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc rename to src/main/antora/modules/ROOT/pages/jpa/misc-merging-persistence-units.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc b/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc rename to src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc rename to src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc b/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/specifications.adoc rename to src/main/antora/modules/ROOT/pages/jpa/specifications.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc b/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc rename to src/main/antora/modules/ROOT/pages/jpa/stored-procedures.adoc diff --git a/spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc b/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc similarity index 100% rename from spring-data-jpa/src/main/antora/modules/ROOT/pages/jpa/transactions.adoc rename to src/main/antora/modules/ROOT/pages/jpa/transactions.adoc diff --git a/spring-data-jpa/src/main/antora/resources/antora-resources/antora.yml b/src/main/antora/resources/antora-resources/antora.yml similarity index 81% rename from spring-data-jpa/src/main/antora/resources/antora-resources/antora.yml rename to src/main/antora/resources/antora-resources/antora.yml index b17d78e0d8..f42c5ef7af 100644 --- a/spring-data-jpa/src/main/antora/resources/antora-resources/antora.yml +++ b/src/main/antora/resources/antora-resources/antora.yml @@ -4,10 +4,10 @@ prerelease: ${antora-component.prerelease} asciidoc: attributes: version: ${project.version} - springversionshort: 6.1 + springversionshort: ${spring.short} springversion: ${spring} attribute-missing: 'warn' spring-data-commons-docs-url: https://docs.spring.io/spring-data-commons/reference - spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/data-commons/docs/current/api/ + spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/commons/docs/${springdata.commons}/api/ springdocsurl: https://docs.spring.io/spring-framework/reference/{springversionshort} - springjavadocurl: https://docs.spring.io/spring-framework/docs/${spring}/javadoc-api \ No newline at end of file + springjavadocurl: https://docs.spring.io/spring-framework/docs/${spring}/javadoc-api From b47a340c0d88660a69994e699c30cd61b6c5529c Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Aug 2023 14:52:18 +0200 Subject: [PATCH 458/821] Restructure content to include partials from Commons. See #3080 --- spring-data-jpa-distribution/pom.xml | 26 +-- src/main/antora/antora-playbook.yml | 5 + src/main/antora/modules/ROOT/nav.adoc | 27 ++- .../ROOT/pages/{jpa => }/auditing.adoc | 6 +- .../modules/ROOT/pages/commons/upgrade.adoc | 1 + .../antora/modules/ROOT/pages/envers.adoc | 205 +----------------- .../ROOT/pages/envers/configuration.adoc | 95 ++++++++ .../ROOT/pages/envers/introduction.adoc | 14 ++ .../modules/ROOT/pages/envers/usage.adoc | 93 ++++++++ src/main/antora/modules/ROOT/pages/index.adoc | 23 +- src/main/antora/modules/ROOT/pages/jpa.adoc | 4 +- .../{introduction.adoc => configuration.adoc} | 12 +- .../modules/ROOT/pages/{ => jpa}/faq.adoc | 0 .../ROOT/pages/{ => jpa}/glossary.adoc | 0 .../modules/ROOT/pages/jpa/misc-context.adoc | 2 +- .../modules/ROOT/pages/jpa/query-methods.adoc | 19 +- .../pages/{jpa => }/query-by-example.adoc | 2 +- .../pages/repositories/core-concepts.adoc | 1 + .../repositories/core-domain-events.adoc | 1 + .../pages/repositories/core-extensions.adoc | 1 + .../pages/repositories/create-instances.adoc | 1 + .../repositories/custom-implementations.adoc | 1 + .../ROOT/pages/repositories/definition.adoc | 1 + .../ROOT/pages/repositories/introduction.adoc | 7 + .../pages/repositories/null-handling.adoc | 1 + .../ROOT/pages/repositories/projections.adoc | 6 + .../query-keywords-reference.adoc | 1 + .../repositories/query-methods-details.adoc | 1 + .../query-return-types-reference.adoc | 1 + .../resources/antora-resources/antora.yml | 7 + 30 files changed, 304 insertions(+), 260 deletions(-) rename src/main/antora/modules/ROOT/pages/{jpa => }/auditing.adoc (93%) create mode 100644 src/main/antora/modules/ROOT/pages/commons/upgrade.adoc create mode 100644 src/main/antora/modules/ROOT/pages/envers/configuration.adoc create mode 100644 src/main/antora/modules/ROOT/pages/envers/introduction.adoc create mode 100644 src/main/antora/modules/ROOT/pages/envers/usage.adoc rename src/main/antora/modules/ROOT/pages/jpa/{introduction.adoc => configuration.adoc} (91%) rename src/main/antora/modules/ROOT/pages/{ => jpa}/faq.adoc (100%) rename src/main/antora/modules/ROOT/pages/{ => jpa}/glossary.adoc (100%) rename src/main/antora/modules/ROOT/pages/{jpa => }/query-by-example.adoc (92%) create mode 100644 src/main/antora/modules/ROOT/pages/repositories/core-concepts.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/core-domain-events.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/core-extensions.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/definition.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/introduction.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/null-handling.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/projections.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/query-keywords-reference.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/query-methods-details.adoc create mode 100644 src/main/antora/modules/ROOT/pages/repositories/query-return-types-reference.adoc diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 453f49dea0..a458953182 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -37,6 +37,19 @@ maven-assembly-plugin + + org.apache.maven.plugins + maven-resources-plugin + + + + resources + + + + + + io.spring.maven.antora antora-maven-plugin @@ -44,17 +57,4 @@ - - - - antora - - - - - - - - - diff --git a/src/main/antora/antora-playbook.yml b/src/main/antora/antora-playbook.yml index 12ac5adfb4..d7c13a3d45 100644 --- a/src/main/antora/antora-playbook.yml +++ b/src/main/antora/antora-playbook.yml @@ -15,6 +15,11 @@ content: branches: HEAD start_path: src/main/antora worktrees: true + - url: https://github.com/spring-projects/spring-data-commons + # Refname matching: + # https://docs.antora.org/antora/latest/playbook/content-refname-matching/ + branches: [main, 3.2.x] + start_path: src/main/antora asciidoc: attributes: page-pagination: '' diff --git a/src/main/antora/modules/ROOT/nav.adoc b/src/main/antora/modules/ROOT/nav.adoc index 204efcda6f..1578f89bea 100644 --- a/src/main/antora/modules/ROOT/nav.adoc +++ b/src/main/antora/modules/ROOT/nav.adoc @@ -1,17 +1,34 @@ * xref:index.adoc[Overview] +** xref:commons/upgrade.adoc[] +* xref:repositories/introduction.adoc[] +** xref:repositories/core-concepts.adoc[] +** xref:repositories/definition.adoc[] +** xref:repositories/create-instances.adoc[] +** xref:repositories/query-methods-details.adoc[] +** xref:repositories/projections.adoc[] +** xref:repositories/custom-implementations.adoc[] +** xref:repositories/core-domain-events.adoc[] +** xref:repositories/core-extensions.adoc[] +** xref:repositories/null-handling.adoc[] +** xref:repositories/query-keywords-reference.adoc[] +** xref:repositories/query-return-types-reference.adoc[] * xref:jpa.adoc[] -** xref:jpa/introduction.adoc[] +** xref:jpa/configuration.adoc[] ** xref:jpa/entity-persistence.adoc[] ** xref:jpa/query-methods.adoc[] ** xref:jpa/stored-procedures.adoc[] ** xref:jpa/specifications.adoc[] -** xref:jpa/query-by-example.adoc[] +** xref:query-by-example.adoc[] ** xref:jpa/transactions.adoc[] ** xref:jpa/locking.adoc[] -** xref:jpa/auditing.adoc[] +** xref:auditing.adoc[] ** xref:jpa/misc-context.adoc[] ** xref:jpa/misc-merging-persistence-units.adoc[] ** xref:jpa/jpd-misc-cdi-integration.adoc[] +** xref:jpa/faq.adoc[] +** xref:jpa/glossary.adoc[] * xref:envers.adoc[] -* xref:faq.adoc[] -* xref:glossary.adoc[] +** xref:envers/introduction.adoc[] +** xref:envers/configuration.adoc[] +** xref:envers/usage.adoc[] +* https://github.com/spring-projects/spring-data-commons/wiki[Wiki] diff --git a/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc b/src/main/antora/modules/ROOT/pages/auditing.adoc similarity index 93% rename from src/main/antora/modules/ROOT/pages/jpa/auditing.adoc rename to src/main/antora/modules/ROOT/pages/auditing.adoc index 20aeaa4c7c..62eb9ce584 100644 --- a/src/main/antora/modules/ROOT/pages/jpa/auditing.adoc +++ b/src/main/antora/modules/ROOT/pages/auditing.adoc @@ -1,8 +1,4 @@ -[[jpa.auditing]] -= JPA Auditing - -Spring Data JPA provides auditing based upon the foundation provided by {spring-data-commons-docs-url}/auditing.html[Spring Data Common's Auditing support]. - +include::{commons}@data-commons::page$auditing.adoc[] There is also a convenience base class, `AbstractAuditable`, which you can extend to avoid the need to manually implement the interface methods. Doing so increases the coupling of your domain classes to Spring Data, which might be something you want to avoid. Usually, the annotation-based way of defining auditing metadata is preferred as it is less invasive and more flexible. diff --git a/src/main/antora/modules/ROOT/pages/commons/upgrade.adoc b/src/main/antora/modules/ROOT/pages/commons/upgrade.adoc new file mode 100644 index 0000000000..51a9189aa0 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/commons/upgrade.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$upgrade.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/envers.adoc b/src/main/antora/modules/ROOT/pages/envers.adoc index df8589135a..2e56e5e1b9 100644 --- a/src/main/antora/modules/ROOT/pages/envers.adoc +++ b/src/main/antora/modules/ROOT/pages/envers.adoc @@ -1,204 +1,5 @@ [[envers]] -= Spring Data Envers += Envers +:page-section-summary-toc: 1 -[[envers.what.is.spring.data]] -== What is Spring Data Envers? - -Spring Data Envers makes typical Envers queries available in repositories for Spring Data JPA. -It differs from other Spring Data modules in that it is always used in combination with another Spring Data Module: Spring Data JPA. - -[[envers.what]] -== What is Envers? - -Envers is a https://hibernate.org/orm/envers/[Hibernate module] that adds auditing capabilities to JPA entities. -This documentation assumes you are familiar with Envers, just as Spring Data Envers relies on Envers being properly configured. - -[[envers.configuration]] -== Configuration - -As a starting point for using Spring Data Envers, you need a project with Spring Data JPA on the classpath and an additional `spring-data-envers` dependency: - -==== -[source,xml,subs="+attributes"] ----- - - - - - - org.springframework.data - spring-data-envers - {version} - - - ----- -==== - -This also brings `hibernate-envers` into the project as a transient dependency. - -To enable Spring Data Envers and Spring Data JPA, we need to configure two beans and a special `repositoryFactoryBeanClass`: - -==== -[source,java] ----- -@Configuration -@EnableEnversRepositories -@EnableTransactionManagement -public class EnversDemoConfiguration { - - @Bean - public DataSource dataSource() { - - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.HSQL).build(); - } - - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - - HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); - vendorAdapter.setGenerateDdl(true); - - LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); - factory.setJpaVendorAdapter(vendorAdapter); - factory.setPackagesToScan("example.springdata.jpa.envers"); - factory.setDataSource(dataSource()); - return factory; - } - - @Bean - public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { - - JpaTransactionManager txManager = new JpaTransactionManager(); - txManager.setEntityManagerFactory(entityManagerFactory); - return txManager; - } -} ----- -==== - -To actually use Spring Data Envers, make one or more repositories into a {spring-data-commons-javadoc-base}/org/springframework/data/repository/history/RevisionRepository.html[`RevisionRepository`] by adding it as an extended interface: - -==== -[source,java] ----- -interface PersonRepository - extends CrudRepository, - RevisionRepository // <1> -{} ----- -<1> The first type parameter (`Person`) denotes the entity type, the second (`Long`) denotes the type of the id property, and the last one (`Long`) is the type of the revision number. -For Envers in default configuration, the revision number parameter should be `Integer` or `Long`. -==== - -The entity for that repository must be an entity with Envers auditing enabled (that is, it must have an `@Audited` annotation): - -==== -[source,java] ----- -@Entity -@Audited -class Person { - - @Id @GeneratedValue - Long id; - String name; - @Version Long version; -} ----- -==== - -[[envers.usage]] -== Usage - -You can now use the methods from `RevisionRepository` to query the revisions of the entity, as the following test case shows: - -==== -[source,java] ----- -@ExtendWith(SpringExtension.class) -@Import(EnversDemoConfiguration.class) // <1> -class EnversIntegrationTests { - - final PersonRepository repository; - final TransactionTemplate tx; - - EnversIntegrationTests(@Autowired PersonRepository repository, @Autowired PlatformTransactionManager tm) { - this.repository = repository; - this.tx = new TransactionTemplate(tm); - } - - @Test - void testRepository() { - - Person updated = preparePersonHistory(); - - Revisions revisions = repository.findRevisions(updated.id); - - Iterator> revisionIterator = revisions.iterator(); - - checkNextRevision(revisionIterator, "John", RevisionType.INSERT); - checkNextRevision(revisionIterator, "Jonny", RevisionType.UPDATE); - checkNextRevision(revisionIterator, null, RevisionType.DELETE); - assertThat(revisionIterator.hasNext()).isFalse(); - - } - - /** - * Checks that the next element in the iterator is a Revision entry referencing a Person - * with the given name after whatever change brought that Revision into existence. - *

    - * As a side effect the Iterator gets advanced by one element. - * - * @param revisionIterator the iterator to be tested. - * @param name the expected name of the Person referenced by the Revision. - * @param revisionType the type of the revision denoting if it represents an insert, update or delete. - */ - private void checkNextRevision(Iterator> revisionIterator, String name, - RevisionType revisionType) { - - assertThat(revisionIterator.hasNext()).isTrue(); - Revision revision = revisionIterator.next(); - assertThat(revision.getEntity().name).isEqualTo(name); - assertThat(revision.getMetadata().getRevisionType()).isEqualTo(revisionType); - } - - /** - * Creates a Person with a couple of changes so it has a non-trivial revision history. - * @return the created Person. - */ - private Person preparePersonHistory() { - - Person john = new Person(); - john.setName("John"); - - // create - Person saved = tx.execute(__ -> repository.save(john)); - assertThat(saved).isNotNull(); - - saved.setName("Jonny"); - - // update - Person updated = tx.execute(__ -> repository.save(saved)); - assertThat(updated).isNotNull(); - - // delete - tx.executeWithoutResult(__ -> repository.delete(updated)); - return updated; - } -} ----- -<1> This references the application context configuration presented earlier (in the xref:envers.adoc#envers.configuration[Configuration] section). -==== - -[[envers.resources]] -== Further Resources - -You can download the https://github.com/spring-projects/spring-data-examples[Spring Data Envers example in the Spring Data Examples repository] and play around with to get a feel for how the library works. - -You should also check out the {spring-data-commons-javadoc-base}/org/springframework/data/repository/history/RevisionRepository.html[Javadoc for `RevisionRepository`] and related classes. - -You can ask questions at https://stackoverflow.com/questions/tagged/spring-data-envers[Stackoverflow by using the `spring-data-envers` tag]. - -The https://github.com/spring-projects/spring-data-jpa[source code and issue tracker for Spring Data Envers is hosted at GitHub] (as a module of Spring Data JPA). +This chapter points out the specialties for repository support for Envers. This builds on the core repository support explained earlier. Make sure you have a sound understanding of the basic concepts explained there. diff --git a/src/main/antora/modules/ROOT/pages/envers/configuration.adoc b/src/main/antora/modules/ROOT/pages/envers/configuration.adoc new file mode 100644 index 0000000000..c144deb915 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/envers/configuration.adoc @@ -0,0 +1,95 @@ +[[envers.configuration]] += Configuration + +As a starting point for using Spring Data Envers, you need a project with Spring Data JPA on the classpath and an additional `spring-data-envers` dependency: + +==== +[source,xml,subs="+attributes"] +---- + + + + + + org.springframework.data + spring-data-envers + {version} + + + +---- +==== + +This also brings `hibernate-envers` into the project as a transient dependency. + +To enable Spring Data Envers and Spring Data JPA, we need to configure two beans and a special `repositoryFactoryBeanClass`: + +==== +[source,java] +---- +@Configuration +@EnableEnversRepositories +@EnableTransactionManagement +public class EnversDemoConfiguration { + + @Bean + public DataSource dataSource() { + + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); + return builder.setType(EmbeddedDatabaseType.HSQL).build(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + vendorAdapter.setGenerateDdl(true); + + LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); + factory.setJpaVendorAdapter(vendorAdapter); + factory.setPackagesToScan("example.springdata.jpa.envers"); + factory.setDataSource(dataSource()); + return factory; + } + + @Bean + public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + + JpaTransactionManager txManager = new JpaTransactionManager(); + txManager.setEntityManagerFactory(entityManagerFactory); + return txManager; + } +} +---- +==== + +To actually use Spring Data Envers, make one or more repositories into a {spring-data-commons-javadoc-base}/org/springframework/data/repository/history/RevisionRepository.html[`RevisionRepository`] by adding it as an extended interface: + +==== +[source,java] +---- +interface PersonRepository + extends CrudRepository, + RevisionRepository // <1> +{} +---- +<1> The first type parameter (`Person`) denotes the entity type, the second (`Long`) denotes the type of the id property, and the last one (`Long`) is the type of the revision number. +For Envers in default configuration, the revision number parameter should be `Integer` or `Long`. +==== + +The entity for that repository must be an entity with Envers auditing enabled (that is, it must have an `@Audited` annotation): + +==== +[source,java] +---- +@Entity +@Audited +class Person { + + @Id @GeneratedValue + Long id; + String name; + @Version Long version; +} +---- +==== diff --git a/src/main/antora/modules/ROOT/pages/envers/introduction.adoc b/src/main/antora/modules/ROOT/pages/envers/introduction.adoc new file mode 100644 index 0000000000..c082c3cf56 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/envers/introduction.adoc @@ -0,0 +1,14 @@ +[[envers.introduction]] += Introduction + +[[envers.what.is.spring.data]] +== What is Spring Data Envers? + +Spring Data Envers makes typical Envers queries available in repositories for Spring Data JPA. +It differs from other Spring Data modules in that it is always used in combination with another Spring Data Module: Spring Data JPA. + +[[envers.what]] +== What is Envers? + +Envers is a https://hibernate.org/orm/envers/[Hibernate module] that adds auditing capabilities to JPA entities. +This documentation assumes you are familiar with Envers, just as Spring Data Envers relies on Envers being properly configured. diff --git a/src/main/antora/modules/ROOT/pages/envers/usage.adoc b/src/main/antora/modules/ROOT/pages/envers/usage.adoc new file mode 100644 index 0000000000..96ee5ba3d3 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/envers/usage.adoc @@ -0,0 +1,93 @@ +[[envers.usage]] += Usage + +You can now use the methods from `RevisionRepository` to query the revisions of the entity, as the following test case shows: + +==== +[source,java] +---- +@ExtendWith(SpringExtension.class) +@Import(EnversDemoConfiguration.class) // <1> +class EnversIntegrationTests { + + final PersonRepository repository; + final TransactionTemplate tx; + + EnversIntegrationTests(@Autowired PersonRepository repository, @Autowired PlatformTransactionManager tm) { + this.repository = repository; + this.tx = new TransactionTemplate(tm); + } + + @Test + void testRepository() { + + Person updated = preparePersonHistory(); + + Revisions revisions = repository.findRevisions(updated.id); + + Iterator> revisionIterator = revisions.iterator(); + + checkNextRevision(revisionIterator, "John", RevisionType.INSERT); + checkNextRevision(revisionIterator, "Jonny", RevisionType.UPDATE); + checkNextRevision(revisionIterator, null, RevisionType.DELETE); + assertThat(revisionIterator.hasNext()).isFalse(); + + } + + /** + * Checks that the next element in the iterator is a Revision entry referencing a Person + * with the given name after whatever change brought that Revision into existence. + *

    + * As a side effect the Iterator gets advanced by one element. + * + * @param revisionIterator the iterator to be tested. + * @param name the expected name of the Person referenced by the Revision. + * @param revisionType the type of the revision denoting if it represents an insert, update or delete. + */ + private void checkNextRevision(Iterator> revisionIterator, String name, + RevisionType revisionType) { + + assertThat(revisionIterator.hasNext()).isTrue(); + Revision revision = revisionIterator.next(); + assertThat(revision.getEntity().name).isEqualTo(name); + assertThat(revision.getMetadata().getRevisionType()).isEqualTo(revisionType); + } + + /** + * Creates a Person with a couple of changes so it has a non-trivial revision history. + * @return the created Person. + */ + private Person preparePersonHistory() { + + Person john = new Person(); + john.setName("John"); + + // create + Person saved = tx.execute(__ -> repository.save(john)); + assertThat(saved).isNotNull(); + + saved.setName("Jonny"); + + // update + Person updated = tx.execute(__ -> repository.save(saved)); + assertThat(updated).isNotNull(); + + // delete + tx.executeWithoutResult(__ -> repository.delete(updated)); + return updated; + } +} +---- +<1> This references the application context configuration presented earlier (in the xref:envers.adoc#envers.configuration[Configuration] section). +==== + +[[envers.resources]] +== Further Resources + +You can download the https://github.com/spring-projects/spring-data-examples[Spring Data Envers example in the Spring Data Examples repository] and play around with to get a feel for how the library works. + +You should also check out the {spring-data-commons-javadoc-base}/org/springframework/data/repository/history/RevisionRepository.html[Javadoc for `RevisionRepository`] and related classes. + +You can ask questions at https://stackoverflow.com/questions/tagged/spring-data-envers[Stackoverflow by using the `spring-data-envers` tag]. + +The https://github.com/spring-projects/spring-data-jpa[source code and issue tracker for Spring Data Envers is hosted at GitHub] (as a module of Spring Data JPA). diff --git a/src/main/antora/modules/ROOT/pages/index.adoc b/src/main/antora/modules/ROOT/pages/index.adoc index 064d46877f..f6c6e67922 100644 --- a/src/main/antora/modules/ROOT/pages/index.adoc +++ b/src/main/antora/modules/ROOT/pages/index.adoc @@ -1,24 +1,21 @@ [[spring-data-jpa-reference-documentation]] = Spring Data JPA -Oliver Gierke; Thomas Darimont; Christoph Strobl; Mark Paluch; Jay Bryant; Greg Turnquist :revnumber: {version} :revdate: {localdate} :feature-scroll: true -(C) 2008-2023 The original authors. +_Spring Data JPA provides repository support for the Jakarta Persistence API (JPA). +It eases development of applications with a consistent programming model that need to access JPA data sources._ -NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. +[horizontal] +xref:jpa.adoc[JPA] :: JPA and JPA Repositories +xref:envers.adoc[Envers] :: Support for Envers Revision Repositories +https://github.com/spring-projects/spring-data-commons/wiki[Wiki] :: What's New, +Upgrade Notes, Supported Versions, additional cross-version information. -[[preface]] -== Preface -:page-section-summary-toc: 1 +Oliver Gierke, Thomas Darimont, Christoph Strobl, Mark Paluch, Jay Bryant, Greg Turnquist -Spring Data JPA provides repository support for the Jakarta Persistence API (JPA). It eases development of applications that need to access JPA data sources. +(C) 2008-2023 VMware, Inc. -[[project]] -== Project Metadata +Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. -* Version control: https://github.com/spring-projects/spring-data-jpa -* Bugtracker: https://github.com/spring-projects/spring-data-jpa/issues -* Milestone repository: https://repo.spring.io/milestone -* Snapshot repository: https://repo.spring.io/snapshot \ No newline at end of file diff --git a/src/main/antora/modules/ROOT/pages/jpa.adoc b/src/main/antora/modules/ROOT/pages/jpa.adoc index eae798bbc7..b490dfcaa6 100644 --- a/src/main/antora/modules/ROOT/pages/jpa.adoc +++ b/src/main/antora/modules/ROOT/pages/jpa.adoc @@ -1,6 +1,6 @@ [[jpa.repositories]] -= JPA Repositories += JPA :page-section-summary-toc: 1 -This chapter points out the specialties for repository support for JPA. This builds on the core repository support explained in {spring-data-commons-docs-url}/repositories.html[Working with Spring Data Repositories]. Make sure you have a sound understanding of the basic concepts explained there. +This chapter points out the specialties for repository support for JPA. This builds on the core repository support explained in xref:repositories/introduction.adoc[Working with Spring Data Repositories]. Make sure you have a sound understanding of the basic concepts explained there. diff --git a/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc b/src/main/antora/modules/ROOT/pages/jpa/configuration.adoc similarity index 91% rename from src/main/antora/modules/ROOT/pages/jpa/introduction.adoc rename to src/main/antora/modules/ROOT/pages/jpa/configuration.adoc index d1f66fd30a..5307e4e986 100644 --- a/src/main/antora/modules/ROOT/pages/jpa/introduction.adoc +++ b/src/main/antora/modules/ROOT/pages/jpa/configuration.adoc @@ -1,10 +1,10 @@ -[[jpa.introduction]] -= Introduction +[[jpa.configuration]] += Configuration -This section describes the basics of configuring Spring Data JPA through either: +This section describes configuring Spring Data JPA through either: -* "`xref:jpa/introduction.adoc#jpa.namespace[Spring Namespace]`" (XML configuration) -* "`xref:jpa/introduction.adoc#jpa.java-config[Annotation-based Configuration]`" (Java configuration) +* "`<>`" (XML configuration) +* "`<>`" (Java configuration) [[jpa.java-config]] == Annotation-based Configuration @@ -79,7 +79,7 @@ The JPA module of Spring Data contains a custom namespace that allows defining r TIP: Which is better, JavaConfig or XML? XML is how Spring was configured long ago. In today's era of fast-growing Java, record types, annotations, and more, new projects typically use as much pure Java as possible. While there is no immediate plan to remove XML support, some of the newest features MAY not be available through XML. -Using the `repositories` element looks up Spring Data repositories as described in {spring-data-commons-docs-url}/repositories/create-instances.html[Creating Repository Instances]. Beyond that, it activates persistence exception translation for all beans annotated with `@Repository`, to let exceptions being thrown by the JPA persistence providers be converted into Spring's `DataAccessException` hierarchy. +Using the `repositories` element looks up Spring Data repositories as described in xref:repositories/create-instances.adoc[Creating Repository Instances]. Beyond that, it activates persistence exception translation for all beans annotated with `@Repository`, to let exceptions being thrown by the JPA persistence providers be converted into Spring's `DataAccessException` hierarchy. [[jpa.namespace.custom-namespace-attributes]] === Custom Namespace Attributes diff --git a/src/main/antora/modules/ROOT/pages/faq.adoc b/src/main/antora/modules/ROOT/pages/jpa/faq.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/faq.adoc rename to src/main/antora/modules/ROOT/pages/jpa/faq.adoc diff --git a/src/main/antora/modules/ROOT/pages/glossary.adoc b/src/main/antora/modules/ROOT/pages/jpa/glossary.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/glossary.adoc rename to src/main/antora/modules/ROOT/pages/jpa/glossary.adoc diff --git a/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc b/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc index 7cdb5feff4..a12840ad44 100644 --- a/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc +++ b/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc @@ -1,7 +1,7 @@ [[jpa.misc.jpa-context]] = Using `JpaContext` in Custom Implementations -When working with multiple `EntityManager` instances and <>, you need to wire the correct `EntityManager` into the repository implementation class. You can do so by explicitly naming the `EntityManager` in the `@PersistenceContext` annotation or, if the `EntityManager` is `@Autowired`, by using `@Qualifier`. +When working with multiple `EntityManager` instances and xref:repositories/custom-implementations.adoc#repositories.custom-implementations[custom repository implementations], you need to wire the correct `EntityManager` into the repository implementation class. You can do so by explicitly naming the `EntityManager` in the `@PersistenceContext` annotation or, if the `EntityManager` is `@Autowired`, by using `@Qualifier`. As of Spring Data JPA 1.9, Spring Data JPA includes a class called `JpaContext` that lets you obtain the `EntityManager` by managed domain class, assuming it is managed by only one of the `EntityManager` instances in the application. The following example shows how to use `JpaContext` in a custom repository: diff --git a/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc index 39be17e2d1..1b59f44ad3 100644 --- a/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc +++ b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc @@ -31,7 +31,7 @@ public interface UserRepository extends Repository { List findByEmailAddressAndLastname(String emailAddress, String lastname); } ---- -We create a query using the JPA criteria API from this, but, essentially, this translates into the following query: `select u from User u where u.emailAddress = ?1 and u.lastname = ?2`. Spring Data JPA does a property check and traverses nested properties, as described in {spring-data-commons-docs-url}/repositories/query-methods-details.html#repositories.query-methods.query-property-expressions[Property Expressions]. +We create a query using the JPA criteria API from this, but, essentially, this translates into the following query: `select u from User u where u.emailAddress = ?1 and u.lastname = ?2`. Spring Data JPA does a property check and traverses nested properties, as described in xref:repositories/query-methods-details.adoc#repositories.query-methods.query-property-expressions[Property Expressions]. ==== The following table describes the keywords supported for JPA and what a method containing that keyword translates to: @@ -67,7 +67,7 @@ The following table describes the keywords supported for JPA and what a method c |`IgnoreCase`|`findByFirstnameIgnoreCase`|`… where UPPER(x.firstname) = UPPER(?1)` |=============== -NOTE: `In` and `NotIn` also take any subclass of `Collection` as a parameter as well as arrays or varargs. For other syntactical versions of the same logical operator, check {spring-data-commons-docs-url}/repository-query-keywords-reference.html[Repository query keywords]. +NOTE: `In` and `NotIn` also take any subclass of `Collection` as a parameter as well as arrays or varargs. For other syntactical versions of the same logical operator, check xref:repositories/query-keywords-reference.adoc[Repository query keywords]. [WARNING] ==== @@ -350,16 +350,16 @@ When working with large data sets, <> can help You have multiple options to consume large query results: -1. <>. +1. xref:repositories/query-methods-details.adoc#repositories.paging-and-sorting[Paging]. You have learned in the previous chapter about `Pageable` and `PageRequest`. 2. <>. This is a lighter variant than paging because it does not require the total result count. 3. <>. This method avoids https://use-the-index-luke.com/no-offset[the shortcomings of offset-based result retrieval by leveraging database indexes]. -Read more on <> for your particular arrangement. +Read more on xref:repositories/query-methods-details.adoc#repositories.scrolling.guidance,which method to use best>> for your particular arrangement. -You can use the Scroll API with query methods, xref:jpa/query-by-example.adoc[Query-by-Example], and <>. +You can use the Scroll API with query methods, xref:query-by-example.adoc[Query-by-Example], and xref:repositories/core-extensions.adoc#core.extensions.querydsl[Querydsl]. NOTE: Scrolling with String-based query methods is not yet supported. Scrolling is also not supported using stored `@Procedure` query methods. @@ -507,7 +507,7 @@ But sometimes, your query may simply be too complicated for the techniques offer In that situation, consider: * If you haven't already, simply write the query yourself using xref:jpa/query-methods.adoc#jpa.query-methods.at-query[`@Query`]. -* If that doesn't fit your needs, consider implementing a <>. This lets you register a method in your repository while leaving the implementation completely up to you. This gives you the ability to: +* If that doesn't fit your needs, consider implementing a xref:repositories/custom-implementations.adoc#repositories.custom-implementations[custom implementation]. This lets you register a method in your repository while leaving the implementation completely up to you. This gives you the ability to: ** Talk directly to the `EntityManager` (writing pure HQL/JPQL/EQL/native SQL or using the *Criteria API*) ** Leverage Spring Framework's `JdbcTemplate` (native SQL) ** Use another 3rd-party database toolkit. @@ -773,9 +773,4 @@ public interface GroupRepository extends CrudRepository { ---- ==== -[[projections]] -== Projections - -Spring Data JPA supports {spring-data-commons-docs-url}/repository-projections.html[Spring Data Commons Projections]. - -NOTE: It is important to note that {spring-data-commons-docs-url}/repository-projections.html#projections.dtos[Class-based projections] with JPQL is limited to *constructor expressions* in your JPQL expression, e.g. `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. (Note the usage of a FQDN for the DTO type!) This JPQL expression can be used in `@Query` annotations as well where you define any named queries. And it's important to point out that class-based projections do not work with native queries AT ALL. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] \ No newline at end of file +include::{commons}@data-commons::page$repositories/scrolling.adoc[leveloffset=+1] diff --git a/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc b/src/main/antora/modules/ROOT/pages/query-by-example.adoc similarity index 92% rename from src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc rename to src/main/antora/modules/ROOT/pages/query-by-example.adoc index 2a7c661db4..5f427c29ba 100644 --- a/src/main/antora/modules/ROOT/pages/jpa/query-by-example.adoc +++ b/src/main/antora/modules/ROOT/pages/query-by-example.adoc @@ -1,6 +1,6 @@ = Query by Example -Spring Data JPA leverages {spring-data-commons-docs-url}/query-by-example.html[Spring Data Commons support for Query by Example]. +include::{commons}@data-commons::page$query-by-example.adoc[leveloffset=+1] [[query-by-example.running]] In Spring Data JPA, you can use Query by Example with Repositories, as shown in the following example: diff --git a/src/main/antora/modules/ROOT/pages/repositories/core-concepts.adoc b/src/main/antora/modules/ROOT/pages/repositories/core-concepts.adoc new file mode 100644 index 0000000000..4ae3ce6763 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/core-concepts.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/core-concepts.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/core-domain-events.adoc b/src/main/antora/modules/ROOT/pages/repositories/core-domain-events.adoc new file mode 100644 index 0000000000..f84313e9da --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/core-domain-events.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/core-domain-events.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/core-extensions.adoc b/src/main/antora/modules/ROOT/pages/repositories/core-extensions.adoc new file mode 100644 index 0000000000..a7c2ff8d3c --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/core-extensions.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/core-extensions.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc b/src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc new file mode 100644 index 0000000000..2ae01801b1 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/create-instances.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc b/src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc new file mode 100644 index 0000000000..c7615191a6 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/custom-implementations.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/definition.adoc b/src/main/antora/modules/ROOT/pages/repositories/definition.adoc new file mode 100644 index 0000000000..bd65a8af83 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/definition.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/definition.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/introduction.adoc b/src/main/antora/modules/ROOT/pages/repositories/introduction.adoc new file mode 100644 index 0000000000..2eda81a93c --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/introduction.adoc @@ -0,0 +1,7 @@ +[[common.basics]] += Introduction +:page-section-summary-toc: 1 + +This chapter explains the basic foundations of Spring Data repositories. Before continuing to the JPA specifics, make sure you have a sound understanding of the basic concepts explained here. + +The goal of the Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores. diff --git a/src/main/antora/modules/ROOT/pages/repositories/null-handling.adoc b/src/main/antora/modules/ROOT/pages/repositories/null-handling.adoc new file mode 100644 index 0000000000..081bac9f61 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/null-handling.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/null-handling.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/projections.adoc b/src/main/antora/modules/ROOT/pages/repositories/projections.adoc new file mode 100644 index 0000000000..5635695699 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/projections.adoc @@ -0,0 +1,6 @@ +[[jpa.projections]] += Projections + +include::{commons}@data-commons::page$repositories/projections.adoc[leveloffset=+1] + +NOTE: It is important to note that <> with JPQL is limited to *constructor expressions* in your JPQL expression, e.g. `SELECT new com.example.NamesOnly(u.firstname, u.lastname) from User u`. (Note the usage of a FQDN for the DTO type!) This JPQL expression can be used in `@Query` annotations as well where you define any named queries. And it's important to point out that class-based projections do not work with native queries AT ALL. As a workaround you may use named queries with `ResultSetMapping` or the Hibernate specific https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/transform/ResultTransformer.html[`ResultTransformer`] diff --git a/src/main/antora/modules/ROOT/pages/repositories/query-keywords-reference.adoc b/src/main/antora/modules/ROOT/pages/repositories/query-keywords-reference.adoc new file mode 100644 index 0000000000..e495eddc6b --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/query-keywords-reference.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/query-keywords-reference.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/query-methods-details.adoc b/src/main/antora/modules/ROOT/pages/repositories/query-methods-details.adoc new file mode 100644 index 0000000000..dfe4814955 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/query-methods-details.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/query-methods-details.adoc[] diff --git a/src/main/antora/modules/ROOT/pages/repositories/query-return-types-reference.adoc b/src/main/antora/modules/ROOT/pages/repositories/query-return-types-reference.adoc new file mode 100644 index 0000000000..a73c3201d0 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/repositories/query-return-types-reference.adoc @@ -0,0 +1 @@ +include::{commons}@data-commons::page$repositories/query-return-types-reference.adoc[] diff --git a/src/main/antora/resources/antora-resources/antora.yml b/src/main/antora/resources/antora-resources/antora.yml index f42c5ef7af..61386b7d27 100644 --- a/src/main/antora/resources/antora-resources/antora.yml +++ b/src/main/antora/resources/antora-resources/antora.yml @@ -7,7 +7,14 @@ asciidoc: springversionshort: ${spring.short} springversion: ${spring} attribute-missing: 'warn' + commons: ${springdata.commons.docs} + include-xml-namespaces: false spring-data-commons-docs-url: https://docs.spring.io/spring-data-commons/reference spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/commons/docs/${springdata.commons}/api/ springdocsurl: https://docs.spring.io/spring-framework/reference/{springversionshort} springjavadocurl: https://docs.spring.io/spring-framework/docs/${spring}/javadoc-api + spring-framework-docs: '{springdocsurl}' + spring-framework-javadoc: '{springjavadocurl}' + springhateoasversion: ${spring-hateoas} + releasetrainversion: ${releasetrain} + store: Jpa From d7be8ec97f07309affd17b443539e141d0e38189 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 25 Aug 2023 09:16:06 +0200 Subject: [PATCH 459/821] Register individual bindings for IN bindings if parameter is already bound differently. We now register a new parameter binding if the named/positional parameter is already bound in an incompatible style. Closes #3126 --- .../jpa/repository/query/StringQuery.java | 26 ++++++--------- .../query/StringQueryUnitTests.java | 32 +++++++++++++++++-- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index cd549ef6f3..2f4a94844c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -288,36 +288,28 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St : ParameterOrigin.ofExpression(expression); BindingIdentifier targetBinding = queryParameter; + Function bindingFactory; switch (ParameterBindingType.of(typeSource)) { case LIKE: Type likeType = LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); - - if (origin.isExpression()) { - parameterBindings.register(new LikeParameterBinding(queryParameter, origin, likeType)); - } else { - targetBinding = parameterBindings.register(queryParameter, origin, - (identifier) -> new LikeParameterBinding(identifier, origin, likeType)); - } - + bindingFactory = (identifier) -> new LikeParameterBinding(identifier, origin, likeType); break; case IN: - - parameterBindings.register(new InParameterBinding(queryParameter, origin)); - + bindingFactory = (identifier) -> new InParameterBinding(identifier, origin); break; case AS_IS: // fall-through we don't need a special parameter queryParameter for the given parameter. default: + bindingFactory = (identifier) -> new ParameterBinding(identifier, origin); + } - if (origin.isExpression()) { - parameterBindings.register(new ParameterBinding(queryParameter, origin)); - } else { - targetBinding = parameterBindings.register(queryParameter, origin, - (identifier) -> new ParameterBinding(identifier, origin)); - } + if (origin.isExpression()) { + parameterBindings.register(bindingFactory.apply(queryParameter)); + } else { + targetBinding = parameterBindings.register(queryParameter, origin, bindingFactory); } replacement = targetBinding.hasName() ? ":" + targetBinding.getName() diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 8c95b38bfd..13ea4ca311 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -243,7 +243,6 @@ void detectsNamedInParameterBindings() { assertThat(bindings).hasSize(1); assertNamedBinding(InParameterBinding.class, "ids", bindings.get(0)); - } @Test // DATAJPA-461 @@ -276,7 +275,37 @@ void detectsPositionalInParameterBindings() { assertThat(bindings).hasSize(1); assertPositionalBinding(InParameterBinding.class, 1, bindings.get(0)); + } + + @Test // GH-3126 + void allowsReuseOfParameterWithInAndRegularBinding() { + + StringQuery query = new StringQuery( + "select u from User u where COALESCE(?1) is null OR u.id in ?1 OR COALESCE(?1) is null OR u.id in ?1", true); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()).isEqualTo( + "select u from User u where COALESCE(?1) is null OR u.id in ?2 OR COALESCE(?1) is null OR u.id in ?2"); + + List bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(2); + assertPositionalBinding(ParameterBinding.class, 1, bindings.get(0)); + assertPositionalBinding(InParameterBinding.class, 2, bindings.get(1)); + + query = new StringQuery( + "select u from User u where COALESCE(:foo) is null OR u.id in :foo OR COALESCE(:foo) is null OR u.id in :foo", + true); + + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getQueryString()).isEqualTo( + "select u from User u where COALESCE(:foo) is null OR u.id in :foo_1 OR COALESCE(:foo) is null OR u.id in :foo_1"); + + bindings = query.getParameterBindings(); + assertThat(bindings).hasSize(2); + + assertNamedBinding(ParameterBinding.class, "foo", bindings.get(0)); + assertNamedBinding(InParameterBinding.class, "foo_1", bindings.get(1)); } @Test // DATAJPA-461 @@ -349,7 +378,6 @@ void detectsInBindingWithSpecialFrenchCharactersInParentheses() { assertThat(bindings).hasSize(1); assertNamedBinding(InParameterBinding.class, "abonnés", bindings.get(0)); - } @Test // DATAJPA-545 From c7d403a37a128915462e3fd5c4c5bd57ec501759 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 25 Aug 2023 09:31:30 +0200 Subject: [PATCH 460/821] Correctly validate mixed parameter bind marker usage. We now inspect individual parameters instead of the resulting query whether the query contains JDBC-style bind markers. Previously, we inspected the final (rewritten) query which might have contained question marks in literals leading to improper validation failures. Closes #3125 --- .../jpa/repository/query/StringQuery.java | 23 +++++++++++-------- .../query/StringQueryUnitTests.java | 14 ++++++++++- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index 2f4a94844c..afa68ff51c 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -244,13 +244,6 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St int currentIndex = 0; boolean usesJpaStyleParameters = false; - if (JDBC_STYLE_PARAM.matcher(resultingQuery).find()) { - queryMeta.usesJdbcStyleParameters = true; - } - - if (NUMBERED_STYLE_PARAM.matcher(resultingQuery).find() || NAMED_STYLE_PARAM.matcher(resultingQuery).find()) { - usesJpaStyleParameters = true; - } while (matcher.find()) { @@ -262,6 +255,19 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St String parameterName = parameterIndexString != null ? null : matcher.group(NAMED_PARAMETER_GROUP); Integer parameterIndex = getParameterIndex(parameterIndexString); + String match = matcher.group(0); + if (JDBC_STYLE_PARAM.matcher(match).find()) { + queryMeta.usesJdbcStyleParameters = true; + } + + if (NUMBERED_STYLE_PARAM.matcher(match).find() || NAMED_STYLE_PARAM.matcher(match).find()) { + usesJpaStyleParameters = true; + } + + if (usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) { + throw new IllegalArgumentException("Mixing of ? parameters and other forms like ?1 is not supported"); + } + String typeSource = matcher.group(COMPARISION_TYPE_GROUP); Assert.isTrue(parameterIndexString != null || parameterName != null, () -> String.format("We need either a name or an index; Offending query string: %s", query)); @@ -273,9 +279,6 @@ private String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(St parameterIndex = expressionParameterIndex; } - if (usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) { - throw new IllegalArgumentException("Mixing of ? parameters and other forms like ?1 is not supported"); - } BindingIdentifier queryParameter; if (parameterIndex != null) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index 13ea4ca311..7f0f81061b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -629,7 +629,7 @@ void questionMarkInStringLiteral() { assertThat(query.getQueryString()).isEqualTo(queryString); assertThat(query.hasParameterBindings()).isFalse(); assertThat(query.getParameterBindings()).isEmpty(); - + assertThat(query.usesJdbcStyleParameters()).isFalse(); } @Test // DATAJPA-1318 @@ -667,6 +667,18 @@ void isNotDefaultProjection() { } } + @Test // GH-3125 + void questionMarkInStringLiteralWithParameters() { + + String queryString = "SELECT CAST(REGEXP_SUBSTR(itp.template_as_txt, '(?<=templateId\\\\\\\\=)(\\\\\\\\d+)(?:\\\\\\\\R)') AS INT) AS templateId FROM foo itp WHERE bar = ?1 AND baz = 1"; + StringQuery query = new StringQuery(queryString, false); + + assertThat(query.getQueryString()).isEqualTo(queryString); + assertThat(query.hasParameterBindings()).isTrue(); + assertThat(query.getParameterBindings()).hasSize(1); + assertThat(query.usesJdbcStyleParameters()).isFalse(); + } + @Test // DATAJPA-1652 void usingPipesWithNamedParameter() { From e5ec1aa0d3f879fa8b856f68815ba290e0c2273d Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 28 Aug 2023 12:05:57 -0500 Subject: [PATCH 461/821] NEW should be legal as a token in a state field path expression in JPQL. A multi-node token that has NEW as one of its elements should work just fine in a JPQL query. See #3128 --- .../data/jpa/repository/query/Jpql.g4 | 1 + .../repository/query/HqlQueryRendererTests.java | 16 ++++++++++++++++ .../repository/query/JpqlQueryRendererTests.java | 16 ++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 5c63a543b1..271e9d168b 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -602,6 +602,7 @@ identification_variable | INNER | KEY | LEFT + | NEW | ORDER | OUTER | FLOOR diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index d95edc11e4..0feaf2cdde 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1584,4 +1584,20 @@ and CTM_UTLRAW_NLSSORT_LOWER(b.nome) like lower(:nome) order by CTM_UTLRAW_NLSSORT_LOWER(b.nome) ASC """); } + + @Test // GH-3128 + void newShouldBeLegalAsPartOfAStateFieldPathExpression() { + + assertQuery(""" + SELECT j + FROM AgentUpdateTask j + WHERE j.creationTimestamp < :date + AND (j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.NEW + OR + j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.STARTED + OR + j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.QUEUED) + ORDER BY j.id + """); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index c30dcbf71f..d738ccac37 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -967,4 +967,20 @@ void timeShouldBeAValidParameterName() { WHERE L.isLocked = FALSE OR L.forceUnlockTime < :time """); } + + @Test // GH-3128 + void newShouldBeLegalAsPartOfAStateFieldPathExpression() { + + assertQuery(""" + SELECT j + FROM AgentUpdateTask j + WHERE j.creationTimestamp < :date + AND (j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.NEW + OR + j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.STARTED + OR + j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.QUEUED) + ORDER BY j.id + """); + } } From 20cef9afd61d4c4f93961739e1965671b93bacf2 Mon Sep 17 00:00:00 2001 From: shin-mallang Date: Thu, 31 Aug 2023 20:43:02 +0900 Subject: [PATCH 462/821] Polish code using instanceof pattern matching. See #3134 --- .../support/EnversRevisionRepositoryImpl.java | 8 ++++---- .../data/jpa/provider/HibernateUtils.java | 4 ++-- .../data/jpa/provider/JpaClassUtils.java | 4 ++-- .../jpa/repository/cdi/JpaRepositoryExtension.java | 2 +- .../repository/query/JpaQueryTransformerSupport.java | 2 +- .../data/jpa/repository/query/Meta.java | 2 +- .../repository/query/ParameterMetadataProvider.java | 3 +-- .../data/jpa/repository/query/QueryUtils.java | 10 ++++------ ...ityManagerBeanDefinitionRegistrarPostProcessor.java | 3 +-- .../data/jpa/repository/support/Querydsl.java | 4 ++-- .../data/jpa/util/BeanDefinitionUtils.java | 8 ++++---- 11 files changed, 23 insertions(+), 27 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index 259f510edb..ef55a78809 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -169,8 +169,8 @@ public Page> findRevisions(ID id, Pageable pageable) { AuditQuery baseQuery = createBaseQuery(id); - List orderMapped = (pageable.getSort() instanceof RevisionSort) ? - Collections.singletonList(mapRevisionSort((RevisionSort) pageable.getSort())) : + List orderMapped = (pageable.getSort() instanceof RevisionSort revisionSort) ? + Collections.singletonList(mapRevisionSort(revisionSort)) : mapPropertySort(pageable.getSort()); orderMapped.forEach(baseQuery::addOrder); @@ -232,8 +232,8 @@ static class QueryResult { RevisionMetadata createRevisionMetadata() { - return metadata instanceof DefaultRevisionEntity // - ? new DefaultRevisionMetadata((DefaultRevisionEntity) metadata, revisionType) // + return metadata instanceof DefaultRevisionEntity defaultRevisionEntity // + ? new DefaultRevisionMetadata(defaultRevisionEntity, revisionType) // : new AnnotationRevisionMetadata<>(Hibernate.unproxy(metadata), RevisionNumber.class, RevisionTimestamp.class, revisionType); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java index 92983f1343..98e65c2d96 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -45,8 +45,8 @@ public static String getHibernateQuery(Object query) { try { // Try the new Hibernate implementation first - if (query instanceof SqmQuery) { - return ((SqmQuery) query).getSqmStatement().toHqlString(); + if (query instanceof SqmQuery sqmQuery) { + return sqmQuery.getSqmStatement().toHqlString(); } // Couple of cases in which this still breaks, see HHH-15389 diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java index 771979c56c..10de0aad25 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java @@ -48,8 +48,8 @@ public static boolean isEntityManagerOfType(EntityManager em, String type) { EntityManager entityManagerToUse = em; Object delegate = em.getDelegate(); - if (delegate instanceof EntityManager) { - entityManagerToUse = (EntityManager) delegate; + if (delegate instanceof EntityManager delegateEntityManager) { + entityManagerToUse = delegateEntityManager; } return isOfType(entityManagerToUse, type, entityManagerToUse.getClass().getClassLoader()); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index 9a218c4abc..2e345c22a0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -68,7 +68,7 @@ void processBean(@Observes ProcessBean processBean) { Bean bean = processBean.getBean(); for (Type type : bean.getTypes()) { // Check if the bean is an EntityManager. - if (type instanceof Class && EntityManager.class.isAssignableFrom((Class) type)) { + if (type instanceof Class classType && EntityManager.class.isAssignableFrom(classType)) { Set qualifiers = new HashSet<>(bean.getQualifiers()); if (bean.isAlternative() || !entityManagers.containsKey(qualifiers)) { LOGGER.debug(String.format("Discovered '%s' with qualifiers %s", EntityManager.class.getName(), qualifiers)); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java index b791c4771e..13ad183fd2 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java @@ -85,7 +85,7 @@ List generateOrderByArguments(String primaryFromAlias, Sor */ private void checkSortExpression(Sort.Order order) { - if (order instanceof JpaSort.JpaOrder && ((JpaSort.JpaOrder) order).isUnsafe()) { + if (order instanceof JpaSort.JpaOrder jpaOrder && jpaOrder.isUnsafe()) { return; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java index a3e07cf14d..771d8b5827 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java @@ -103,7 +103,7 @@ void setValue(String key, @Nullable Object value) { values = new LinkedHashMap<>(2); } - if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) { + if (value == null || (value instanceof String stringValue && !StringUtils.hasText(stringValue))) { this.values.remove(key); } this.values.put(key, value); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index b3737940f5..52cc951561 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -274,9 +274,8 @@ private static Collection toCollection(@Nullable Object value) { return null; } - if (value instanceof Collection) { + if (value instanceof Collection collection) { - Collection collection = (Collection) value; return collection.isEmpty() ? null : collection; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 0d2c65072b..0b781b33d4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -846,12 +846,10 @@ private static boolean requiresOuterJoin(From from, PropertyPath property, return true; } - if (!(propertyPathModel instanceof Attribute)) { + if (!(propertyPathModel instanceof Attribute attribute)) { return false; } - Attribute attribute = (Attribute) propertyPathModel; - // not a persistent attribute type association (@OneToOne, @ManyToOne) if (!ASSOCIATION_TYPES.containsKey(attribute.getPersistentAttributeType())) { return false; @@ -882,11 +880,11 @@ private static T getAnnotationProperty(Attribute attribute, String pro Member member = attribute.getJavaMember(); - if (!(member instanceof AnnotatedElement)) { + if (!(member instanceof AnnotatedElement annotatedMember)) { return defaultValue; } - Annotation annotation = AnnotationUtils.getAnnotation((AnnotatedElement) member, associationAnnotation); + Annotation annotation = AnnotationUtils.getAnnotation(annotatedMember, associationAnnotation); return annotation == null ? defaultValue : (T) AnnotationUtils.getValue(annotation, propertyName); } @@ -945,7 +943,7 @@ private static boolean isAlreadyInnerJoined(From from, String attribute) { */ static void checkSortExpression(Order order) { - if (order instanceof JpaOrder && ((JpaOrder) order).isUnsafe()) { + if (order instanceof JpaOrder jpaOrder && jpaOrder.isUnsafe()) { return; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index f4e953576d..cf3ed03ff0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -64,12 +64,11 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) BeanFactory definitionFactory = definition.getBeanFactory(); - if (!(definitionFactory instanceof BeanDefinitionRegistry)) { + if (!(definitionFactory instanceof BeanDefinitionRegistry definitionRegistry)) { continue; } String entityManagerBeanName = "jpaSharedEM_AWC_" + definition.getBeanName(); - BeanDefinitionRegistry definitionRegistry = (BeanDefinitionRegistry) definitionFactory; if (!beanFactory.containsBeanDefinition(entityManagerBeanName) && !definitionRegistry.containsBeanDefinition(entityManagerBeanName)) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 25fb4abec2..9cc1017a47 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -137,8 +137,8 @@ public JPQLQuery applySorting(Sort sort, JPQLQuery query) { return query; } - if (sort instanceof QSort) { - return addOrderByFrom((QSort) sort, query); + if (sort instanceof QSort qsort) { + return addOrderByFrom(qsort, query); } return addOrderByFrom(sort, query); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java index 779be14457..322cf3095d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java @@ -107,8 +107,8 @@ public static Collection getEntityManagerFac BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory(); - if (parentBeanFactory instanceof ConfigurableListableBeanFactory) { - definitions.addAll(getEntityManagerFactoryBeanDefinitions((ConfigurableListableBeanFactory) parentBeanFactory)); + if (parentBeanFactory instanceof ConfigurableListableBeanFactory parentConfigurableListableBeanFactory) { + definitions.addAll(getEntityManagerFactoryBeanDefinitions(parentConfigurableListableBeanFactory)); } return definitions; @@ -157,8 +157,8 @@ public static BeanDefinition getBeanDefinition(String name, ConfigurableListable BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory(); - if (parentBeanFactory instanceof ConfigurableListableBeanFactory) { - return getBeanDefinition(name, (ConfigurableListableBeanFactory) parentBeanFactory); + if (parentBeanFactory instanceof ConfigurableListableBeanFactory parentConfigurableListableBeanFactory) { + return getBeanDefinition(name, parentConfigurableListableBeanFactory); } throw o_O; From a4977b2a6f2ce2f6a41023e000d4ec50c95af396 Mon Sep 17 00:00:00 2001 From: shin-mallang Date: Thu, 31 Aug 2023 20:56:22 +0900 Subject: [PATCH 463/821] Polishing a switch statement. See #3134 --- .../support/EnversRevisionRepositoryImpl.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index ef55a78809..334aa60ee8 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -240,17 +240,12 @@ RevisionMetadata createRevisionMetadata() { private static RevisionMetadata.RevisionType convertRevisionType(RevisionType datum) { - switch (datum) { - - case ADD: - return INSERT; - case MOD: - return UPDATE; - case DEL: - return DELETE; - default: - return UNKNOWN; - } + return switch (datum) { + case ADD -> INSERT; + case MOD -> UPDATE; + case DEL -> DELETE; + default -> UNKNOWN; + }; } } From 3b8556bcf472a80ed3270297bcf8413ab5522664 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 5 Sep 2023 08:49:34 -0500 Subject: [PATCH 464/821] Polishing. See #3134 --- .../support/EnversRevisionRepositoryImpl.java | 50 ++++++++++--------- .../data/jpa/provider/HibernateUtils.java | 6 ++- .../data/jpa/provider/JpaClassUtils.java | 11 ++-- .../cdi/JpaRepositoryExtension.java | 17 ++++--- .../query/JpaQueryTransformerSupport.java | 1 + .../data/jpa/repository/query/Meta.java | 1 + .../query/ParameterMetadataProvider.java | 10 +++- .../data/jpa/repository/query/QueryUtils.java | 1 + ...rBeanDefinitionRegistrarPostProcessor.java | 1 + .../data/jpa/repository/support/Querydsl.java | 1 + .../data/jpa/util/BeanDefinitionUtils.java | 5 +- 11 files changed, 60 insertions(+), 44 deletions(-) diff --git a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java index 334aa60ee8..46a92c6d8c 100755 --- a/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java +++ b/spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java @@ -15,7 +15,15 @@ */ package org.springframework.data.envers.repository.support; +import static org.springframework.data.history.RevisionMetadata.RevisionType.*; + import jakarta.persistence.EntityManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + import org.hibernate.Hibernate; import org.hibernate.envers.AuditReader; import org.hibernate.envers.AuditReaderFactory; @@ -43,13 +51,6 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.springframework.data.history.RevisionMetadata.RevisionType.*; - /** * Repository implementation using Hibernate Envers to implement revision specific query methods. * @@ -61,6 +62,8 @@ * @author Mark Paluch * @author Sander Bylemans * @author Niklas Loechte + * @author Donghun Shin + * @author Greg Turnquist */ @Transactional(readOnly = true) public class EnversRevisionRepositoryImpl> @@ -73,12 +76,12 @@ public class EnversRevisionRepositoryImpl entityInformation, - RevisionEntityInformation revisionEntityInformation, EntityManager entityManager) { + RevisionEntityInformation revisionEntityInformation, EntityManager entityManager) { Assert.notNull(revisionEntityInformation, "RevisionEntityInformation must not be null!"); @@ -136,7 +139,6 @@ public Revisions findRevisions(ID id) { return Revisions.of(revisionList); } - private AuditOrder mapRevisionSort(RevisionSort revisionSort) { return RevisionSort.getRevisionDirection(revisionSort).isDescending() // @@ -154,9 +156,9 @@ private List mapPropertySort(Sort sort) { for (Sort.Order order : sort) { AuditProperty property = AuditEntity.property(order.getProperty()); - AuditOrder auditOrder = order.getDirection().isAscending() ? - property.asc() : - property.desc(); + AuditOrder auditOrder = order.getDirection().isAscending() // + ? property.asc() // + : property.desc(); result.add(auditOrder); } @@ -169,9 +171,9 @@ public Page> findRevisions(ID id, Pageable pageable) { AuditQuery baseQuery = createBaseQuery(id); - List orderMapped = (pageable.getSort() instanceof RevisionSort revisionSort) ? - Collections.singletonList(mapRevisionSort(revisionSort)) : - mapPropertySort(pageable.getSort()); + List orderMapped = (pageable.getSort()instanceof RevisionSort revisionSort) + ? List.of(mapRevisionSort(revisionSort)) + : mapPropertySort(pageable.getSort()); orderMapped.forEach(baseQuery::addOrder); @@ -235,17 +237,17 @@ RevisionMetadata createRevisionMetadata() { return metadata instanceof DefaultRevisionEntity defaultRevisionEntity // ? new DefaultRevisionMetadata(defaultRevisionEntity, revisionType) // : new AnnotationRevisionMetadata<>(Hibernate.unproxy(metadata), RevisionNumber.class, RevisionTimestamp.class, - revisionType); + revisionType); } private static RevisionMetadata.RevisionType convertRevisionType(RevisionType datum) { - return switch (datum) { - case ADD -> INSERT; - case MOD -> UPDATE; - case DEL -> DELETE; - default -> UNKNOWN; - }; + return switch (datum) { + case ADD -> INSERT; + case MOD -> UPDATE; + case DEL -> DELETE; + default -> UNKNOWN; + }; } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java index 98e65c2d96..ccecfb52a4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -26,6 +26,8 @@ * @author Oliver Gierke * @author Mark Paluch * @author Jens Schauder + * @author Donghun Shin + * @author Greg Turnquist * @since 1.10.2 * @soundtrack Benny Greb - Soulfood (Live, https://www.youtube.com/watch?v=9_ErMa_CtSw) */ @@ -54,8 +56,8 @@ public static String getHibernateQuery(Object query) { // Try the old way, as it still works in some cases (haven't investigated in which exactly) - if (query instanceof Query) { - return ((Query) query).getQueryString(); + if (query instanceof Query hibernateQuery) { + return hibernateQuery.getQueryString(); } else { throw new IllegalArgumentException("Don't know how to extract the query string from " + query); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java index 10de0aad25..2bd740a458 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/JpaClassUtils.java @@ -28,6 +28,8 @@ * @author Oliver Gierke * @author Christoph Strobl * @author Jens Schauder + * @author Donghun Shin + * @author Greg Turnquist */ abstract class JpaClassUtils { @@ -45,12 +47,9 @@ private JpaClassUtils() {} */ public static boolean isEntityManagerOfType(EntityManager em, String type) { - EntityManager entityManagerToUse = em; - Object delegate = em.getDelegate(); - - if (delegate instanceof EntityManager delegateEntityManager) { - entityManagerToUse = delegateEntityManager; - } + EntityManager entityManagerToUse = em.getDelegate()instanceof EntityManager delegate // + ? delegate // + : em; return isOfType(entityManagerToUse, type, entityManagerToUse.getClass().getClassLoader()); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index 2e345c22a0..4329893e44 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -15,6 +15,14 @@ */ package org.springframework.data.jpa.repository.cdi; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.UnsatisfiedResolutionException; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.enterprise.inject.spi.ProcessBean; +import jakarta.persistence.EntityManager; + import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.HashMap; @@ -24,14 +32,6 @@ import java.util.Optional; import java.util.Set; -import jakarta.enterprise.event.Observes; -import jakarta.enterprise.inject.UnsatisfiedResolutionException; -import jakarta.enterprise.inject.spi.AfterBeanDiscovery; -import jakarta.enterprise.inject.spi.Bean; -import jakarta.enterprise.inject.spi.BeanManager; -import jakarta.enterprise.inject.spi.ProcessBean; -import jakarta.persistence.EntityManager; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.data.repository.cdi.CdiRepositoryBean; @@ -44,6 +44,7 @@ * @author Oliver Gierke * @author Mark Paluch * @author Christoph Strobl + * @author Donghun Shin */ public class JpaRepositoryExtension extends CdiRepositoryExtensionSupport { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java index 13ad183fd2..6c730e7924 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryTransformerSupport.java @@ -18,6 +18,7 @@ * Transformational operations needed to support either {@link HqlQueryTransformer} or {@link JpqlQueryTransformer}. * * @author Greg Turnquist + * @author Donghun Shin * @since 3.1 */ class JpaQueryTransformerSupport { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java index 771d8b5827..f5f7a3fe14 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/Meta.java @@ -27,6 +27,7 @@ * Value object to hold metadata about repository methods. * * @author Greg Turnquist + * @author Donghun Shin * @since 3.0 * @see org.springframework.data.jpa.repository.Meta */ diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 52cc951561..0b1d9a0709 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -18,7 +18,12 @@ import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.ParameterExpression; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -46,6 +51,8 @@ * @author Jens Schauder * @author Andrey Kovalev * @author Yuriy Tsarkov + * @author Donghun Shin + * @author Greg Turnquist */ class ParameterMetadataProvider { @@ -275,7 +282,6 @@ private static Collection toCollection(@Nullable Object value) { } if (value instanceof Collection collection) { - return collection.isEmpty() ? null : collection; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 0b781b33d4..021e375807 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -80,6 +80,7 @@ * @author Simon Paradies * @author Vladislav Yukharin * @author Chris Fraser + * @author Donghun Shin */ public abstract class QueryUtils { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java index cf3ed03ff0..6e20a4b347 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/EntityManagerBeanDefinitionRegistrarPostProcessor.java @@ -43,6 +43,7 @@ * @author Oliver Gierke * @author Réda Housni Alaoui * @author Mark Paluch + * @author Donghun Shin */ public class EntityManagerBeanDefinitionRegistrarPostProcessor implements BeanFactoryPostProcessor, Ordered { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 9cc1017a47..ee832ce71a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -48,6 +48,7 @@ * @author Mark Paluch * @author Christoph Strobl * @author Marcus Voltolim + * @author Donghun Shin */ public class Querydsl { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java index 322cf3095d..6f74e8c563 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java @@ -18,6 +18,8 @@ import static java.util.Arrays.*; import static org.springframework.beans.factory.BeanFactoryUtils.*; +import jakarta.persistence.EntityManagerFactory; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -25,8 +27,6 @@ import java.util.List; import java.util.Set; -import jakarta.persistence.EntityManagerFactory; - import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -43,6 +43,7 @@ * * @author Oliver Gierke * @author Mark Paluch + * @author Donghun Shin */ public final class BeanDefinitionUtils { From 895d60c969edb193a7adeb085bd4882e377e3fd9 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Fri, 8 Sep 2023 08:04:19 +0200 Subject: [PATCH 465/821] Upgrade Hibernate 6.3 build profile to enable integration builds against latest snapshots. Related ticket: #3146. --- Jenkinsfile | 22 ++-------------------- pom.xml | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2ac291af20..7b7d4d340e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -54,7 +54,7 @@ pipeline { } parallel { - stage("test: java.next (hibernate 6.1)") { + stage("test: baseline (hibernate 6.3.x snapshots)") { agent { label 'data' } @@ -66,25 +66,7 @@ pipeline { steps { script { docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { - sh 'PROFILE=all-dbs,hibernate-61 ci/test.sh' - sh "ci/clean.sh" - } - } - } - } - stage("test: baseline (hibernate 6.3)") { - agent { - label 'data' - } - options { timeout(time: 30, unit: 'MINUTES')} - environment { - ARTIFACTORY = credentials("${p['artifactory.credentials']}") - TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' - } - steps { - script { - docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { - sh 'PROFILE=all-dbs,hibernate-63 ci/test.sh' + sh 'PROFILE=all-dbs,hibernate-63-next ci/test.sh' sh "ci/clean.sh" } } diff --git a/pom.xml b/pom.xml index d63ed1c14b..6a4dd07ff5 100644 --- a/pom.xml +++ b/pom.xml @@ -53,16 +53,25 @@ - hibernate-61 + hibernate-63 - 6.1.7.Final + 6.3.0.Final - hibernate-63 + hibernate-63-next - 6.3.0.CR1 + 6.3.1-SNAPSHOT + + + sonatype-oss + https://oss.sonatype.org/content/repositories/snapshots + + false + + + all-dbs From 065500d9815c0377ad5ee1ab30f43a97c3cbf6f0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 8 Sep 2023 09:21:09 +0200 Subject: [PATCH 466/821] Delete docs build trigger. See #3123 --- .github/workflows/deploy-docs.yml | 33 ------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 .github/workflows/deploy-docs.yml diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml deleted file mode 100644 index 1435fc2171..0000000000 --- a/.github/workflows/deploy-docs.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy Docs -on: - push: - branches-ignore: [ gh-pages ] - tags: '**' - repository_dispatch: - types: request-build-reference # legacy - #schedule: - #- cron: '0 10 * * *' # Once per day at 10am UTC - workflow_dispatch: -permissions: - actions: write -jobs: - build: - runs-on: ubuntu-latest - # FIXME enable when pushed to spring-projects - # if: github.repository_owner == 'spring-projects' - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: docs-build - fetch-depth: 1 - - name: Dispatch (partial build) - if: github.ref_type == 'branch' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }} - - name: Dispatch (full build) - if: github.ref_type == 'tag' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) From 0aee918c79588e0084503be33afab326c2e07eef Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 8 Sep 2023 10:39:09 +0200 Subject: [PATCH 467/821] Polishing. Refine navigation structure. See #3080 --- .gitignore | 3 +- README.adoc | 15 +-- src/main/antora/antora-playbook.yml | 2 +- src/main/antora/modules/ROOT/nav.adoc | 27 ++-- src/main/antora/modules/ROOT/pages/jpa.adoc | 2 +- .../modules/ROOT/pages/jpa/configuration.adoc | 126 ----------------- .../ROOT/pages/jpa/getting-started.adoc | 67 +++++++++ .../modules/ROOT/pages/jpa/misc-context.adoc | 27 ---- .../modules/ROOT/pages/jpa/query-methods.adoc | 10 +- .../introduction.adoc => repositories.adoc} | 0 .../pages/repositories/create-instances.adoc | 127 +++++++++++++++++- .../repositories/custom-implementations.adoc | 28 ++++ .../{ => repositories}/query-by-example.adoc | 4 +- 13 files changed, 251 insertions(+), 187 deletions(-) delete mode 100644 src/main/antora/modules/ROOT/pages/jpa/configuration.adoc create mode 100644 src/main/antora/modules/ROOT/pages/jpa/getting-started.adoc delete mode 100644 src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc rename src/main/antora/modules/ROOT/pages/{repositories/introduction.adoc => repositories.adoc} (100%) rename src/main/antora/modules/ROOT/pages/{ => repositories}/query-by-example.adoc (94%) diff --git a/.gitignore b/.gitignore index 7cdfa7a0f4..e01b6ea607 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ target/ node_modules package-lock.json package.json -node \ No newline at end of file +node +build/ diff --git a/README.adoc b/README.adoc index bf8882d648..276da0427c 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,3 @@ -image:https://spring.io/badges/spring-data-jpa/ga.svg[Spring Data JPA,link=https://projects.spring.io/spring-data-jpa/#quick-start] -image:https://spring.io/badges/spring-data-jpa/snapshot.svg[Spring Data JPA,link=https://projects.spring.io/spring-data-jpa/#quick-start] - = Spring Data JPA image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] Spring Data JPA, part of the larger https://projects.spring.io/spring-data[Spring Data] family, makes it easy to implement JPA-based repositories. @@ -104,7 +101,7 @@ Add the Maven dependency: org.springframework.data spring-data-jpa - ${version}.RELEASE + ${version} ---- @@ -115,7 +112,7 @@ If you'd rather like the latest snapshots of the upcoming major version, use our org.springframework.data spring-data-jpa - ${version}.BUILD-SNAPSHOT + ${version}-SNAPSHOT @@ -130,7 +127,7 @@ If you'd rather like the latest snapshots of the upcoming major version, use our Having trouble with Spring Data? We’d love to help! * Check the -https://docs.spring.io/spring-data/jpa/docs/current/reference/html/[reference documentation], and https://docs.spring.io/spring-data/jpa/docs/current/api/[Javadocs]. +https://docs.spring.io/spring-data/jpa/reference/[reference documentation], and https://docs.spring.io/spring-data/jpa/docs/current/api/[Javadocs]. * Learn the Spring basics – Spring Data builds on Spring Framework, check the https://spring.io[spring.io] web-site for a wealth of reference documentation. If you are just starting out with Spring, try one of the https://spring.io/guides[guides]. * If you are upgrading, check out the https://github.com/spring-projects/spring-data-jpa/releases[Spring Data JPA release notes] and scroll down to the one you're considering. See the details there. (Also check out the https://github.com/spring-projects/spring-data-jpa/releases/latest[latest stable release]) @@ -159,7 +156,7 @@ You also need JDK 17 or above. $ ./mvnw clean install ---- -If you want to build with the regular `mvn` command, you will need https://maven.apache.org/run-maven/index.html[Maven v3.5.0 or above]. +If you want to build with the regular `mvn` command, you will need https://maven.apache.org/run-maven/index.html[Maven v3.8.0 or above]. _Also see link:CONTRIBUTING.adoc[CONTRIBUTING.adoc] if you wish to submit pull requests, and in particular please sign the https://cla.pivotal.io/sign/spring[Contributor’s Agreement] before your first non-trivial change._ @@ -169,10 +166,10 @@ Building the documentation builds also the project without running tests. [source,bash] ---- - $ ./mvnw clean install -Pdistribute + $ ./mvnw clean install -Pantora ---- -The generated documentation is available from `target/site/reference/html/index.html`. +The generated documentation is available from `target/antora/site/index.html`. == Guides diff --git a/src/main/antora/antora-playbook.yml b/src/main/antora/antora-playbook.yml index d7c13a3d45..db6ae1d713 100644 --- a/src/main/antora/antora-playbook.yml +++ b/src/main/antora/antora-playbook.yml @@ -8,7 +8,7 @@ antora: root_component_name: 'data-jpa' site: title: Spring Data JPA - url: https://docs.spring.io/spring-data-jpa/reference/ + url: https://docs.spring.io/spring-data/jpa/reference/ content: sources: - url: ./../../.. diff --git a/src/main/antora/modules/ROOT/nav.adoc b/src/main/antora/modules/ROOT/nav.adoc index 1578f89bea..5a0deada15 100644 --- a/src/main/antora/modules/ROOT/nav.adoc +++ b/src/main/antora/modules/ROOT/nav.adoc @@ -1,34 +1,35 @@ * xref:index.adoc[Overview] ** xref:commons/upgrade.adoc[] -* xref:repositories/introduction.adoc[] + +* xref:jpa.adoc[] +** xref:jpa/getting-started.adoc[] ** xref:repositories/core-concepts.adoc[] ** xref:repositories/definition.adoc[] ** xref:repositories/create-instances.adoc[] -** xref:repositories/query-methods-details.adoc[] -** xref:repositories/projections.adoc[] -** xref:repositories/custom-implementations.adoc[] -** xref:repositories/core-domain-events.adoc[] -** xref:repositories/core-extensions.adoc[] -** xref:repositories/null-handling.adoc[] -** xref:repositories/query-keywords-reference.adoc[] -** xref:repositories/query-return-types-reference.adoc[] -* xref:jpa.adoc[] -** xref:jpa/configuration.adoc[] ** xref:jpa/entity-persistence.adoc[] +** xref:repositories/query-methods-details.adoc[] ** xref:jpa/query-methods.adoc[] +** xref:repositories/projections.adoc[] ** xref:jpa/stored-procedures.adoc[] ** xref:jpa/specifications.adoc[] -** xref:query-by-example.adoc[] +** xref:repositories/query-by-example.adoc[] ** xref:jpa/transactions.adoc[] ** xref:jpa/locking.adoc[] ** xref:auditing.adoc[] -** xref:jpa/misc-context.adoc[] ** xref:jpa/misc-merging-persistence-units.adoc[] ** xref:jpa/jpd-misc-cdi-integration.adoc[] +** xref:repositories/custom-implementations.adoc[] +** xref:repositories/core-domain-events.adoc[] +** xref:repositories/null-handling.adoc[] +** xref:repositories/core-extensions.adoc[] +** xref:repositories/query-keywords-reference.adoc[] +** xref:repositories/query-return-types-reference.adoc[] ** xref:jpa/faq.adoc[] ** xref:jpa/glossary.adoc[] + * xref:envers.adoc[] ** xref:envers/introduction.adoc[] ** xref:envers/configuration.adoc[] ** xref:envers/usage.adoc[] + * https://github.com/spring-projects/spring-data-commons/wiki[Wiki] diff --git a/src/main/antora/modules/ROOT/pages/jpa.adoc b/src/main/antora/modules/ROOT/pages/jpa.adoc index b490dfcaa6..e527d59f69 100644 --- a/src/main/antora/modules/ROOT/pages/jpa.adoc +++ b/src/main/antora/modules/ROOT/pages/jpa.adoc @@ -2,5 +2,5 @@ = JPA :page-section-summary-toc: 1 -This chapter points out the specialties for repository support for JPA. This builds on the core repository support explained in xref:repositories/introduction.adoc[Working with Spring Data Repositories]. Make sure you have a sound understanding of the basic concepts explained there. +This chapter points out the specialties for repository support for JPA. This builds on the core repository support explained in xref:repositories.adoc[Working with Spring Data Repositories]. Make sure you have a sound understanding of the basic concepts explained there. diff --git a/src/main/antora/modules/ROOT/pages/jpa/configuration.adoc b/src/main/antora/modules/ROOT/pages/jpa/configuration.adoc deleted file mode 100644 index 5307e4e986..0000000000 --- a/src/main/antora/modules/ROOT/pages/jpa/configuration.adoc +++ /dev/null @@ -1,126 +0,0 @@ -[[jpa.configuration]] -= Configuration - -This section describes configuring Spring Data JPA through either: - -* "`<>`" (XML configuration) -* "`<>`" (Java configuration) - -[[jpa.java-config]] -== Annotation-based Configuration -The Spring Data JPA repositories support can be activated through both JavaConfig as well as a custom XML namespace, as shown in the following example: - -.Spring Data JPA repositories using JavaConfig -==== -[source, java] ----- -@Configuration -@EnableJpaRepositories -@EnableTransactionManagement -class ApplicationConfig { - - @Bean - public DataSource dataSource() { - - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.HSQL).build(); - } - - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - - HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); - vendorAdapter.setGenerateDdl(true); - - LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); - factory.setJpaVendorAdapter(vendorAdapter); - factory.setPackagesToScan("com.acme.domain"); - factory.setDataSource(dataSource()); - return factory; - } - - @Bean - public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { - - JpaTransactionManager txManager = new JpaTransactionManager(); - txManager.setEntityManagerFactory(entityManagerFactory); - return txManager; - } -} ----- -==== -NOTE: You must create `LocalContainerEntityManagerFactoryBean` and not `EntityManagerFactory` directly, since the former also participates in exception translation mechanisms in addition to creating `EntityManagerFactory`. - -The preceding configuration class sets up an embedded HSQL database by using the `EmbeddedDatabaseBuilder` API of `spring-jdbc`. Spring Data then sets up an `EntityManagerFactory` and uses Hibernate as the sample persistence provider. The last infrastructure component declared here is the `JpaTransactionManager`. Finally, the example activates Spring Data JPA repositories by using the `@EnableJpaRepositories` annotation, which essentially carries the same attributes as the XML namespace. If no base package is configured, it uses the one in which the configuration class resides. - -[[jpa.namespace]] -== Spring Namespace - -The JPA module of Spring Data contains a custom namespace that allows defining repository beans. It also contains certain features and element attributes that are special to JPA. Generally, the JPA repositories can be set up by using the `repositories` element, as shown in the following example: - -.Setting up JPA repositories by using the namespace -==== -[source, xml] ----- - - - - - - ----- -==== - -TIP: Which is better, JavaConfig or XML? XML is how Spring was configured long ago. In today's era of fast-growing Java, record types, annotations, and more, new projects typically use as much pure Java as possible. While there is no immediate plan to remove XML support, some of the newest features MAY not be available through XML. - -Using the `repositories` element looks up Spring Data repositories as described in xref:repositories/create-instances.adoc[Creating Repository Instances]. Beyond that, it activates persistence exception translation for all beans annotated with `@Repository`, to let exceptions being thrown by the JPA persistence providers be converted into Spring's `DataAccessException` hierarchy. - -[[jpa.namespace.custom-namespace-attributes]] -=== Custom Namespace Attributes -Beyond the default attributes of the `repositories` element, the JPA namespace offers additional attributes to let you gain more detailed control over the setup of the repositories: - -.Custom JPA-specific attributes of the `repositories` element -[options = "autowidth"] -|=============== -|`entity-manager-factory-ref`|Explicitly wire the `EntityManagerFactory` to be used with the repositories being detected by the `repositories` element. Usually used if multiple `EntityManagerFactory` beans are used within the application. If not configured, Spring Data automatically looks up the `EntityManagerFactory` bean with the name `entityManagerFactory` in the `ApplicationContext`. -|`transaction-manager-ref`|Explicitly wire the `PlatformTransactionManager` to be used with the repositories being detected by the `repositories` element. Usually only necessary if multiple transaction managers or `EntityManagerFactory` beans have been configured. Default to a single defined `PlatformTransactionManager` inside the current `ApplicationContext`. -|=============== - -NOTE: Spring Data JPA requires a `PlatformTransactionManager` bean named `transactionManager` to be present if no explicit `transaction-manager-ref` is defined. - -[[jpa.bootstrap-mode]] -== Bootstrap Mode - -By default, Spring Data JPA repositories are default Spring beans. -They are singleton scoped and eagerly initialized. -During startup, they already interact with the JPA `EntityManager` for verification and metadata analysis purposes. -Spring Framework supports the initialization of the JPA `EntityManagerFactory` in a background thread because that process usually takes up a significant amount of startup time in a Spring application. -To make use of that background initialization effectively, we need to make sure that JPA repositories are initialized as late as possible. - -As of Spring Data JPA 2.1 you can now configure a `BootstrapMode` (either via the `@EnableJpaRepositories` annotation or the XML namespace) that takes the following values: - -* `DEFAULT` (default) -- Repositories are instantiated eagerly unless explicitly annotated with `@Lazy`. -The lazification only has effect if no client bean needs an instance of the repository as that will require the initialization of the repository bean. -* `LAZY` -- Implicitly declares all repository beans lazy and also causes lazy initialization proxies to be created to be injected into client beans. -That means, that repositories will not get instantiated if the client bean is simply storing the instance in a field and not making use of the repository during initialization. -Repository instances will be initialized and verified upon first interaction with the repository. -* `DEFERRED` -- Fundamentally the same mode of operation as `LAZY`, but triggering repository initialization in response to an `ContextRefreshedEvent` so that repositories are verified before the application has completely started. - -[[jpa.bootstrap-mode.recommendations]] -=== Recommendations - -If you're not using asynchronous JPA bootstrap stick with the default bootstrap mode. - -In case you bootstrap JPA asynchronously, `DEFERRED` is a reasonable default as it will make sure the Spring Data JPA bootstrap only waits for the `EntityManagerFactory` setup if that itself takes longer than initializing all other application components. -Still, it makes sure that repositories are properly initialized and validated before the application signals it's up. - -`LAZY` is a decent choice for testing scenarios and local development. -Once you are pretty sure that repositories can properly bootstrap, or in cases where you are testing other parts of the application, running verification for all repositories might unnecessarily increase the startup time. -The same applies to local development in which you only access parts of the application that might need to have a single repository initialized. - diff --git a/src/main/antora/modules/ROOT/pages/jpa/getting-started.adoc b/src/main/antora/modules/ROOT/pages/jpa/getting-started.adoc new file mode 100644 index 0000000000..2608237cc1 --- /dev/null +++ b/src/main/antora/modules/ROOT/pages/jpa/getting-started.adoc @@ -0,0 +1,67 @@ +[[jpa.getting-started]] += Getting Started + +An easy way to bootstrap setting up a working environment is to create a Spring-based project via https://start.spring.io/#!type=maven-project&dependencies=h2,data-jpa[start.spring.io] or create a Spring project in https://spring.io/tools[Spring Tools]. + +[[jpa.examples-repo]] +== Examples Repository + +The GitHub https://github.com/spring-projects/spring-data-examples[spring-data-examples repository] hosts several examples that you can download and play around with to get a feel for how the library works. + +[[redis.hello-world]] +== Hello World + +Let's start with a simple entity and its corresponding repository: + +[source,java] +---- +@Entity +class Person { + + @Id @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + private String name; + + // getters and setters ommited for brevity +} + +interface PersonRepository extends Repository { + + Person save(Person person); + + Optional findById(long id); +} +---- + +Create the main application to run, as the following example shows: + +[source,java] +---- +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + + @Bean + CommandLineRunner runner(PersonRepository repository) { + return args -> { + + Person person = new Person(); + person.setName("John"); + + repository.save(person); + Person saved = repository.findById(person.getId()).orElseThrow(NoSuchElementException::new); + }; + } +} +---- + +Even in this simple example, there are a few notable things to point out: + +* Repository instances are automatically implemented. +When used as parameters of `@Bean` methods, these will be autowired without further need for annotations. +* The basic repository extends `Repository`. +We suggest to consider how much API surface you want to expose towards your application. +More complex repository interfaces are `ListCrudRepository` or `JpaRepository`. diff --git a/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc b/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc deleted file mode 100644 index a12840ad44..0000000000 --- a/src/main/antora/modules/ROOT/pages/jpa/misc-context.adoc +++ /dev/null @@ -1,27 +0,0 @@ -[[jpa.misc.jpa-context]] -= Using `JpaContext` in Custom Implementations - -When working with multiple `EntityManager` instances and xref:repositories/custom-implementations.adoc#repositories.custom-implementations[custom repository implementations], you need to wire the correct `EntityManager` into the repository implementation class. You can do so by explicitly naming the `EntityManager` in the `@PersistenceContext` annotation or, if the `EntityManager` is `@Autowired`, by using `@Qualifier`. - -As of Spring Data JPA 1.9, Spring Data JPA includes a class called `JpaContext` that lets you obtain the `EntityManager` by managed domain class, assuming it is managed by only one of the `EntityManager` instances in the application. The following example shows how to use `JpaContext` in a custom repository: - -.Using `JpaContext` in a custom repository implementation -==== -[source, java] ----- -class UserRepositoryImpl implements UserRepositoryCustom { - - private final EntityManager em; - - @Autowired - public UserRepositoryImpl(JpaContext context) { - this.em = context.getEntityManagerByManagedType(User.class); - } - - … -} ----- -==== - -The advantage of this approach is that, if the domain type gets assigned to a different persistence unit, the repository does not have to be touched to alter the reference to the persistence unit. - diff --git a/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc index 1b59f44ad3..00e4c203e3 100644 --- a/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc +++ b/src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc @@ -1,5 +1,5 @@ [[jpa.query-methods]] -= Query Methods += JPA Query Methods This section describes the various ways to create a query with Spring Data JPA. @@ -357,9 +357,9 @@ This is a lighter variant than paging because it does not require the total resu 3. <>. This method avoids https://use-the-index-luke.com/no-offset[the shortcomings of offset-based result retrieval by leveraging database indexes]. -Read more on xref:repositories/query-methods-details.adoc#repositories.scrolling.guidance,which method to use best>> for your particular arrangement. +Read more on <> for your particular arrangement. -You can use the Scroll API with query methods, xref:query-by-example.adoc[Query-by-Example], and xref:repositories/core-extensions.adoc#core.extensions.querydsl[Querydsl]. +You can use the Scroll API with query methods, xref:repositories/query-by-example.adoc[Query-by-Example], and xref:repositories/core-extensions.adoc#core.extensions.querydsl[Querydsl]. NOTE: Scrolling with String-based query methods is not yet supported. Scrolling is also not supported using stored `@Procedure` query methods. @@ -432,7 +432,7 @@ Another use case for the `#{#entityName}` expression in a query string is if you @MappedSuperclass public abstract class AbstractMappedType { … - String attribute + String attribute; } @Entity @@ -507,7 +507,7 @@ But sometimes, your query may simply be too complicated for the techniques offer In that situation, consider: * If you haven't already, simply write the query yourself using xref:jpa/query-methods.adoc#jpa.query-methods.at-query[`@Query`]. -* If that doesn't fit your needs, consider implementing a xref:repositories/custom-implementations.adoc#repositories.custom-implementations[custom implementation]. This lets you register a method in your repository while leaving the implementation completely up to you. This gives you the ability to: +* If that doesn't fit your needs, consider implementing a xref:repositories/custom-implementations.adoc[custom implementation]. This lets you register a method in your repository while leaving the implementation completely up to you. This gives you the ability to: ** Talk directly to the `EntityManager` (writing pure HQL/JPQL/EQL/native SQL or using the *Criteria API*) ** Leverage Spring Framework's `JdbcTemplate` (native SQL) ** Use another 3rd-party database toolkit. diff --git a/src/main/antora/modules/ROOT/pages/repositories/introduction.adoc b/src/main/antora/modules/ROOT/pages/repositories.adoc similarity index 100% rename from src/main/antora/modules/ROOT/pages/repositories/introduction.adoc rename to src/main/antora/modules/ROOT/pages/repositories.adoc diff --git a/src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc b/src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc index 2ae01801b1..a1064f9546 100644 --- a/src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc +++ b/src/main/antora/modules/ROOT/pages/repositories/create-instances.adoc @@ -1 +1,126 @@ -include::{commons}@data-commons::page$repositories/create-instances.adoc[] +[[jpa.configuration]] += Configuration + +This section describes configuring Spring Data JPA through either: + +* "`<>`" (Java configuration) +* "`<>`" (XML configuration) + +[[jpa.java-config]] +== Annotation-based Configuration +The Spring Data JPA repositories support can be activated through both JavaConfig as well as a custom XML namespace, as shown in the following example: + +.Spring Data JPA repositories using JavaConfig +==== +[source, java] +---- +@Configuration +@EnableJpaRepositories +@EnableTransactionManagement +class ApplicationConfig { + + @Bean + public DataSource dataSource() { + + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); + return builder.setType(EmbeddedDatabaseType.HSQL).build(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + vendorAdapter.setGenerateDdl(true); + + LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); + factory.setJpaVendorAdapter(vendorAdapter); + factory.setPackagesToScan("com.acme.domain"); + factory.setDataSource(dataSource()); + return factory; + } + + @Bean + public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + + JpaTransactionManager txManager = new JpaTransactionManager(); + txManager.setEntityManagerFactory(entityManagerFactory); + return txManager; + } +} +---- +==== +NOTE: You must create `LocalContainerEntityManagerFactoryBean` and not `EntityManagerFactory` directly, since the former also participates in exception translation mechanisms in addition to creating `EntityManagerFactory`. + +The preceding configuration class sets up an embedded HSQL database by using the `EmbeddedDatabaseBuilder` API of `spring-jdbc`. Spring Data then sets up an `EntityManagerFactory` and uses Hibernate as the sample persistence provider. The last infrastructure component declared here is the `JpaTransactionManager`. Finally, the example activates Spring Data JPA repositories by using the `@EnableJpaRepositories` annotation, which essentially carries the same attributes as the XML namespace. If no base package is configured, it uses the one in which the configuration class resides. + +[[repositories.create-instances.xml]] +== Spring Namespace + +The JPA module of Spring Data contains a custom namespace that allows defining repository beans. It also contains certain features and element attributes that are special to JPA. Generally, the JPA repositories can be set up by using the `repositories` element, as shown in the following example: + +.Setting up JPA repositories by using the namespace +==== +[source, xml] +---- + + + + + + +---- +==== + +TIP: Which is better, JavaConfig or XML? XML is how Spring was configured long ago. In today's era of fast-growing Java, record types, annotations, and more, new projects typically use as much pure Java as possible. While there is no immediate plan to remove XML support, some of the newest features MAY not be available through XML. + +Using the `repositories` element it activates persistence exception translation for all beans annotated with `@Repository`, to let exceptions being thrown by the JPA persistence providers be converted into Spring's `DataAccessException` hierarchy. + +[[jpa.namespace.custom-namespace-attributes]] +=== Custom Namespace Attributes +Beyond the default attributes of the `repositories` element, the JPA namespace offers additional attributes to let you gain more detailed control over the setup of the repositories: + +.Custom JPA-specific attributes of the `repositories` element +[options = "autowidth"] +|=============== +|`entity-manager-factory-ref`|Explicitly wire the `EntityManagerFactory` to be used with the repositories being detected by the `repositories` element. Usually used if multiple `EntityManagerFactory` beans are used within the application. If not configured, Spring Data automatically looks up the `EntityManagerFactory` bean with the name `entityManagerFactory` in the `ApplicationContext`. +|`transaction-manager-ref`|Explicitly wire the `PlatformTransactionManager` to be used with the repositories being detected by the `repositories` element. Usually only necessary if multiple transaction managers or `EntityManagerFactory` beans have been configured. Default to a single defined `PlatformTransactionManager` inside the current `ApplicationContext`. +|=============== + +NOTE: Spring Data JPA requires a `PlatformTransactionManager` bean named `transactionManager` to be present if no explicit `transaction-manager-ref` is defined. + +[[jpa.bootstrap-mode]] +== Bootstrap Mode + +By default, Spring Data JPA repositories are default Spring beans. +They are singleton scoped and eagerly initialized. +During startup, they already interact with the JPA `EntityManager` for verification and metadata analysis purposes. +Spring Framework supports the initialization of the JPA `EntityManagerFactory` in a background thread because that process usually takes up a significant amount of startup time in a Spring application. +To make use of that background initialization effectively, we need to make sure that JPA repositories are initialized as late as possible. + +As of Spring Data JPA 2.1 you can now configure a `BootstrapMode` (either via the `@EnableJpaRepositories` annotation or the XML namespace) that takes the following values: + +* `DEFAULT` (default) -- Repositories are instantiated eagerly unless explicitly annotated with `@Lazy`. +The lazification only has effect if no client bean needs an instance of the repository as that will require the initialization of the repository bean. +* `LAZY` -- Implicitly declares all repository beans lazy and also causes lazy initialization proxies to be created to be injected into client beans. +That means, that repositories will not get instantiated if the client bean is simply storing the instance in a field and not making use of the repository during initialization. +Repository instances will be initialized and verified upon first interaction with the repository. +* `DEFERRED` -- Fundamentally the same mode of operation as `LAZY`, but triggering repository initialization in response to an `ContextRefreshedEvent` so that repositories are verified before the application has completely started. + +[[jpa.bootstrap-mode.recommendations]] +=== Recommendations + +If you're not using asynchronous JPA bootstrap stick with the default bootstrap mode. + +In case you bootstrap JPA asynchronously, `DEFERRED` is a reasonable default as it will make sure the Spring Data JPA bootstrap only waits for the `EntityManagerFactory` setup if that itself takes longer than initializing all other application components. +Still, it makes sure that repositories are properly initialized and validated before the application signals it's up. + +`LAZY` is a decent choice for testing scenarios and local development. +Once you are pretty sure that repositories can properly bootstrap, or in cases where you are testing other parts of the application, running verification for all repositories might unnecessarily increase the startup time. +The same applies to local development in which you only access parts of the application that might need to have a single repository initialized. + diff --git a/src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc b/src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc index c7615191a6..c5d704ac12 100644 --- a/src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc +++ b/src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc @@ -1 +1,29 @@ include::{commons}@data-commons::page$repositories/custom-implementations.adoc[] + +[[jpa.misc.jpa-context]] +== Using `JpaContext` in Custom Implementations + +When working with multiple `EntityManager` instances and xref:repositories/custom-implementations.adoc#repositories.custom-implementations[custom repository implementations], you need to wire the correct `EntityManager` into the repository implementation class. You can do so by explicitly naming the `EntityManager` in the `@PersistenceContext` annotation or, if the `EntityManager` is `@Autowired`, by using `@Qualifier`. + +As of Spring Data JPA 1.9, Spring Data JPA includes a class called `JpaContext` that lets you obtain the `EntityManager` by managed domain class, assuming it is managed by only one of the `EntityManager` instances in the application. The following example shows how to use `JpaContext` in a custom repository: + +.Using `JpaContext` in a custom repository implementation +==== +[source, java] +---- +class UserRepositoryImpl implements UserRepositoryCustom { + + private final EntityManager em; + + @Autowired + public UserRepositoryImpl(JpaContext context) { + this.em = context.getEntityManagerByManagedType(User.class); + } + + … +} +---- +==== + +The advantage of this approach is that, if the domain type gets assigned to a different persistence unit, the repository does not have to be touched to alter the reference to the persistence unit. + diff --git a/src/main/antora/modules/ROOT/pages/query-by-example.adoc b/src/main/antora/modules/ROOT/pages/repositories/query-by-example.adoc similarity index 94% rename from src/main/antora/modules/ROOT/pages/query-by-example.adoc rename to src/main/antora/modules/ROOT/pages/repositories/query-by-example.adoc index 5f427c29ba..c0d180a281 100644 --- a/src/main/antora/modules/ROOT/pages/query-by-example.adoc +++ b/src/main/antora/modules/ROOT/pages/repositories/query-by-example.adoc @@ -1,6 +1,4 @@ -= Query by Example - -include::{commons}@data-commons::page$query-by-example.adoc[leveloffset=+1] +include::{commons}@data-commons::page$query-by-example.adoc[] [[query-by-example.running]] In Spring Data JPA, you can use Query by Example with Repositories, as shown in the following example: From f009e42c4f4964512f76509ef2d44d3b0312a45c Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 8 Sep 2023 09:25:24 -0500 Subject: [PATCH 468/821] Polishing. Move version settings into properties to consolidate and ease making updates. Related: #3146 --- pom.xml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6a4dd07ff5..dd2134e5d1 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,10 @@ 4.10.1 3.0.3 + 4.0.2 6.2.4.Final + 6.3.0.Final + 6.3.1-SNAPSHOT 2.7.1

    2.2.220

    4.5 @@ -55,13 +58,13 @@ hibernate-63 - 6.3.0.Final + ${hibernate-next} hibernate-63-next - 6.3.1-SNAPSHOT + ${hibernate-next-snapshots} @@ -113,7 +116,7 @@ eclipselink-next - 4.0.2 + ${eclipselink-next}
    From 4c66e7adb4b0243595ef0a176c8110dfdd627b46 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 8 Sep 2023 09:35:28 -0500 Subject: [PATCH 469/821] Make POWER a valid token as an identifier for JPQL and HQL. See #3143 --- .../org/springframework/data/jpa/repository/query/Hql.g4 | 1 + .../org/springframework/data/jpa/repository/query/Jpql.g4 | 1 + .../data/jpa/repository/query/HqlQueryRendererTests.java | 5 +++++ .../data/jpa/repository/query/JpqlQueryRendererTests.java | 5 +++++ 4 files changed, 12 insertions(+) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 36205292b6..1e80b10fec 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -812,6 +812,7 @@ reservedWord | PERCENT | PLACING | POSITION + | POWER | PRECEDING | QUARTER | RANGE diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 index 271e9d168b..637bac6c34 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Jpql.g4 @@ -605,6 +605,7 @@ identification_variable | NEW | ORDER | OUTER + | POWER | FLOOR | SIGN | TIME diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 0feaf2cdde..39ce36c605 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1600,4 +1600,9 @@ void newShouldBeLegalAsPartOfAStateFieldPathExpression() { ORDER BY j.id """); } + + @Test // GH-3143 + void powerShouldBeLegalInAQuery() { + assertQuery("select e.power.id from MyEntity e"); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index d738ccac37..403f7ffaed 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -983,4 +983,9 @@ void newShouldBeLegalAsPartOfAStateFieldPathExpression() { ORDER BY j.id """); } + + @Test // GH-3143 + void powerShouldBeLegalInAQuery() { + assertQuery("select e.power.id from MyEntity e"); + } } From b077d645dc10ed42c68ec0dd6e00c23817a3b774 Mon Sep 17 00:00:00 2001 From: jeomxon Date: Fri, 18 Aug 2023 22:53:11 +0900 Subject: [PATCH 470/821] Rename em as entityManager in SimpleJpaRepository. See #3120 --- .../support/SimpleJpaRepository.java | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index e9a2d4b204..98d839b645 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -99,7 +99,7 @@ public class SimpleJpaRepository implements JpaRepositoryImplementation entityInformation; - private final EntityManager em; + private final EntityManager entityManager; private final PersistenceProvider provider; private @Nullable CrudMethodMetadata metadata; @@ -117,7 +117,7 @@ public SimpleJpaRepository(JpaEntityInformation entityInformation, EntityM Assert.notNull(entityManager, "EntityManager must not be null"); this.entityInformation = entityInformation; - this.em = entityManager; + this.entityManager = entityManager; this.provider = PersistenceProvider.fromEntityManager(entityManager); } @@ -125,10 +125,10 @@ public SimpleJpaRepository(JpaEntityInformation entityInformation, EntityM * Creates a new {@link SimpleJpaRepository} to manage objects of the given domain type. * * @param domainClass must not be {@literal null}. - * @param em must not be {@literal null}. + * @param entityManager must not be {@literal null}. */ - public SimpleJpaRepository(Class domainClass, EntityManager em) { - this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em); + public SimpleJpaRepository(Class domainClass, EntityManager entityManager) { + this(JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager), entityManager); } /** @@ -188,14 +188,14 @@ public void delete(T entity) { Class type = ProxyUtils.getUserClass(entity); - T existing = (T) em.find(type, entityInformation.getId(entity)); + T existing = (T) entityManager.find(type, entityInformation.getId(entity)); // if the entity to be deleted doesn't exist, delete is a NOOP if (existing == null) { return; } - em.remove(em.contains(entity) ? entity : em.merge(entity)); + entityManager.remove(entityManager.contains(entity) ? entity : entityManager.merge(entity)); } @Override @@ -230,7 +230,7 @@ public void deleteAllByIdInBatch(Iterable ids) { String queryString = String.format(DELETE_ALL_QUERY_BY_ID_STRING, entityInformation.getEntityName(), entityInformation.getIdAttribute().getName()); - Query query = em.createQuery(queryString); + Query query = entityManager.createQuery(queryString); /* * Some JPA providers require {@code ids} to be a {@link Collection} so we must convert if it's not already. @@ -271,7 +271,8 @@ public void deleteAllInBatch(Iterable entities) { return; } - applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em) + applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, + entityManager) .executeUpdate(); } @@ -288,7 +289,7 @@ public void deleteAll() { @Transactional public void deleteAllInBatch() { - Query query = em.createQuery(getDeleteAllQueryString()); + Query query = entityManager.createQuery(getDeleteAllQueryString()); applyQueryHints(query); @@ -303,13 +304,13 @@ public Optional findById(ID id) { Class domainType = getDomainClass(); if (metadata == null) { - return Optional.ofNullable(em.find(domainType, id)); + return Optional.ofNullable(entityManager.find(domainType, id)); } LockModeType type = metadata.getLockModeType(); Map hints = getHints(); - return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints)); + return Optional.ofNullable(type == null ? entityManager.find(domainType, id, hints) : entityManager.find(domainType, id, type, hints)); } @Deprecated @@ -332,7 +333,7 @@ public T getById(ID id) { public T getReferenceById(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); - return em.getReference(getDomainClass(), id); + return entityManager.getReference(getDomainClass(), id); } @Override @@ -349,7 +350,7 @@ public boolean existsById(ID id) { Iterable idAttributeNames = entityInformation.getIdAttributeNames(); String existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames); - TypedQuery query = em.createQuery(existsQuery, Long.class); + TypedQuery query = entityManager.createQuery(existsQuery, Long.class); applyQueryHints(query); @@ -456,20 +457,20 @@ public List findAll(Specification spec, Sort sort) { @Override public boolean exists(Specification spec) { - CriteriaQuery cq = this.em.getCriteriaBuilder() // + CriteriaQuery cq = this.entityManager.getCriteriaBuilder() // .createQuery(Integer.class) // - .select(this.em.getCriteriaBuilder().literal(1)); + .select(this.entityManager.getCriteriaBuilder().literal(1)); applySpecificationToCriteria(spec, getDomainClass(), cq); - TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); + TypedQuery query = applyRepositoryMethodMetadata(this.entityManager.createQuery(cq)); return query.setMaxResults(1).getResultList().size() == 1; } @Override public long delete(Specification spec) { - CriteriaBuilder builder = this.em.getCriteriaBuilder(); + CriteriaBuilder builder = this.entityManager.getCriteriaBuilder(); CriteriaDelete delete = builder.createCriteriaDelete(getDomainClass()); if (spec != null) { @@ -480,7 +481,7 @@ public long delete(Specification spec) { } } - return this.em.createQuery(delete).executeUpdate(); + return this.entityManager.createQuery(delete).executeUpdate(); } @Override @@ -522,7 +523,7 @@ private R doFindBy(Specification spec, Class domainClass, SpecificationScrollDelegate scrollDelegate = new SpecificationScrollDelegate<>(scrollFunction, entityInformation); FetchableFluentQuery fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass, finder, - scrollDelegate, this::count, this::exists, this.em); + scrollDelegate, this::count, this::exists, this.entityManager); return queryFunction.apply((FetchableFluentQuery) fluentQuery); } @@ -549,13 +550,13 @@ public long count(Example example) { public boolean exists(Example example) { Specification spec = new ExampleSpecification<>(example, this.escapeCharacter); - CriteriaQuery cq = this.em.getCriteriaBuilder() // + CriteriaQuery cq = this.entityManager.getCriteriaBuilder() // .createQuery(Integer.class) // - .select(this.em.getCriteriaBuilder().literal(1)); + .select(this.entityManager.getCriteriaBuilder().literal(1)); applySpecificationToCriteria(spec, example.getProbeType(), cq); - TypedQuery query = applyRepositoryMethodMetadata(this.em.createQuery(cq)); + TypedQuery query = applyRepositoryMethodMetadata(this.entityManager.createQuery(cq)); return query.setMaxResults(1).getResultList().size() == 1; } @@ -595,7 +596,7 @@ public R findBy(Example example, Function query = em.createQuery(getCountQueryString(), Long.class); + TypedQuery query = entityManager.createQuery(getCountQueryString(), Long.class); applyQueryHintsForCount(query); @@ -614,10 +615,10 @@ public S save(S entity) { Assert.notNull(entity, "Entity must not be null"); if (entityInformation.isNew(entity)) { - em.persist(entity); + entityManager.persist(entity); return entity; } else { - return em.merge(entity); + return entityManager.merge(entity); } } @@ -659,7 +660,7 @@ public List saveAllAndFlush(Iterable entities) { @Transactional @Override public void flush() { - em.flush(); + entityManager.flush(); } /** @@ -742,7 +743,7 @@ protected TypedQuery getQuery(@Nullable Specification spec, Sort sort) { */ protected TypedQuery getQuery(@Nullable Specification spec, Class domainClass, Sort sort) { - CriteriaBuilder builder = em.getCriteriaBuilder(); + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(domainClass); Root root = applySpecificationToCriteria(spec, domainClass, query); @@ -752,7 +753,7 @@ protected TypedQuery getQuery(@Nullable Specification spec, query.orderBy(toOrders(sort, root, builder)); } - return applyRepositoryMethodMetadata(em.createQuery(query)); + return applyRepositoryMethodMetadata(entityManager.createQuery(query)); } /** @@ -774,7 +775,7 @@ protected TypedQuery getCountQuery(@Nullable Specification spec) { */ protected TypedQuery getCountQuery(@Nullable Specification spec, Class domainClass) { - CriteriaBuilder builder = em.getCriteriaBuilder(); + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(Long.class); Root root = applySpecificationToCriteria(spec, domainClass, query); @@ -788,7 +789,7 @@ protected TypedQuery getCountQuery(@Nullable Specification Root applySpecificationToCriteria(@Nullable Specific return root; } - CriteriaBuilder builder = em.getCriteriaBuilder(); + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); Predicate predicate = spec.toPredicate(root, query, builder); if (predicate != null) { @@ -855,7 +856,7 @@ private void applyQueryHints(Query query) { return; } - getQueryHints().withFetchGraphs(em).forEach(query::setHint); + getQueryHints().withFetchGraphs(entityManager).forEach(query::setHint); applyComment(metadata, query::setHint); } @@ -884,7 +885,7 @@ private Map getHints() { Map hints = new HashMap<>(); - getQueryHints().withFetchGraphs(em).forEach(hints::put); + getQueryHints().withFetchGraphs(entityManager).forEach(hints::put); if (metadata != null) { applyComment(metadata, hints::put); From c6f9186d70f2753552a6f08126623505b41f5ce4 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 24 Jul 2023 14:08:31 -0500 Subject: [PATCH 471/821] Fetch HQL from Hibernate using a different API. See #3085 --- .../springframework/data/jpa/provider/HibernateUtils.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java index ccecfb52a4..c5b3ce423e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -48,6 +48,13 @@ public static String getHibernateQuery(Object query) { // Try the new Hibernate implementation first if (query instanceof SqmQuery sqmQuery) { + + String hql = sqmQuery.getQueryString(); + + if (!hql.equals("")) { + return hql; + } + return sqmQuery.getSqmStatement().toHqlString(); } @@ -55,7 +62,6 @@ public static String getHibernateQuery(Object query) { } catch (RuntimeException o_O) {} // Try the old way, as it still works in some cases (haven't investigated in which exactly) - if (query instanceof Query hibernateQuery) { return hibernateQuery.getQueryString(); } else { From 0277f079d2be9463ba9df2ca663dca80ad164377 Mon Sep 17 00:00:00 2001 From: Eric Haag Date: Wed, 30 Aug 2023 10:27:51 -0500 Subject: [PATCH 472/821] Connect build to ge.spring.io. This change publishes a build scan to ge.spring.io for every local build from an authenticated Spring committer and for CI where appropriate access tokens are available. The build will not fail if publishing fails. This change also allows the build to benefit from local and remote build caching, providing faster builds for all contributors. Additionally, the project will have access to all features of Gradle Enterprise such as: - Dashboards to view all historical build scans, along with performance trends over time - Build failure analytics for enhanced investigation and diagnosis of build failures - Test failure analytics to better understand trends and causes around slow, failing, and flaky tests See #3142 --- .gitignore | 1 + .mvn/extensions.xml | 13 +++++++++++++ .mvn/gradle-enterprise.xml | 31 +++++++++++++++++++++++++++++++ Jenkinsfile | 24 ++++++++++++++++++------ README.adoc | 5 ++++- ci/clean.sh | 6 ------ ci/pipeline.properties | 2 ++ ci/test.sh | 12 +++++++++++- pom.xml | 20 ++++++++++++++++++++ 9 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 .mvn/extensions.xml create mode 100644 .mvn/gradle-enterprise.xml delete mode 100755 ci/clean.sh diff --git a/.gitignore b/.gitignore index e01b6ea607..6b743d7650 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ package-lock.json package.json node build/ +.mvn/.gradle-enterprise diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml new file mode 100644 index 0000000000..85a16c3aa2 --- /dev/null +++ b/.mvn/extensions.xml @@ -0,0 +1,13 @@ + + + + com.gradle + gradle-enterprise-maven-extension + 1.18.1 + + + com.gradle + common-custom-user-data-maven-extension + 1.12.2 + + \ No newline at end of file diff --git a/.mvn/gradle-enterprise.xml b/.mvn/gradle-enterprise.xml new file mode 100644 index 0000000000..135c118348 --- /dev/null +++ b/.mvn/gradle-enterprise.xml @@ -0,0 +1,31 @@ + + + + https://ge.spring.io + + + false + true + true + + #{{'0.0.0.0'}} + + + + + true + + + + + spring-builds+jenkins + ${env.GRADLE_ENTERPRISE_CACHE_PASSWORD} + + + true + #{env['GRADLE_ENTERPRISE_CACHE_USERNAME'] != null and env['GRADLE_ENTERPRISE_CACHE_PASSWORD'] != null} + + + \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index 7b7d4d340e..52f4f2cf2f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -32,13 +32,14 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") + GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { sh 'PROFILE=all-dbs ci/test.sh' - sh "ci/clean.sh" } } } @@ -61,13 +62,14 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") + GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { script { docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { sh 'PROFILE=all-dbs,hibernate-63-next ci/test.sh' - sh "ci/clean.sh" } } } @@ -79,13 +81,14 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") + GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { script { docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { sh 'PROFILE=all-dbs ci/test.sh' - sh "ci/clean.sh" } } } @@ -97,13 +100,14 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") + GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { sh 'PROFILE=all-dbs,eclipselink-next ci/test.sh' - sh "ci/clean.sh" } } } @@ -126,19 +130,27 @@ pipeline { environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") + GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") + GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") } steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ' + + 'GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} ' + + 'GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} ' + + 'GRADLE_ENTERPRISE_ACCESS_KEY=${GRADLE_ENTERPRISE_ACCESS_KEY} ' + + './mvnw -s settings.xml -Pci,artifactory ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " + "-Dartifactory.staging-repository=libs-snapshot-local " + "-Dartifactory.build-name=spring-data-jpa " + "-Dartifactory.build-number=${BUILD_NUMBER} " + - '-Dmaven.test.skip=true clean deploy -U -B' + '-Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa-enterprise ' + + '-Dmaven.test.skip=true clean deploy -U -B ' + } } } diff --git a/README.adoc b/README.adoc index 276da0427c..15ecfb1e92 100644 --- a/README.adoc +++ b/README.adoc @@ -1,4 +1,7 @@ -= Spring Data JPA image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] +image:https://spring.io/badges/spring-data-jpa/ga.svg[Spring Data JPA,link=https://projects.spring.io/spring-data-jpa/#quick-start] +image:https://spring.io/badges/spring-data-jpa/snapshot.svg[Spring Data JPA,link=https://projects.spring.io/spring-data-jpa/#quick-start] + += Spring Data JPA image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-jpa%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-jpa/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Gradle Enterprise", link="https://ge.spring.io/scans?search.rootProjectNames=Spring Data JPA Parent"] Spring Data JPA, part of the larger https://projects.spring.io/spring-data[Spring Data] family, makes it easy to implement JPA-based repositories. This module deals with enhanced support for JPA-based data access layers. diff --git a/ci/clean.sh b/ci/clean.sh deleted file mode 100755 index 7b38a05c9c..0000000000 --- a/ci/clean.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -x - -set -euo pipefail - -MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \ - ./mvnw -s settings.xml clean -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 0359247e80..c02f2a1231 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -25,3 +25,5 @@ docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock - docker.registry= docker.credentials=hub.docker.com-springbuildmaster artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c +gradle-enterprise-cache.credentials=gradle_enterprise_cache_user +gradle-enterprise.access-key=gradle_enterprise_secret_access_key diff --git a/ci/test.sh b/ci/test.sh index ffb33bccf5..dfb14eff39 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -3,8 +3,18 @@ set -euo pipefail mkdir -p /tmp/jenkins-home/.m2/spring-data-jpa +mkdir -p /tmp/jenkins-home/.m2/.gradle-enterprise chown -R 1001:1001 . +export GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} +export GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} +export GRADLE_ENTERPRISE_ACCESS_KEY=${GRADLE_ENTERPRISE_ACCESS_KEY} + MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \ ./mvnw -s settings.xml \ - -P${PROFILE} clean dependency:list test -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa \ No newline at end of file + -P${PROFILE} clean dependency:list test -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa + +MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \ + ./mvnw -s settings.xml clean -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa + +chown -R 1001:1001 /tmp/jenkins-home/.m2/.gradle-enterprise diff --git a/pom.xml b/pom.xml index dd2134e5d1..1169772efd 100644 --- a/pom.xml +++ b/pom.xml @@ -221,6 +221,26 @@ + + + + + com.gradle + gradle-enterprise-maven-extension + + + + + + builddef.lst + + + + + + + + From bfbbc3460ef6af1d0276ab08b3d576d5b78a5acc Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 Sep 2023 11:29:20 +0200 Subject: [PATCH 473/821] Prepare 3.2 M3 (2023.1.0). See #3119 --- pom.xml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 1169772efd..e050458a95 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-SNAPSHOT + 3.2.0-M3 @@ -38,7 +38,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-SNAPSHOT + 3.2.0-M3 0.10.3 org.hibernate @@ -244,16 +244,6 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - spring-milestone https://repo.spring.io/milestone From 1b50d23b9fa180b70c4ff167875085bd1e1394a0 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 Sep 2023 11:30:14 +0200 Subject: [PATCH 474/821] Release version 3.2 M3 (2023.1.0). See #3119 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index e050458a95..cdfb3b1df6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M3 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index f239d6394b..218f3379aa 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-SNAPSHOT + 3.2.0-M3 org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M3 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a458953182..4aa22cdc36 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M3 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index b21b03c313..f3fd955e5a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-SNAPSHOT + 3.2.0-M3 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-M3 ../pom.xml From 11ab43501b745af875bb108aefde1530c9eff2c5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 Sep 2023 11:33:20 +0200 Subject: [PATCH 475/821] Prepare next development iteration. See #3119 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index cdfb3b1df6..e050458a95 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M3 + 3.2.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 218f3379aa..f239d6394b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-M3 + 3.2.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.2.0-M3 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 4aa22cdc36..a458953182 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M3 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index f3fd955e5a..b21b03c313 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-M3 + 3.2.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-M3 + 3.2.0-SNAPSHOT ../pom.xml From 6580fd79134bf3e4bc0eb98f359f4a80a84b8ed5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 Sep 2023 11:33:22 +0200 Subject: [PATCH 476/821] After release cleanups. See #3119 --- pom.xml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e050458a95..1169772efd 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-M3 + 3.2.0-SNAPSHOT @@ -38,7 +38,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-M3 + 3.2.0-SNAPSHOT 0.10.3 org.hibernate @@ -244,6 +244,16 @@ + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + spring-milestone https://repo.spring.io/milestone From 7cdf53f184dd030524c998684f3bcdb9aeb89ef2 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 19 Sep 2023 21:36:05 +0200 Subject: [PATCH 477/821] Upgrade build to Hibernate 6.3.1. Fixes #3166. --- pom.xml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 1169772efd..94542470ab 100644 --- a/pom.xml +++ b/pom.xml @@ -30,9 +30,8 @@ 4.10.1 3.0.3 4.0.2 - 6.2.4.Final - 6.3.0.Final - 6.3.1-SNAPSHOT + 6.3.1.Final + 6.3.2-SNAPSHOT 2.7.1

    2.2.220

    4.5 @@ -55,12 +54,6 @@ - - hibernate-63 - - ${hibernate-next} - - hibernate-63-next From 814d214a27c2cadded8e7d0f4ee8f9f2c69c2a82 Mon Sep 17 00:00:00 2001 From: Julia <5765049+sxhinzvc@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:02:47 -0400 Subject: [PATCH 478/821] Restrict `TypedParameterValue` usage to native queries only. Use Hibernate parameter accessor for native queries only to avoid affecting JPQL queries. Co-locate Hibernate-specific parameters accessor as the same package as JPA parameters accessor. Remove Parameter Accessor reference from Persistence Provider since it's created in AbstractJpaQuery. Closes #3137 Original Pull Request: #3173 --- .../jpa/provider/PersistenceProvider.java | 11 ----- .../repository/query/AbstractJpaQuery.java | 7 ++- ...bernateJpaParametersParameterAccessor.java | 4 +- .../query/ParameterMetadataProvider.java | 20 ++++----- .../query/AbstractJpaQueryTests.java | 45 +++++++++++++++++++ ...aParametersParameterAccessorUnitTests.java | 3 +- .../JpaParametersParameterAccessorTests.java | 6 +-- .../ParameterMetadataProviderUnitTests.java | 33 +++++++++++++- .../QueryWithNullLikeIntegrationTests.java | 16 ++++++- 9 files changed, 111 insertions(+), 34 deletions(-) rename spring-data-jpa/src/main/java/org/springframework/data/jpa/{provider => repository/query}/HibernateJpaParametersParameterAccessor.java (95%) rename spring-data-jpa/src/test/java/org/springframework/data/jpa/{provider => repository/query}/HibernateJpaParametersParameterAccessorUnitTests.java (92%) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index fd9eec9e3a..076f447c3d 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -106,12 +106,6 @@ public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { return new HibernateScrollableResultsIterator(jpaQuery); } - @Override - public JpaParametersParameterAccessor getParameterAccessor(JpaParameters parameters, Object[] values, - EntityManager em) { - return new HibernateJpaParametersParameterAccessor(parameters, values, em); - } - @Override public String getCommentHintKey() { return "org.hibernate.comment"; @@ -292,11 +286,6 @@ public static PersistenceProvider fromMetamodel(Metamodel metamodel) { return cacheAndReturn(metamodelType, GENERIC_JPA); } - public JpaParametersParameterAccessor getParameterAccessor(JpaParameters parameters, Object[] values, - EntityManager em) { - return new JpaParametersParameterAccessor(parameters, values); - } - /** * Returns the placeholder to be used for simple count queries. Default implementation returns {@code x}. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 32c5f438b1..42135e104e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -61,6 +61,7 @@ * @author Jens Schauder * @author Сергей Цыпанов * @author Wonchul Heo + * @author Julia Lee */ public abstract class AbstractJpaQuery implements RepositoryQuery { @@ -153,7 +154,11 @@ private Object doExecute(JpaQueryExecution execution, Object[] values) { private JpaParametersParameterAccessor obtainParameterAccessor(Object[] values) { - return provider.getParameterAccessor(method.getParameters(), values, em); + if (method.isNativeQuery() && PersistenceProvider.HIBERNATE.equals(provider)) { + return new HibernateJpaParametersParameterAccessor(method.getParameters(), values, em); + } + + return new JpaParametersParameterAccessor(method.getParameters(), values); } protected JpaQueryExecution getExecution() { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java similarity index 95% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java index d5f153b8ec..4889483bab 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.provider; +package org.springframework.data.jpa.repository.query; import jakarta.persistence.EntityManager; @@ -21,7 +21,6 @@ import org.hibernate.query.TypedParameterValue; import org.hibernate.type.BasicType; import org.hibernate.type.BasicTypeRegistry; -import org.springframework.data.jpa.repository.query.JpaParametersParameterAccessor; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; @@ -38,6 +37,7 @@ * @author Robert Wilson * @author Oliver Drotbohm * @author Greg Turnquist + * @author Julia Lee * @since 2.7 */ class HibernateJpaParametersParameterAccessor extends JpaParametersParameterAccessor { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 0b1d9a0709..cd8f50ad94 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -233,31 +233,27 @@ public boolean isIsNullParameter() { /** * Prepares the object before it's actually bound to the {@link jakarta.persistence.Query;}. * - * @param value must not be {@literal null}. + * @param value can be {@literal null}. */ @Nullable - public Object prepare(Object value) { + public Object prepare(@Nullable Object value) { - Assert.notNull(value, "Value must not be null"); - - Object unwrapped = PersistenceProvider.unwrapTypedParameterValue(value); - - if (unwrapped == null || expression.getJavaType() == null) { - return unwrapped; + if (value == null || expression.getJavaType() == null) { + return value; } if (String.class.equals(expression.getJavaType()) && !noWildcards) { switch (type) { case STARTING_WITH: - return String.format("%s%%", escape.escape(unwrapped.toString())); + return String.format("%s%%", escape.escape(value.toString())); case ENDING_WITH: - return String.format("%%%s", escape.escape(unwrapped.toString())); + return String.format("%%%s", escape.escape(value.toString())); case CONTAINING: case NOT_CONTAINING: - return String.format("%%%s%%", escape.escape(unwrapped.toString())); + return String.format("%%%s%%", escape.escape(value.toString())); default: - return unwrapped; + return value; } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index f2ee0c720b..e51adcf7c0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -16,7 +16,9 @@ package org.springframework.data.jpa.repository.query; import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -36,6 +38,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.EntityGraph; @@ -56,6 +59,7 @@ * @author Thomas Darimont * @author Mark Paluch * @author Krzysztof Krason + * @author Julia Lee */ @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:infrastructure.xml") @@ -65,12 +69,14 @@ class AbstractJpaQueryTests { private Query query; private TypedQuery countQuery; + private JpaQueryExecution execution; @BeforeEach @SuppressWarnings("unchecked") void setUp() { query = mock(Query.class); countQuery = mock(TypedQuery.class); + execution = mock(JpaQueryExecution.class); } @Test // DATADOC-97 @@ -150,6 +156,37 @@ void shouldAddEntityGraphHintForLoad() throws Exception { verify(result).setHint("jakarta.persistence.loadgraph", entityGraph); } + @Test // GH-3137 + void shouldCreateHibernateJpaParameterParametersAccessorForNativeQuery() throws Exception { + + JpaQueryMethod queryMethod = getMethod("findByLastnameNativeQuery", String.class); + + AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); + + jpaQuery.execute(new Object[] {"some last name"}); + + ArgumentCaptor captor = ArgumentCaptor.forClass(JpaParametersParameterAccessor.class); + verify(execution).execute(eq(jpaQuery), captor.capture()); + JpaParametersParameterAccessor parameterAccessor = captor.getValue(); + + assertThat(parameterAccessor).isInstanceOf(HibernateJpaParametersParameterAccessor.class); + } + + @Test // GH-3137 + void shouldCreateGenericJpaParameterParametersAccessorForNonNativeQuery() throws Exception { + + JpaQueryMethod queryMethod = getMethod("findByFirstname", String.class); + AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); + + jpaQuery.execute(new Object[] {"some first name"}); + + ArgumentCaptor captor = ArgumentCaptor.forClass(JpaParametersParameterAccessor.class); + verify(execution).execute(eq(jpaQuery), captor.capture()); + JpaParametersParameterAccessor parameterAccessor = captor.getValue(); + + assertThat(parameterAccessor).isNotInstanceOf(HibernateJpaParametersParameterAccessor.class); + } + private JpaQueryMethod getMethod(String name, Class... parameterTypes) throws Exception { Method method = SampleRepository.class.getMethod(name, parameterTypes); @@ -164,6 +201,9 @@ interface SampleRepository extends Repository { @QueryHints({ @QueryHint(name = "foo", value = "bar") }) List findByLastname(String lastname); + @org.springframework.data.jpa.repository.Query(value = "select u from User u where u.lastname = ?1", nativeQuery = true) + List findByLastnameNativeQuery(String lastname); + @QueryHints(value = { @QueryHint(name = "bar", value = "foo") }, forCounting = false) List findByFirstname(String firstname); @@ -186,6 +226,11 @@ class DummyJpaQuery extends AbstractJpaQuery { super(method, em); } + @Override + protected JpaQueryExecution getExecution() { + return execution; + } + @Override protected Query doCreateQuery(JpaParametersParameterAccessor accessor) { return query; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessorUnitTests.java similarity index 92% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessorUnitTests.java index fa5810927e..0e7135c5c6 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/provider/HibernateJpaParametersParameterAccessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessorUnitTests.java @@ -1,4 +1,4 @@ -package org.springframework.data.jpa.provider; +package org.springframework.data.jpa.repository.query; import jakarta.persistence.EntityManager; @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.repository.query.HibernateJpaParametersParameterAccessor; import org.springframework.data.jpa.repository.query.JpaParameters; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java index 4577e81453..0a702eaed4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java @@ -43,8 +43,7 @@ void createsJpaParametersParameterAccessor() throws Exception { Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); Object[] values = { null }; JpaParameters parameters = new JpaParameters(withNativeQuery); - JpaParametersParameterAccessor accessor = PersistenceProvider.GENERIC_JPA.getParameterAccessor(parameters, values, - em); + JpaParametersParameterAccessor accessor = new JpaParametersParameterAccessor(parameters, values); bind(parameters, accessor); @@ -57,8 +56,7 @@ void createsHibernateParametersParameterAccessor() throws Exception { Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); Object[] values = { null }; JpaParameters parameters = new JpaParameters(withNativeQuery); - JpaParametersParameterAccessor accessor = PersistenceProvider.HIBERNATE.getParameterAccessor(parameters, values, - em); + JpaParametersParameterAccessor accessor = new HibernateJpaParametersParameterAccessor(parameters, values, em); bind(parameters, accessor); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java index e30d742b76..026738f95d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java @@ -22,17 +22,32 @@ import jakarta.persistence.criteria.CriteriaBuilder; -import org.junit.jupiter.api.Test; +import org.eclipse.persistence.internal.jpa.querydef.ParameterExpressionImpl; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.parser.Part; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + /** * Unit tests for {@link ParameterMetadataProvider}. * * @author Jens Schauder + * @author Julia Lee */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.STRICT_STUBS) class ParameterMetadataProviderUnitTests { + @Mock(answer = Answers.RETURNS_DEEP_STUBS) Part part; + + private ParameterExpressionImpl parameterExpression = new ParameterExpressionImpl(null, String.class); + @Test // DATAJPA-863 void errorMessageMentionesParametersWhenParametersAreExhausted() { @@ -49,4 +64,20 @@ void errorMessageMentionesParametersWhenParametersAreExhausted() { .withMessageContaining("parameter"); } + @Test // GH-3137 + void returnAugmentedValueForStringExpressions() { + when(part.getProperty().getLeafProperty().isCollection()).thenReturn(false); + + assertThat(createParameterMetadata(Part.Type.STARTING_WITH).prepare("starting with")).isEqualTo("starting with%"); + assertThat(createParameterMetadata(Part.Type.ENDING_WITH).prepare("ending with")).isEqualTo("%ending with"); + assertThat(createParameterMetadata(Part.Type.CONTAINING).prepare("containing")).isEqualTo("%containing%"); + assertThat(createParameterMetadata(Part.Type.NOT_CONTAINING).prepare("not containing")).isEqualTo("%not containing%"); + assertThat(createParameterMetadata(Part.Type.LIKE).prepare("%like%")).isEqualTo("%like%"); + assertThat(createParameterMetadata(Part.Type.IS_NULL).prepare(null)).isEqualTo(null); + } + + private ParameterMetadataProvider.ParameterMetadata createParameterMetadata(Part.Type partType) { + when(part.getType()).thenReturn(partType); + return new ParameterMetadataProvider.ParameterMetadata<>(parameterExpression, part, null, EscapeCharacter.DEFAULT); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java index 898975ad8d..d420a70ce7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java @@ -54,6 +54,7 @@ * * @author Greg Turnquist * @author Yuriy Tsarkov + * @author Julia Lee */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = QueryWithNullLikeIntegrationTests.Config.class) @@ -66,7 +67,8 @@ class QueryWithNullLikeIntegrationTests { void setUp() { repository.saveAllAndFlush(List.of( // new EmployeeWithName("Frodo Baggins"), // - new EmployeeWithName("Bilbo Baggins"))); + new EmployeeWithName("Bilbo Baggins"), + new EmployeeWithName(null))); } @Test @@ -273,7 +275,14 @@ void mismatchedReturnTypeShouldCauseException() { @Test // GH-1184 void alignedReturnTypeShouldWork() { assertThat(repository.customQueryWithAlignedReturnType()).containsExactly(new Object[][] { - { "Frodo Baggins", "Frodo Baggins with suffix" }, { "Bilbo Baggins", "Bilbo Baggins with suffix" } }); + { "Frodo Baggins", "Frodo Baggins with suffix" }, { "Bilbo Baggins", "Bilbo Baggins with suffix" }, { null, null} }); + } + + @Test + void nullOptionalParameterShouldReturnAllEntries() { + List result = repository.customQueryWithOptionalParameter(null); + + assertThat(result).hasSize(3); } @Transactional @@ -291,6 +300,9 @@ public interface EmployeeWithNullLikeRepository extends JpaRepository customQueryWithNullableParamInNative(@Nullable @Param("partialName") String partialName); + @Query("select e from EmployeeWithName e where (:partialName is null or e.name like %:partialName%)") + List customQueryWithOptionalParameter(@Nullable @Param("partialName") String partialName); + List findByNameStartsWith(@Nullable String partialName); List findByNameEndsWith(@Nullable String partialName); From ae12c1cb0558981acd25305efb3a125ec876b63d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 27 Sep 2023 10:52:56 +0200 Subject: [PATCH 479/821] Polishing. Reformat code, add ticket references to test methods. See #3137 Original Pull Request: #3173 --- .../query/AbstractJpaQueryTests.java | 33 ++++++++----------- .../ParameterMetadataProviderUnitTests.java | 20 ++++++----- .../QueryWithNullLikeIntegrationTests.java | 3 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java index e51adcf7c0..040af0a17a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractJpaQueryTests.java @@ -15,15 +15,11 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assumptions.assumeThat; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.springframework.data.jpa.support.EntityManagerTestUtils.currentEntityManagerIsAJpa21EntityManager; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.data.jpa.support.EntityManagerTestUtils.*; import jakarta.persistence.EntityManager; import jakarta.persistence.LockModeType; @@ -83,7 +79,6 @@ void setUp() { void addsHintsToQueryObject() throws Exception { JpaQueryMethod queryMethod = getMethod("findByLastname", String.class); - AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); Query result = jpaQuery @@ -116,8 +111,8 @@ void addsLockingModeToQueryObject() throws Exception { when(query.setLockMode(any(LockModeType.class))).thenReturn(query); JpaQueryMethod queryMethod = getMethod("findOneLocked", Integer.class); - AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); + Query result = jpaQuery.createQuery( new JpaParametersParameterAccessor(queryMethod.getParameters(), new Object[] { Integer.valueOf(1) })); verify(result).setLockMode(LockModeType.PESSIMISTIC_WRITE); @@ -130,7 +125,6 @@ void shouldAddEntityGraphHintForFetch() throws Exception { assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); JpaQueryMethod queryMethod = getMethod("findAll"); - jakarta.persistence.EntityGraph entityGraph = em.getEntityGraph("User.overview"); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); @@ -146,7 +140,6 @@ void shouldAddEntityGraphHintForLoad() throws Exception { assumeThat(currentEntityManagerIsAJpa21EntityManager(em)).isTrue(); JpaQueryMethod queryMethod = getMethod("getById", Integer.class); - jakarta.persistence.EntityGraph entityGraph = em.getEntityGraph("User.detail"); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); @@ -160,12 +153,12 @@ void shouldAddEntityGraphHintForLoad() throws Exception { void shouldCreateHibernateJpaParameterParametersAccessorForNativeQuery() throws Exception { JpaQueryMethod queryMethod = getMethod("findByLastnameNativeQuery", String.class); - AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); - jpaQuery.execute(new Object[] {"some last name"}); + jpaQuery.execute(new Object[] { "some last name" }); - ArgumentCaptor captor = ArgumentCaptor.forClass(JpaParametersParameterAccessor.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(JpaParametersParameterAccessor.class); verify(execution).execute(eq(jpaQuery), captor.capture()); JpaParametersParameterAccessor parameterAccessor = captor.getValue(); @@ -178,9 +171,10 @@ void shouldCreateGenericJpaParameterParametersAccessorForNonNativeQuery() throws JpaQueryMethod queryMethod = getMethod("findByFirstname", String.class); AbstractJpaQuery jpaQuery = new DummyJpaQuery(queryMethod, em); - jpaQuery.execute(new Object[] {"some first name"}); + jpaQuery.execute(new Object[] { "some first name" }); - ArgumentCaptor captor = ArgumentCaptor.forClass(JpaParametersParameterAccessor.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(JpaParametersParameterAccessor.class); verify(execution).execute(eq(jpaQuery), captor.capture()); JpaParametersParameterAccessor parameterAccessor = captor.getValue(); @@ -201,7 +195,8 @@ interface SampleRepository extends Repository { @QueryHints({ @QueryHint(name = "foo", value = "bar") }) List findByLastname(String lastname); - @org.springframework.data.jpa.repository.Query(value = "select u from User u where u.lastname = ?1", nativeQuery = true) + @org.springframework.data.jpa.repository.Query(value = "select u from User u where u.lastname = ?1", + nativeQuery = true) List findByLastnameNativeQuery(String lastname); @QueryHints(value = { @QueryHint(name = "bar", value = "foo") }, forCounting = false) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java index 026738f95d..30079da0a4 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderUnitTests.java @@ -18,14 +18,11 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.util.Collections; - import jakarta.persistence.criteria.CriteriaBuilder; -import org.eclipse.persistence.internal.jpa.querydef.ParameterExpressionImpl; -import org.springframework.data.repository.query.Parameters; -import org.springframework.data.repository.query.parser.Part; +import java.util.Collections; +import org.eclipse.persistence.internal.jpa.querydef.ParameterExpressionImpl; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; @@ -33,6 +30,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +import org.springframework.data.repository.query.Parameters; +import org.springframework.data.repository.query.parser.Part; /** * Unit tests for {@link ParameterMetadataProvider}. @@ -46,10 +45,11 @@ class ParameterMetadataProviderUnitTests { @Mock(answer = Answers.RETURNS_DEEP_STUBS) Part part; - private ParameterExpressionImpl parameterExpression = new ParameterExpressionImpl(null, String.class); + @SuppressWarnings("rawtypes") // + private final ParameterExpressionImpl parameterExpression = new ParameterExpressionImpl<>(null, String.class); @Test // DATAJPA-863 - void errorMessageMentionesParametersWhenParametersAreExhausted() { + void errorMessageMentionsParametersWhenParametersAreExhausted() { CriteriaBuilder builder = mock(CriteriaBuilder.class); @@ -66,17 +66,21 @@ void errorMessageMentionesParametersWhenParametersAreExhausted() { @Test // GH-3137 void returnAugmentedValueForStringExpressions() { + when(part.getProperty().getLeafProperty().isCollection()).thenReturn(false); assertThat(createParameterMetadata(Part.Type.STARTING_WITH).prepare("starting with")).isEqualTo("starting with%"); assertThat(createParameterMetadata(Part.Type.ENDING_WITH).prepare("ending with")).isEqualTo("%ending with"); assertThat(createParameterMetadata(Part.Type.CONTAINING).prepare("containing")).isEqualTo("%containing%"); - assertThat(createParameterMetadata(Part.Type.NOT_CONTAINING).prepare("not containing")).isEqualTo("%not containing%"); + assertThat(createParameterMetadata(Part.Type.NOT_CONTAINING).prepare("not containing")) + .isEqualTo("%not containing%"); assertThat(createParameterMetadata(Part.Type.LIKE).prepare("%like%")).isEqualTo("%like%"); assertThat(createParameterMetadata(Part.Type.IS_NULL).prepare(null)).isEqualTo(null); } + @SuppressWarnings({ "rawtypes", "unchecked" }) private ParameterMetadataProvider.ParameterMetadata createParameterMetadata(Part.Type partType) { + when(part.getType()).thenReturn(partType); return new ParameterMetadataProvider.ParameterMetadata<>(parameterExpression, part, null, EscapeCharacter.DEFAULT); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java index d420a70ce7..518b66e808 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryWithNullLikeIntegrationTests.java @@ -278,8 +278,9 @@ void alignedReturnTypeShouldWork() { { "Frodo Baggins", "Frodo Baggins with suffix" }, { "Bilbo Baggins", "Bilbo Baggins with suffix" }, { null, null} }); } - @Test + @Test // GH-3137 void nullOptionalParameterShouldReturnAllEntries() { + List result = repository.customQueryWithOptionalParameter(null); assertThat(result).hasSize(3); From dde9e07c24eeeaf222c17d639071367c187cb11d Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 21 Sep 2023 15:12:32 -0500 Subject: [PATCH 480/821] Implement EQL parser. Implement support for EclipseLink Query Language (EQL), handling the various extensions it offers. Closes #3170 Original pull request: #3176 --- .../data/jpa/repository/query/Eql.g4 | 897 ++++++ .../jpa/provider/PersistenceProvider.java | 17 +- .../data/jpa/provider/PresenceDetector.java | 27 + .../jpa/repository/query/EqlQueryParser.java | 132 + .../repository/query/EqlQueryRenderer.java | 2540 +++++++++++++++++ .../repository/query/EqlQueryTransformer.java | 243 ++ .../repository/query/JpaQueryEnhancer.java | 15 + .../query/JpaQueryParsingToken.java | 6 + .../query/QueryEnhancerFactory.java | 19 +- .../repository/query/EqlComplianceTests.java | 403 +++ .../EqlParserQueryEnhancerUnitTests.java | 54 + .../query/EqlQueryRendererTests.java | 995 +++++++ .../query/EqlQueryTransformerTests.java | 783 +++++ .../query/EqlSpecificationTests.java | 887 ++++++ .../query/JpqlQueryRendererTests.java | 83 +- 15 files changed, 7056 insertions(+), 45 deletions(-) create mode 100644 spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PresenceDetector.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryParser.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java create mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryTransformer.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlComplianceTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlParserQueryEnhancerUnitTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryTransformerTests.java create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 new file mode 100644 index 0000000000..a0aa24491a --- /dev/null +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Eql.g4 @@ -0,0 +1,897 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +grammar Eql; + +@header { +/** + * Implementation of EclipseLink Query Language (EQL) + * See: + * * https://eclipse.dev/eclipselink/documentation/3.0/jpa/extensions/jpql.htm + * * https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL + * + * @author Greg Turnquist + * @since 3.2 + */ +} + +/* + Parser rules + */ + +start + : ql_statement EOF + ; + +ql_statement + : select_statement + | update_statement + | delete_statement + ; + +select_statement + : select_clause from_clause (where_clause)? (groupby_clause)? (having_clause)? (orderby_clause)? (setOperator select_statement)* + ; + +setOperator + : UNION ALL? + | INTERSECT ALL? + | EXCEPT ALL? + ; + +update_statement + : update_clause (where_clause)? + ; + +delete_statement + : delete_clause (where_clause)? + ; + +from_clause + : FROM identification_variable_declaration (',' identificationVariableDeclarationOrCollectionMemberDeclaration )* + ; + +// This parser rule is needed to iterate over these two types from #from_clause +identificationVariableDeclarationOrCollectionMemberDeclaration + : identification_variable_declaration + | collection_member_declaration + | '(' subquery ')' identification_variable + ; + +identification_variable_declaration + : range_variable_declaration (join | fetch_join)* + ; + +range_variable_declaration + : (entity_name|function_invocation) AS? identification_variable + ; + +join + : join_spec join_association_path_expression AS? identification_variable? join_condition? + ; + +fetch_join + : join_spec FETCH join_association_path_expression AS? identification_variable? join_condition? + ; + +join_spec + : ((LEFT (OUTER)?) | INNER)? JOIN + ; + +join_condition + : ON conditional_expression + ; + +join_association_path_expression + : join_collection_valued_path_expression + | join_single_valued_path_expression + | TREAT '(' join_collection_valued_path_expression AS subtype ')' + | TREAT '(' join_single_valued_path_expression AS subtype ')' + ; + +join_collection_valued_path_expression + : (identification_variable '.')? (single_valued_embeddable_object_field '.')* collection_valued_field + ; + +join_single_valued_path_expression + : (identification_variable '.')? (single_valued_embeddable_object_field '.')* single_valued_object_field + ; + +collection_member_declaration + : IN '(' collection_valued_path_expression ')' AS? identification_variable + ; + +qualified_identification_variable + : map_field_identification_variable + | ENTRY '(' identification_variable ')' + ; + +map_field_identification_variable + : KEY '(' identification_variable ')' + | VALUE '(' identification_variable ')' + ; + +single_valued_path_expression + : qualified_identification_variable + | TREAT '(' qualified_identification_variable AS subtype ')' + | state_field_path_expression + | single_valued_object_path_expression + ; + +general_identification_variable + : identification_variable + | map_field_identification_variable + ; + +general_subpath + : simple_subpath + | treated_subpath ('.' single_valued_object_field)* + ; + +simple_subpath + : general_identification_variable + | general_identification_variable ('.' single_valued_object_field)* + ; + +treated_subpath + : TREAT '(' general_subpath AS subtype ')' + ; + +state_field_path_expression + : general_subpath '.' state_field + ; + +state_valued_path_expression + : state_field_path_expression + | general_identification_variable + ; + +single_valued_object_path_expression + : general_subpath '.' single_valued_object_field + ; + +collection_valued_path_expression + : general_subpath '.' collection_value_field // BNF at end of spec has a typo + ; + +update_clause + : UPDATE entity_name (AS? identification_variable)? SET update_item (',' update_item)* + ; + +update_item + : (identification_variable '.')? (single_valued_embeddable_object_field '.')* (state_field | single_valued_object_field) EQUAL new_value + ; + +new_value + : scalar_expression + | simple_entity_expression + | NULL + ; + +delete_clause + : DELETE FROM entity_name (AS? identification_variable)? + ; + +select_clause + : SELECT (DISTINCT)? select_item (',' select_item)* + ; + +select_item + : select_expression (AS? result_variable)? + ; + +select_expression + : single_valued_path_expression + | scalar_expression + | aggregate_expression + | identification_variable + | OBJECT '(' identification_variable ')' + | constructor_expression + ; + +constructor_expression + : NEW constructor_name '(' constructor_item (',' constructor_item)* ')' + ; + +constructor_item + : single_valued_path_expression + | scalar_expression + | aggregate_expression + | identification_variable + ; + +aggregate_expression + : (AVG | MAX | MIN | SUM) '(' (DISTINCT)? state_valued_path_expression ')' + | COUNT '(' (DISTINCT)? (identification_variable | state_valued_path_expression | single_valued_object_path_expression) ')' + | function_invocation + ; + +where_clause + : WHERE conditional_expression + ; + +groupby_clause + : GROUP BY groupby_item (',' groupby_item)* + ; + +groupby_item + : single_valued_path_expression + | identification_variable + | scalar_expression + ; + +having_clause + : HAVING conditional_expression + ; + +orderby_clause + : ORDER BY orderby_item (',' orderby_item)* + ; + +// TODO Error in spec BNF, correctly shown elsewhere in spec. +orderby_item + : state_field_path_expression (ASC | DESC)? nullsPrecedence? + | general_identification_variable (ASC | DESC)? nullsPrecedence? + | result_variable (ASC | DESC)? nullsPrecedence? + | string_expression (ASC | DESC)? nullsPrecedence? + | scalar_expression (ASC | DESC)? nullsPrecedence? + | + ; + +nullsPrecedence + : NULLS (FIRST | LAST) + ; + +subquery + : simple_select_clause subquery_from_clause (where_clause)? (groupby_clause)? (having_clause)? + ; + +subquery_from_clause + : FROM subselect_identification_variable_declaration (',' (subselect_identification_variable_declaration | collection_member_declaration))* + ; + +subselect_identification_variable_declaration + : identification_variable_declaration + | derived_path_expression AS? identification_variable (join)* + | derived_collection_member_declaration + ; + +derived_path_expression + : general_derived_path '.' single_valued_object_field + | general_derived_path '.' collection_valued_field + ; + +general_derived_path + : simple_derived_path + | treated_derived_path ('.' single_valued_object_field)* + ; + +simple_derived_path + : superquery_identification_variable ('.' single_valued_object_field)* + ; + +treated_derived_path + : TREAT '(' general_derived_path AS subtype ')' + ; + +derived_collection_member_declaration + : IN superquery_identification_variable '.' (single_valued_object_field '.')* collection_valued_field + ; + +simple_select_clause + : SELECT (DISTINCT)? simple_select_expression + ; + +simple_select_expression + : single_valued_path_expression + | scalar_expression + | aggregate_expression + | identification_variable + ; + +scalar_expression + : arithmetic_expression + | string_expression + | enum_expression + | datetime_expression + | boolean_expression + | case_expression + | entity_type_expression + ; + +conditional_expression + : conditional_term + | conditional_expression OR conditional_term + ; + +conditional_term + : conditional_factor + | conditional_term AND conditional_factor + ; + +conditional_factor + : (NOT)? conditional_primary + ; + +conditional_primary + : simple_cond_expression + | '(' conditional_expression ')' + ; + +simple_cond_expression + : comparison_expression + | between_expression + | in_expression + | like_expression + | null_comparison_expression + | empty_collection_comparison_expression + | collection_member_expression + | exists_expression + ; + +between_expression + : arithmetic_expression (NOT)? BETWEEN arithmetic_expression AND arithmetic_expression + | string_expression (NOT)? BETWEEN string_expression AND string_expression + | datetime_expression (NOT)? BETWEEN datetime_expression AND datetime_expression + ; + +in_expression + : (state_valued_path_expression | type_discriminator) (NOT)? IN (('(' in_item (',' in_item)* ')') | ( '(' subquery ')') | collection_valued_input_parameter) + ; + +in_item + : literal + | single_valued_input_parameter + ; + +like_expression + : string_expression (NOT)? LIKE pattern_value (ESCAPE escape_character)? + ; + +null_comparison_expression + : (single_valued_path_expression | input_parameter | nullif_expression) IS (NOT)? NULL + ; + +empty_collection_comparison_expression + : collection_valued_path_expression IS (NOT)? EMPTY + ; + +collection_member_expression + : entity_or_value_expression (NOT)? MEMBER (OF)? collection_valued_path_expression + ; + +entity_or_value_expression + : single_valued_object_path_expression + | state_field_path_expression + | simple_entity_or_value_expression + ; + +simple_entity_or_value_expression + : identification_variable + | input_parameter + | literal + ; + +exists_expression + : (NOT)? EXISTS '(' subquery ')' + ; + +all_or_any_expression + : (ALL | ANY | SOME) '(' subquery ')' + ; + +comparison_expression + : string_expression comparison_operator (string_expression | all_or_any_expression) #StringComparison + | boolean_expression op=(EQUAL | NOT_EQUAL) (boolean_expression | all_or_any_expression) #BooleanComparison + | boolean_expression #DirectBooleanCheck + | enum_expression op=(EQUAL | NOT_EQUAL) (enum_expression | all_or_any_expression) #EnumComparison + | datetime_expression comparison_operator (datetime_expression | all_or_any_expression) #DatetimeComparison + | entity_expression op=(EQUAL | NOT_EQUAL) (entity_expression | all_or_any_expression) #EntityComparison + | arithmetic_expression comparison_operator (arithmetic_expression | all_or_any_expression) #ArithmeticComparison + | entity_type_expression op=(EQUAL | NOT_EQUAL) entity_type_expression #EntityTypeComparison + | string_expression REGEXP string_literal #RegexpComparison + ; + +comparison_operator + : op=EQUAL + | op='>' + | op='>=' + | op='<' + | op='<=' + | op=NOT_EQUAL + ; + +arithmetic_expression + : arithmetic_term + | arithmetic_expression op=('+' | '-') arithmetic_term + ; + +arithmetic_term + : arithmetic_factor + | arithmetic_term op=('*' | '/') arithmetic_factor + ; + +arithmetic_factor + : op=('+' | '-')? arithmetic_primary + ; + +arithmetic_primary + : state_valued_path_expression + | numeric_literal + | '(' arithmetic_expression ')' + | input_parameter + | functions_returning_numerics + | aggregate_expression + | case_expression + | cast_function + | function_invocation + | '(' subquery ')' + ; + +string_expression + : state_valued_path_expression + | string_literal + | input_parameter + | functions_returning_strings + | aggregate_expression + | case_expression + | function_invocation + | '(' subquery ')' + ; + +datetime_expression + : state_valued_path_expression + | input_parameter + | functions_returning_datetime + | aggregate_expression + | case_expression + | function_invocation + | date_time_timestamp_literal + | '(' subquery ')' + ; + +boolean_expression + : state_valued_path_expression + | boolean_literal + | input_parameter + | case_expression + | function_invocation + | '(' subquery ')' + ; + +enum_expression + : state_valued_path_expression + | enum_literal + | input_parameter + | case_expression + | '(' subquery ')' + ; + +entity_expression + : single_valued_object_path_expression + | simple_entity_expression + ; + +simple_entity_expression + : identification_variable + | input_parameter + ; + +entity_type_expression + : type_discriminator + | entity_type_literal + | input_parameter + ; + +type_discriminator + : TYPE '(' (general_identification_variable | single_valued_object_path_expression | input_parameter) ')' + ; + +functions_returning_numerics + : LENGTH '(' string_expression ')' + | LOCATE '(' string_expression ',' string_expression (',' arithmetic_expression)? ')' + | ABS '(' arithmetic_expression ')' + | CEILING '(' arithmetic_expression ')' + | EXP '(' arithmetic_expression ')' + | FLOOR '(' arithmetic_expression ')' + | LN '(' arithmetic_expression ')' + | SIGN '(' arithmetic_expression ')' + | SQRT '(' arithmetic_expression ')' + | MOD '(' arithmetic_expression '/' arithmetic_expression ')' + | POWER '(' arithmetic_expression ',' arithmetic_expression ')' + | ROUND '(' arithmetic_expression ',' arithmetic_expression ')' + | SIZE '(' collection_valued_path_expression ')' + | INDEX '(' identification_variable ')' + | extract_datetime_field + ; + +functions_returning_datetime + : CURRENT_DATE + | CURRENT_TIME + | CURRENT_TIMESTAMP + | LOCAL DATE + | LOCAL TIME + | LOCAL DATETIME + | extract_datetime_part + ; + +functions_returning_strings + : CONCAT '(' string_expression ',' string_expression (',' string_expression)* ')' + | SUBSTRING '(' string_expression ',' arithmetic_expression (',' arithmetic_expression)? ')' + | TRIM '(' ((trim_specification)? (trim_character)? FROM)? string_expression ')' + | LOWER '(' string_expression ')' + | UPPER '(' string_expression ')' + ; + +trim_specification + : LEADING + | TRAILING + | BOTH + ; + +cast_function + : CAST '(' single_valued_path_expression identification_variable ('(' numeric_literal (',' numeric_literal)* ')')? ')' + ; + +function_invocation + : (FUNCTION|identification_variable) '(' function_name (',' function_arg)* ')' + ; + +extract_datetime_field + : EXTRACT '(' datetime_field FROM datetime_expression ')' + ; + +datetime_field + : identification_variable + ; + +extract_datetime_part + : EXTRACT '(' datetime_part FROM datetime_expression ')' + ; + +datetime_part + : identification_variable + ; + +function_arg + : literal + | state_valued_path_expression + | input_parameter + | scalar_expression + ; + +case_expression + : general_case_expression + | simple_case_expression + | coalesce_expression + | nullif_expression + ; + +general_case_expression + : CASE when_clause (when_clause)* ELSE scalar_expression END + ; + +when_clause + : WHEN conditional_expression THEN scalar_expression + ; + +simple_case_expression + : CASE case_operand simple_when_clause (simple_when_clause)* ELSE scalar_expression END + ; + +case_operand + : state_valued_path_expression + | type_discriminator + ; + +simple_when_clause + : WHEN scalar_expression THEN scalar_expression + ; + +coalesce_expression + : COALESCE '(' scalar_expression (',' scalar_expression)+ ')' + ; + +nullif_expression + : NULLIF '(' scalar_expression ',' scalar_expression ')' + ; + +/******************* + Gaps in the spec. + *******************/ + +trim_character + : CHARACTER + | character_valued_input_parameter + ; + +identification_variable + : IDENTIFICATION_VARIABLE + | f=(COUNT + | DATE + | FROM + | INNER + | KEY + | LEFT + | NEW + | ORDER + | OUTER + | POWER + | FLOOR + | SIGN + | TIME + | TYPE + | VALUE) + ; + +constructor_name + : state_field_path_expression + ; + +literal + : STRINGLITERAL + | INTLITERAL + | FLOATLITERAL + | LONGLITERAL + | boolean_literal + | entity_type_literal + ; + +input_parameter + : '?' INTLITERAL + | ':' identification_variable + ; + +pattern_value + : string_expression + ; + +date_time_timestamp_literal + : STRINGLITERAL + | DATELITERAL + | TIMELITERAL + | TIMESTAMPLITERAL + ; + +entity_type_literal + : identification_variable + ; + +escape_character + : CHARACTER + | character_valued_input_parameter // + ; + +numeric_literal + : INTLITERAL + | FLOATLITERAL + | LONGLITERAL + ; + +boolean_literal + : TRUE + | FALSE + ; + +enum_literal + : state_field_path_expression + ; + +string_literal + : CHARACTER + | STRINGLITERAL + ; + +single_valued_embeddable_object_field + : identification_variable + ; + +subtype + : identification_variable + ; + +collection_valued_field + : identification_variable + ; + +single_valued_object_field + : identification_variable + ; + +state_field + : identification_variable + ; + +collection_value_field + : identification_variable + ; + +entity_name + : identification_variable ('.' identification_variable)* // Hibernate sometimes expands the entity name to FQDN when using named queries + ; + +result_variable + : identification_variable + ; + +superquery_identification_variable + : identification_variable + ; + +collection_valued_input_parameter + : input_parameter + ; + +single_valued_input_parameter + : input_parameter + ; + +function_name + : string_literal + ; + +character_valued_input_parameter + : CHARACTER + | input_parameter + ; + +/* + Lexer rules + */ + + +WS : [ \t\r\n] -> skip ; + +// Build up case-insentive tokens + +fragment A: 'a' | 'A'; +fragment B: 'b' | 'B'; +fragment C: 'c' | 'C'; +fragment D: 'd' | 'D'; +fragment E: 'e' | 'E'; +fragment F: 'f' | 'F'; +fragment G: 'g' | 'G'; +fragment H: 'h' | 'H'; +fragment I: 'i' | 'I'; +fragment J: 'j' | 'J'; +fragment K: 'k' | 'K'; +fragment L: 'l' | 'L'; +fragment M: 'm' | 'M'; +fragment N: 'n' | 'N'; +fragment O: 'o' | 'O'; +fragment P: 'p' | 'P'; +fragment Q: 'q' | 'Q'; +fragment R: 'r' | 'R'; +fragment S: 's' | 'S'; +fragment T: 't' | 'T'; +fragment U: 'u' | 'U'; +fragment V: 'v' | 'V'; +fragment W: 'w' | 'W'; +fragment X: 'x' | 'X'; +fragment Y: 'y' | 'Y'; +fragment Z: 'z' | 'Z'; + +// The following are reserved identifiers: + +ABS : A B S; +ALL : A L L; +AND : A N D; +ANY : A N Y; +AS : A S; +ASC : A S C; +AVG : A V G; +BETWEEN : B E T W E E N; +BOTH : B O T H; +BY : B Y; +CASE : C A S E; +CAST : C A S T; +CEILING : C E I L I N G; +COALESCE : C O A L E S C E; +CONCAT : C O N C A T; +COUNT : C O U N T; +CURRENT_DATE : C U R R E N T '_' D A T E; +CURRENT_TIME : C U R R E N T '_' T I M E; +CURRENT_TIMESTAMP : C U R R E N T '_' T I M E S T A M P; +DATE : D A T E; +DATETIME : D A T E T I M E ; +DELETE : D E L E T E; +DESC : D E S C; +DISTINCT : D I S T I N C T; +END : E N D; +ELSE : E L S E; +EMPTY : E M P T Y; +ENTRY : E N T R Y; +ESCAPE : E S C A P E; +EXCEPT : E X C E P T; +EXISTS : E X I S T S; +EXP : E X P; +EXTRACT : E X T R A C T; +FALSE : F A L S E; +FETCH : F E T C H; +FIRST : F I R S T; +FLOOR : F L O O R; +FROM : F R O M; +FUNCTION : F U N C T I O N; +GROUP : G R O U P; +HAVING : H A V I N G; +IN : I N; +INDEX : I N D E X; +INNER : I N N E R; +INTERSECT : I N T E R S E C T; +IS : I S; +JOIN : J O I N; +KEY : K E Y; +LAST : L A S T; +LEADING : L E A D I N G; +LEFT : L E F T; +LENGTH : L E N G T H; +LIKE : L I K E; +LN : L N; +LOCAL : L O C A L; +LOCATE : L O C A T E; +LOWER : L O W E R; +MAX : M A X; +MEMBER : M E M B E R; +MIN : M I N; +MOD : M O D; +NEW : N E W; +NOT : N O T; +NULL : N U L L; +NULLIF : N U L L I F; +NULLS : N U L L S; +OBJECT : O B J E C T; +OF : O F; +ON : O N; +OR : O R; +ORDER : O R D E R; +OUTER : O U T E R; +POWER : P O W E R; +REGEXP : R E G E X P; +ROUND : R O U N D; +SELECT : S E L E C T; +SET : S E T; +SIGN : S I G N; +SIZE : S I Z E; +SOME : S O M E; +SQRT : S Q R T; +SUBSTRING : S U B S T R I N G; +SUM : S U M; +THEN : T H E N; +TIME : T I M E; +TRAILING : T R A I L I N G; +TREAT : T R E A T; +TRIM : T R I M; +TRUE : T R U E; +TYPE : T Y P E; +UNION : U N I O N; +UPDATE : U P D A T E; +UPPER : U P P E R; +VALUE : V A L U E; +WHEN : W H E N; +WHERE : W H E R E; + +EQUAL : '=' ; +NOT_EQUAL : '<>' | '!=' ; + + +CHARACTER : '\'' (~ ('\'' | '\\')) '\'' ; +IDENTIFICATION_VARIABLE : ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '$' | '_') ('a' .. 'z' | 'A' .. 'Z' | '\u0080' .. '\ufffe' | '0' .. '9' | '$' | '_')* ; +STRINGLITERAL : '\'' (~ ('\'' | '\\')|'\\')* '\'' ; +FLOATLITERAL : ('0' .. '9')* '.' ('0' .. '9')+ (E ('0' .. '9')+)* (F|D)?; +INTLITERAL : ('0' .. '9')+ ; +LONGLITERAL : ('0' .. '9')+ L; +DATELITERAL : '{' D STRINGLITERAL '}'; +TIMELITERAL : '{' T STRINGLITERAL '}'; +TIMESTAMPLITERAL : '{' T S STRINGLITERAL '}'; \ No newline at end of file diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 076f447c3d..379e30b55e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -55,7 +55,7 @@ * @author Greg Turnquist * @author Yuriy Tsarkov */ -public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor, QueryComment { +public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor, QueryComment, PresenceDetector { /** * Hibernate persistence provider. @@ -110,6 +110,7 @@ public CloseableIterator executeQueryWithResultStream(Query jpaQuery) { public String getCommentHintKey() { return "org.hibernate.comment"; } + }, /** @@ -203,6 +204,8 @@ public String getCommentHintKey() { private final Iterable entityManagerClassNames; private final Iterable metamodelClassNames; + private boolean present; + /** * Creates a new {@link PersistenceProvider}. * @@ -214,6 +217,13 @@ public String getCommentHintKey() { this.entityManagerClassNames = entityManagerClassNames; this.metamodelClassNames = metamodelClassNames; + + this.present = false; + entityManagerClassNames.forEach(entityManagerClassName -> { + if (ClassUtils.isPresent(entityManagerClassName, PersistenceProvider.class.getClassLoader())) { + this.present = true; + } + }); } /** @@ -330,6 +340,11 @@ public static Object unwrapTypedParameterValue(@Nullable Object value) { : value; } + @Override + public boolean isPresent() { + return this.present; + } + /** * Holds the PersistenceProvider specific interface names. * diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PresenceDetector.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PresenceDetector.java new file mode 100644 index 0000000000..e6bd19e703 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PresenceDetector.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + */ +package org.springframework.data.jpa.provider; + +/** + * Define operations needed to detect the presence of a given JPA provider. + * + * @author Greg Turnquist + * @since 3.2 + */ +interface PresenceDetector { + boolean isPresent(); +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryParser.java new file mode 100644 index 0000000000..25e8326181 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryParser.java @@ -0,0 +1,132 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.util.List; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; + +/** + * Implements the {@code EQL} parsing operations of a {@link JpaQueryParserSupport} using the ANTLR-generated + * {@link EqlParser} and {@link EqlQueryTransformer}. + * + * @author Greg Turnquist + * @since 3.2 + */ +class EqlQueryParser extends JpaQueryParserSupport { + + EqlQueryParser(String query) { + super(query); + } + + /** + * Convenience method to parse a EQL query. Will throw a {@link BadJpqlGrammarException} if the query is invalid. + * + * @param query + * @return a parsed query, ready for postprocessing + */ + public static ParserRuleContext parseQuery(String query) { + + EqlLexer lexer = new EqlLexer(CharStreams.fromString(query)); + EqlParser parser = new EqlParser(new CommonTokenStream(lexer)); + + configureParser(query, lexer, parser); + + return parser.start(); + } + + /** + * Parse the query using {@link #parseQuery(String)}. + * + * @return a parsed query + */ + @Override + protected ParserRuleContext parse(String query) { + return parseQuery(query); + } + + /** + * Use the {@link EqlQueryTransformer} to transform the original query into a query with the {@link Sort} applied. + * + * @param parsedQuery + * @param sort can be {@literal null} + * @return list of {@link JpaQueryParsingToken}s + */ + @Override + protected List applySort(ParserRuleContext parsedQuery, Sort sort) { + return new EqlQueryTransformer(sort).visit(parsedQuery); + } + + /** + * Use the {@link EqlQueryTransformer} to transform the original query into a count query. + * + * @param parsedQuery + * @param countProjection + * @return list of {@link JpaQueryParsingToken}s + */ + @Override + protected List doCreateCountQuery(ParserRuleContext parsedQuery, + @Nullable String countProjection) { + return new EqlQueryTransformer(true, countProjection).visit(parsedQuery); + } + + /** + * Run the parsed query through {@link EqlQueryTransformer} to find the primary FROM clause's alias. + * + * @param parsedQuery + * @return can be {@literal null} + */ + @Override + protected String doFindAlias(ParserRuleContext parsedQuery) { + + EqlQueryTransformer transformVisitor = new EqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.getAlias(); + } + + /** + * Use {@link EqlQueryTransformer} to find the projection of the query. + * + * @param parsedQuery + * @return + */ + @Override + protected List doFindProjection(ParserRuleContext parsedQuery) { + + EqlQueryTransformer transformVisitor = new EqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.getProjection(); + } + + /** + * Use {@link EqlQueryTransformer} to detect if the query uses a {@code new com.example.Dto()} DTO constructor in the + * primary select clause. + * + * @param parsedQuery + * @return Guaranteed to be {@literal true} or {@literal false}. + */ + @Override + protected boolean doCheckForConstructor(ParserRuleContext parsedQuery) { + + EqlQueryTransformer transformVisitor = new EqlQueryTransformer(); + transformVisitor.visit(parsedQuery); + return transformVisitor.hasConstructorExpression(); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java new file mode 100644 index 0000000000..91ede2389f --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java @@ -0,0 +1,2540 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that renders an EQL query without making any changes. + * + * @author Greg Turnquist + * @since 3.2 + */ +class EqlQueryRenderer extends EqlBaseVisitor> { + + @Override + public List visitStart(EqlParser.StartContext ctx) { + return visit(ctx.ql_statement()); + } + + @Override + public List visitQl_statement(EqlParser.Ql_statementContext ctx) { + + if (ctx.select_statement() != null) { + return visit(ctx.select_statement()); + } else if (ctx.update_statement() != null) { + return visit(ctx.update_statement()); + } else if (ctx.delete_statement() != null) { + return visit(ctx.delete_statement()); + } else { + return List.of(); + } + } + + @Override + public List visitSelect_statement(EqlParser.Select_statementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.select_clause())); + tokens.addAll(visit(ctx.from_clause())); + + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + + if (ctx.groupby_clause() != null) { + tokens.addAll(visit(ctx.groupby_clause())); + } + + if (ctx.having_clause() != null) { + tokens.addAll(visit(ctx.having_clause())); + } + + if (ctx.orderby_clause() != null) { + tokens.addAll(visit(ctx.orderby_clause())); + } + + for (int i = 0; i < ctx.setOperator().size(); i++) { + + tokens.addAll(visit(ctx.setOperator(i))); + tokens.addAll(visit(ctx.select_statement(i))); + } + + return tokens; + } + + @Override + public List visitSetOperator(EqlParser.SetOperatorContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.UNION() != null) { + tokens.add(new JpaQueryParsingToken(ctx.UNION())); + } else if (ctx.INTERSECT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INTERSECT())); + } else if (ctx.EXCEPT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.EXCEPT())); + } + + if (ctx.ALL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ALL())); + } + + return tokens; + } + + @Override + public List visitUpdate_statement(EqlParser.Update_statementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.update_clause())); + + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + + return tokens; + } + + @Override + public List visitDelete_statement(EqlParser.Delete_statementContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.delete_clause())); + + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + + return tokens; + } + + @Override + public List visitFrom_clause(EqlParser.From_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.FROM(), true)); + tokens.addAll(visit(ctx.identification_variable_declaration())); + + ctx.identificationVariableDeclarationOrCollectionMemberDeclaration() + .forEach(identificationVariableDeclarationOrCollectionMemberDeclarationContext -> { + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(identificationVariableDeclarationOrCollectionMemberDeclarationContext)); + }); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitIdentificationVariableDeclarationOrCollectionMemberDeclaration( + EqlParser.IdentificationVariableDeclarationOrCollectionMemberDeclarationContext ctx) { + + if (ctx.identification_variable_declaration() != null) { + return visit(ctx.identification_variable_declaration()); + } else if (ctx.collection_member_declaration() != null) { + return visit(ctx.collection_member_declaration()); + } else if (ctx.subquery() != null) { + + List tokens = new ArrayList<>(); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + + return tokens; + } else { + return List.of(); + } + } + + @Override + public List visitIdentification_variable_declaration( + EqlParser.Identification_variable_declarationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.range_variable_declaration())); + + ctx.join().forEach(joinContext -> { + tokens.addAll(visit(joinContext)); + }); + ctx.fetch_join().forEach(fetchJoinContext -> { + tokens.addAll(visit(fetchJoinContext)); + }); + + return tokens; + } + + @Override + public List visitRange_variable_declaration(EqlParser.Range_variable_declarationContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.entity_name() != null) { + tokens.addAll(visit(ctx.entity_name())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + tokens.addAll(visit(ctx.identification_variable())); + + return tokens; + } + + @Override + public List visitJoin(EqlParser.JoinContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.join_spec())); + tokens.addAll(visit(ctx.join_association_path_expression())); + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + if (ctx.join_condition() != null) { + tokens.addAll(visit(ctx.join_condition())); + } + + return tokens; + } + + @Override + public List visitFetch_join(EqlParser.Fetch_joinContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.join_spec())); + tokens.add(new JpaQueryParsingToken(ctx.FETCH())); + tokens.addAll(visit(ctx.join_association_path_expression())); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + if (ctx.join_condition() != null) { + tokens.addAll(visit(ctx.join_condition())); + } + + return tokens; + } + + @Override + public List visitJoin_spec(EqlParser.Join_specContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.LEFT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LEFT())); + } + if (ctx.OUTER() != null) { + tokens.add(new JpaQueryParsingToken(ctx.OUTER())); + } + if (ctx.INNER() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INNER())); + } + if (ctx.JOIN() != null) { + tokens.add(new JpaQueryParsingToken(ctx.JOIN())); + } + + return tokens; + } + + @Override + public List visitJoin_condition(EqlParser.Join_conditionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.ON())); + tokens.addAll(visit(ctx.conditional_expression())); + + return tokens; + } + + @Override + public List visitJoin_association_path_expression( + EqlParser.Join_association_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.TREAT() == null) { + + if (ctx.join_collection_valued_path_expression() != null) { + tokens.addAll(visit(ctx.join_collection_valued_path_expression())); + } else if (ctx.join_single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.join_single_valued_path_expression())); + } + } else { + if (ctx.join_collection_valued_path_expression() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.join_collection_valued_path_expression())); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.subtype())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.join_single_valued_path_expression() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.join_single_valued_path_expression())); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.subtype())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + } + + return tokens; + } + + @Override + public List visitJoin_collection_valued_path_expression( + EqlParser.Join_collection_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + } + + ctx.single_valued_embeddable_object_field().forEach(singleValuedEmbeddableObjectFieldContext -> { + tokens.addAll(visit(singleValuedEmbeddableObjectFieldContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + + tokens.addAll(visit(ctx.collection_valued_field())); + + return tokens; + } + + @Override + public List visitJoin_single_valued_path_expression( + EqlParser.Join_single_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + + tokens.addAll(visit(ctx.identification_variable())); + tokens.add(TOKEN_DOT); + } + + ctx.single_valued_embeddable_object_field().forEach(singleValuedEmbeddableObjectFieldContext -> { + tokens.addAll(visit(singleValuedEmbeddableObjectFieldContext)); + tokens.add(TOKEN_DOT); + }); + + tokens.addAll(visit(ctx.single_valued_object_field())); + + return tokens; + } + + @Override + public List visitCollection_member_declaration( + EqlParser.Collection_member_declarationContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.IN(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.collection_valued_path_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + tokens.addAll(visit(ctx.identification_variable())); + + return tokens; + } + + @Override + public List visitQualified_identification_variable( + EqlParser.Qualified_identification_variableContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.map_field_identification_variable() != null) { + tokens.addAll(visit(ctx.map_field_identification_variable())); + } else if (ctx.identification_variable() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ENTRY())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitMap_field_identification_variable( + EqlParser.Map_field_identification_variableContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.KEY() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.KEY(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.VALUE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.VALUE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitSingle_valued_path_expression( + EqlParser.Single_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.qualified_identification_variable() != null) { + tokens.addAll(visit(ctx.qualified_identification_variable())); + } else if (ctx.qualified_identification_variable() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.qualified_identification_variable())); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.subtype())); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.state_field_path_expression() != null) { + tokens.addAll(visit(ctx.state_field_path_expression())); + } else if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } + + return tokens; + } + + @Override + public List visitGeneral_identification_variable( + EqlParser.General_identification_variableContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.map_field_identification_variable() != null) { + tokens.addAll(visit(ctx.map_field_identification_variable())); + } + + return tokens; + } + + @Override + public List visitGeneral_subpath(EqlParser.General_subpathContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.simple_subpath() != null) { + tokens.addAll(visit(ctx.simple_subpath())); + } else if (ctx.treated_subpath() != null) { + + tokens.addAll(visit(ctx.treated_subpath())); + + ctx.single_valued_object_field().forEach(singleValuedObjectFieldContext -> { + tokens.add(TOKEN_DOT); + tokens.addAll(visit(singleValuedObjectFieldContext)); + }); + } + + return tokens; + } + + @Override + public List visitSimple_subpath(EqlParser.Simple_subpathContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.general_identification_variable())); + NOSPACE(tokens); + + ctx.single_valued_object_field().forEach(singleValuedObjectFieldContext -> { + tokens.add(TOKEN_DOT); + tokens.addAll(visit(singleValuedObjectFieldContext)); + NOSPACE(tokens); + }); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitTreated_subpath(EqlParser.Treated_subpathContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.TREAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.general_subpath())); + SPACE(tokens); + tokens.add(new JpaQueryParsingToken(ctx.AS())); + tokens.addAll(visit(ctx.subtype())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitState_field_path_expression(EqlParser.State_field_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.general_subpath())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.state_field())); + + return tokens; + } + + @Override + public List visitState_valued_path_expression( + EqlParser.State_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_field_path_expression() != null) { + tokens.addAll(visit(ctx.state_field_path_expression())); + } else if (ctx.general_identification_variable() != null) { + tokens.addAll(visit(ctx.general_identification_variable())); + } + + return tokens; + } + + @Override + public List visitSingle_valued_object_path_expression( + EqlParser.Single_valued_object_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.general_subpath())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.single_valued_object_field())); + + return tokens; + } + + @Override + public List visitCollection_valued_path_expression( + EqlParser.Collection_valued_path_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.general_subpath())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + tokens.addAll(visit(ctx.collection_value_field())); + + return tokens; + } + + @Override + public List visitUpdate_clause(EqlParser.Update_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.UPDATE())); + tokens.addAll(visit(ctx.entity_name())); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + tokens.add(new JpaQueryParsingToken(ctx.SET())); + + ctx.update_item().forEach(updateItemContext -> { + tokens.addAll(visit(updateItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitUpdate_item(EqlParser.Update_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + } + + ctx.single_valued_embeddable_object_field().forEach(singleValuedEmbeddableObjectFieldContext -> { + tokens.addAll(visit(singleValuedEmbeddableObjectFieldContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + + if (ctx.state_field() != null) { + tokens.addAll(visit(ctx.state_field())); + } else if (ctx.single_valued_object_field() != null) { + tokens.addAll(visit(ctx.single_valued_object_field())); + } + + tokens.add(TOKEN_EQUALS); + tokens.addAll(visit(ctx.new_value())); + + return tokens; + } + + @Override + public List visitNew_value(EqlParser.New_valueContext ctx) { + + if (ctx.scalar_expression() != null) { + return visit(ctx.scalar_expression()); + } else if (ctx.simple_entity_expression() != null) { + return visit(ctx.simple_entity_expression()); + } else if (ctx.NULL() != null) { + return List.of(new JpaQueryParsingToken(ctx.NULL())); + } else { + return List.of(); + } + } + + @Override + public List visitDelete_clause(EqlParser.Delete_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.DELETE())); + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + tokens.addAll(visit(ctx.entity_name())); + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitSelect_clause(EqlParser.Select_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SELECT())); + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + ctx.select_item().forEach(selectItemContext -> { + tokens.addAll(visit(selectItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitSelect_item(EqlParser.Select_itemContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.select_expression())); + SPACE(tokens); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + if (ctx.result_variable() != null) { + tokens.addAll(visit(ctx.result_variable())); + } + + return tokens; + } + + @Override + public List visitSelect_expression(EqlParser.Select_expressionContext ctx) { + + if (ctx.single_valued_path_expression() != null) { + return visit(ctx.single_valued_path_expression()); + } else if (ctx.scalar_expression() != null) { + return visit(ctx.scalar_expression()); + } else if (ctx.aggregate_expression() != null) { + return visit(ctx.aggregate_expression()); + } else if (ctx.identification_variable() != null) { + + if (ctx.OBJECT() == null) { + return visit(ctx.identification_variable()); + } else { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.OBJECT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + } else if (ctx.constructor_expression() != null) { + return visit(ctx.constructor_expression()); + } else { + return List.of(); + } + } + + @Override + public List visitConstructor_expression(EqlParser.Constructor_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.NEW())); + tokens.addAll(visit(ctx.constructor_name())); + tokens.add(TOKEN_OPEN_PAREN); + + ctx.constructor_item().forEach(constructorItemContext -> { + tokens.addAll(visit(constructorItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitConstructor_item(EqlParser.Constructor_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_path_expression())); + } else if (ctx.scalar_expression() != null) { + tokens.addAll(visit(ctx.scalar_expression())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitAggregate_expression(EqlParser.Aggregate_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.AVG() != null || ctx.MAX() != null || ctx.MIN() != null || ctx.SUM() != null) { + + if (ctx.AVG() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AVG(), false)); + } + if (ctx.MAX() != null) { + tokens.add(new JpaQueryParsingToken(ctx.MAX(), false)); + } + if (ctx.MIN() != null) { + tokens.add(new JpaQueryParsingToken(ctx.MIN(), false)); + } + if (ctx.SUM() != null) { + tokens.add(new JpaQueryParsingToken(ctx.SUM(), false)); + } + + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + tokens.addAll(visit(ctx.state_valued_path_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.COUNT() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.COUNT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } + + return tokens; + } + + @Override + public List visitWhere_clause(EqlParser.Where_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHERE(), true)); + tokens.addAll(visit(ctx.conditional_expression())); + + return tokens; + } + + @Override + public List visitGroupby_clause(EqlParser.Groupby_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.GROUP())); + tokens.add(new JpaQueryParsingToken(ctx.BY())); + ctx.groupby_item().forEach(groupbyItemContext -> { + tokens.addAll(visit(groupbyItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitGroupby_item(EqlParser.Groupby_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_path_expression())); + } else if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.scalar_expression() != null) { + tokens.addAll(visit(ctx.scalar_expression())); + } + + return tokens; + } + + @Override + public List visitHaving_clause(EqlParser.Having_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.HAVING())); + tokens.addAll(visit(ctx.conditional_expression())); + + return tokens; + } + + @Override + public List visitOrderby_clause(EqlParser.Orderby_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.ORDER())); + tokens.add(new JpaQueryParsingToken(ctx.BY())); + + ctx.orderby_item().forEach(orderbyItemContext -> { + tokens.addAll(visit(orderbyItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + return tokens; + } + + @Override + public List visitOrderby_item(EqlParser.Orderby_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_field_path_expression() != null) { + tokens.addAll(visit(ctx.state_field_path_expression())); + } else if (ctx.general_identification_variable() != null) { + tokens.addAll(visit(ctx.general_identification_variable())); + } else if (ctx.result_variable() != null) { + tokens.addAll(visit(ctx.result_variable())); + } else if (ctx.string_expression() != null) { + tokens.addAll(visit(ctx.string_expression())); + } else if (ctx.scalar_expression() != null) { + tokens.addAll(visit(ctx.scalar_expression())); + } + + if (ctx.ASC() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ASC())); + } + if (ctx.DESC() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DESC())); + } + + if (ctx.nullsPrecedence() != null) { + tokens.addAll(visit(ctx.nullsPrecedence())); + } + + return tokens; + } + + @Override + public List visitNullsPrecedence(EqlParser.NullsPrecedenceContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(TOKEN_NULLS); + + if (ctx.FIRST() != null) { + tokens.add(TOKEN_FIRST); + } else if (ctx.LAST() != null) { + tokens.add(TOKEN_LAST); + } + + return tokens; + } + + @Override + public List visitSubquery(EqlParser.SubqueryContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.simple_select_clause())); + tokens.addAll(visit(ctx.subquery_from_clause())); + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + if (ctx.groupby_clause() != null) { + tokens.addAll(visit(ctx.groupby_clause())); + } + if (ctx.having_clause() != null) { + tokens.addAll(visit(ctx.having_clause())); + } + + return tokens; + } + + @Override + public List visitSubquery_from_clause(EqlParser.Subquery_from_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + ctx.subselect_identification_variable_declaration().forEach(subselectIdentificationVariableDeclarationContext -> { + tokens.addAll(visit(subselectIdentificationVariableDeclarationContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitSubselect_identification_variable_declaration( + EqlParser.Subselect_identification_variable_declarationContext ctx) { + return super.visitSubselect_identification_variable_declaration(ctx); + } + + @Override + public List visitDerived_path_expression(EqlParser.Derived_path_expressionContext ctx) { + return super.visitDerived_path_expression(ctx); + } + + @Override + public List visitGeneral_derived_path(EqlParser.General_derived_pathContext ctx) { + return super.visitGeneral_derived_path(ctx); + } + + @Override + public List visitSimple_derived_path(EqlParser.Simple_derived_pathContext ctx) { + return super.visitSimple_derived_path(ctx); + } + + @Override + public List visitTreated_derived_path(EqlParser.Treated_derived_pathContext ctx) { + return super.visitTreated_derived_path(ctx); + } + + @Override + public List visitDerived_collection_member_declaration( + EqlParser.Derived_collection_member_declarationContext ctx) { + return super.visitDerived_collection_member_declaration(ctx); + } + + @Override + public List visitSimple_select_clause(EqlParser.Simple_select_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.SELECT())); + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + tokens.addAll(visit(ctx.simple_select_expression())); + + return tokens; + } + + @Override + public List visitSimple_select_expression(EqlParser.Simple_select_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_path_expression())); + } else if (ctx.scalar_expression() != null) { + tokens.addAll(visit(ctx.scalar_expression())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitScalar_expression(EqlParser.Scalar_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.arithmetic_expression() != null) { + tokens.addAll(visit(ctx.arithmetic_expression())); + } else if (ctx.string_expression() != null) { + tokens.addAll(visit(ctx.string_expression())); + } else if (ctx.enum_expression() != null) { + tokens.addAll(visit(ctx.enum_expression())); + } else if (ctx.datetime_expression() != null) { + tokens.addAll(visit(ctx.datetime_expression())); + } else if (ctx.boolean_expression() != null) { + tokens.addAll(visit(ctx.boolean_expression())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.entity_type_expression() != null) { + tokens.addAll(visit(ctx.entity_type_expression())); + } + + return tokens; + } + + @Override + public List visitConditional_expression(EqlParser.Conditional_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.conditional_expression() != null) { + tokens.addAll(visit(ctx.conditional_expression())); + tokens.add(new JpaQueryParsingToken(ctx.OR())); + tokens.addAll(visit(ctx.conditional_term())); + } else { + tokens.addAll(visit(ctx.conditional_term())); + } + + return tokens; + } + + @Override + public List visitConditional_term(EqlParser.Conditional_termContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.conditional_term() != null) { + tokens.addAll(visit(ctx.conditional_term())); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.conditional_factor())); + } else { + tokens.addAll(visit(ctx.conditional_factor())); + } + + return tokens; + } + + @Override + public List visitConditional_factor(EqlParser.Conditional_factorContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + EqlParser.Conditional_primaryContext conditionalPrimary = ctx.conditional_primary(); + List visitedConditionalPrimary = visit(conditionalPrimary); + tokens.addAll(visitedConditionalPrimary); + + return tokens; + } + + @Override + public List visitConditional_primary(EqlParser.Conditional_primaryContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.simple_cond_expression() != null) { + tokens.addAll(visit(ctx.simple_cond_expression())); + } else if (ctx.conditional_expression() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.conditional_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitSimple_cond_expression(EqlParser.Simple_cond_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.comparison_expression() != null) { + tokens.addAll(visit(ctx.comparison_expression())); + } else if (ctx.between_expression() != null) { + tokens.addAll(visit(ctx.between_expression())); + } else if (ctx.in_expression() != null) { + tokens.addAll(visit(ctx.in_expression())); + } else if (ctx.like_expression() != null) { + tokens.addAll(visit(ctx.like_expression())); + } else if (ctx.null_comparison_expression() != null) { + tokens.addAll(visit(ctx.null_comparison_expression())); + } else if (ctx.empty_collection_comparison_expression() != null) { + tokens.addAll(visit(ctx.empty_collection_comparison_expression())); + } else if (ctx.collection_member_expression() != null) { + tokens.addAll(visit(ctx.collection_member_expression())); + } else if (ctx.exists_expression() != null) { + tokens.addAll(visit(ctx.exists_expression())); + } + + return tokens; + } + + @Override + public List visitBetween_expression(EqlParser.Between_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.arithmetic_expression(0) != null) { + + tokens.addAll(visit(ctx.arithmetic_expression(0))); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); + tokens.addAll(visit(ctx.arithmetic_expression(1))); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.arithmetic_expression(2))); + + } else if (ctx.string_expression(0) != null) { + + tokens.addAll(visit(ctx.string_expression(0))); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); + tokens.addAll(visit(ctx.string_expression(1))); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.string_expression(2))); + + } else if (ctx.datetime_expression(0) != null) { + + tokens.addAll(visit(ctx.datetime_expression(0))); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + + tokens.add(new JpaQueryParsingToken(ctx.BETWEEN())); + tokens.addAll(visit(ctx.datetime_expression(1))); + tokens.add(new JpaQueryParsingToken(ctx.AND())); + tokens.addAll(visit(ctx.datetime_expression(2))); + } + + return tokens; + } + + @Override + public List visitIn_expression(EqlParser.In_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } + if (ctx.type_discriminator() != null) { + tokens.addAll(visit(ctx.type_discriminator())); + } + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + if (ctx.IN() != null) { + tokens.add(new JpaQueryParsingToken(ctx.IN())); + } + + if (ctx.in_item() != null && !ctx.in_item().isEmpty()) { + + tokens.add(TOKEN_OPEN_PAREN); + + ctx.in_item().forEach(inItemContext -> { + + tokens.addAll(visit(inItemContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.collection_valued_input_parameter() != null) { + tokens.addAll(visit(ctx.collection_valued_input_parameter())); + } + + return tokens; + } + + @Override + public List visitIn_item(EqlParser.In_itemContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.literal() != null) { + tokens.addAll(visit(ctx.literal())); + } else if (ctx.single_valued_input_parameter() != null) { + tokens.addAll(visit(ctx.single_valued_input_parameter())); + } + + return tokens; + } + + @Override + public List visitLike_expression(EqlParser.Like_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.string_expression())); + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.LIKE())); + tokens.addAll(visit(ctx.pattern_value())); + + if (ctx.ESCAPE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ESCAPE())); + tokens.addAll(visit(ctx.escape_character())); + } + + return tokens; + } + + @Override + public List visitNull_comparison_expression(EqlParser.Null_comparison_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_path_expression())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.nullif_expression() != null) { + tokens.addAll(visit(ctx.nullif_expression())); + } + + tokens.add(new JpaQueryParsingToken(ctx.IS())); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.NULL())); + + return tokens; + } + + @Override + public List visitEmpty_collection_comparison_expression( + EqlParser.Empty_collection_comparison_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.collection_valued_path_expression())); + tokens.add(new JpaQueryParsingToken(ctx.IS())); + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.EMPTY())); + + return tokens; + } + + @Override + public List visitCollection_member_expression( + EqlParser.Collection_member_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.entity_or_value_expression())); + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.MEMBER())); + if (ctx.OF() != null) { + tokens.add(new JpaQueryParsingToken(ctx.OF())); + } + tokens.addAll(visit(ctx.collection_valued_path_expression())); + + return tokens; + } + + @Override + public List visitEntity_or_value_expression(EqlParser.Entity_or_value_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } else if (ctx.state_field_path_expression() != null) { + tokens.addAll(visit(ctx.state_field_path_expression())); + } else if (ctx.simple_entity_or_value_expression() != null) { + tokens.addAll(visit(ctx.simple_entity_or_value_expression())); + } + + return tokens; + } + + @Override + public List visitSimple_entity_or_value_expression( + EqlParser.Simple_entity_or_value_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.literal() != null) { + tokens.addAll(visit(ctx.literal())); + } + + return tokens; + } + + @Override + public List visitExists_expression(EqlParser.Exists_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.NOT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.NOT())); + } + tokens.add(new JpaQueryParsingToken(ctx.EXISTS())); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitAll_or_any_expression(EqlParser.All_or_any_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.ALL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ALL())); + } else if (ctx.ANY() != null) { + tokens.add(new JpaQueryParsingToken(ctx.ANY())); + } else if (ctx.SOME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.SOME())); + } + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitStringComparison(EqlParser.StringComparisonContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.string_expression(0))); + tokens.addAll(visit(ctx.comparison_operator())); + + if (ctx.string_expression(1) != null) { + tokens.addAll(visit(ctx.string_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + + return tokens; + } + + @Override + public List visitBooleanComparison(EqlParser.BooleanComparisonContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.boolean_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + + if (ctx.boolean_expression(1) != null) { + tokens.addAll(visit(ctx.boolean_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + + return tokens; + } + + @Override + public List visitDirectBooleanCheck(EqlParser.DirectBooleanCheckContext ctx) { + return visit(ctx.boolean_expression()); + } + + @Override + public List visitEnumComparison(EqlParser.EnumComparisonContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.enum_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + + if (ctx.enum_expression(1) != null) { + tokens.addAll(visit(ctx.enum_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + + return tokens; + } + + @Override + public List visitDatetimeComparison(EqlParser.DatetimeComparisonContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.datetime_expression(0))); + tokens.addAll(visit(ctx.comparison_operator())); + + if (ctx.datetime_expression(1) != null) { + tokens.addAll(visit(ctx.datetime_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + + return tokens; + } + + @Override + public List visitEntityComparison(EqlParser.EntityComparisonContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.entity_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + + if (ctx.entity_expression(1) != null) { + tokens.addAll(visit(ctx.entity_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + + return tokens; + } + + @Override + public List visitArithmeticComparison(EqlParser.ArithmeticComparisonContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.arithmetic_expression(0))); + tokens.addAll(visit(ctx.comparison_operator())); + + if (ctx.arithmetic_expression(1) != null) { + tokens.addAll(visit(ctx.arithmetic_expression(1))); + } else { + tokens.addAll(visit(ctx.all_or_any_expression())); + } + + return tokens; + } + + @Override + public List visitEntityTypeComparison(EqlParser.EntityTypeComparisonContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.entity_type_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.op)); + tokens.addAll(visit(ctx.entity_type_expression(1))); + + return tokens; + } + + @Override + public List visitRegexpComparison(EqlParser.RegexpComparisonContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.string_expression())); + tokens.add(new JpaQueryParsingToken(ctx.REGEXP())); + tokens.addAll(visit(ctx.string_literal())); + + return tokens; + } + + @Override + public List visitComparison_operator(EqlParser.Comparison_operatorContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.op)); + } + + @Override + public List visitArithmetic_expression(EqlParser.Arithmetic_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.arithmetic_expression() != null) { + + tokens.addAll(visit(ctx.arithmetic_expression())); + tokens.add(new JpaQueryParsingToken(ctx.op)); + tokens.addAll(visit(ctx.arithmetic_term())); + + } else { + tokens.addAll(visit(ctx.arithmetic_term())); + } + + return tokens; + } + + @Override + public List visitArithmetic_term(EqlParser.Arithmetic_termContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.arithmetic_term() != null) { + + tokens.addAll(visit(ctx.arithmetic_term())); + NOSPACE(tokens); + tokens.add(new JpaQueryParsingToken(ctx.op, false)); + tokens.addAll(visit(ctx.arithmetic_factor())); + } else { + tokens.addAll(visit(ctx.arithmetic_factor())); + } + + return tokens; + } + + @Override + public List visitArithmetic_factor(EqlParser.Arithmetic_factorContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.op != null) { + tokens.add(new JpaQueryParsingToken(ctx.op)); + } + tokens.addAll(visit(ctx.arithmetic_primary())); + + return tokens; + } + + @Override + public List visitArithmetic_primary(EqlParser.Arithmetic_primaryContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.numeric_literal() != null) { + tokens.addAll(visit(ctx.numeric_literal())); + } else if (ctx.arithmetic_expression() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.functions_returning_numerics() != null) { + tokens.addAll(visit(ctx.functions_returning_numerics())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.cast_function() != null) { + tokens.addAll(visit(ctx.cast_function())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitString_expression(EqlParser.String_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.string_literal() != null) { + tokens.addAll(visit(ctx.string_literal())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.functions_returning_strings() != null) { + tokens.addAll(visit(ctx.functions_returning_strings())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitDatetime_expression(EqlParser.Datetime_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.functions_returning_datetime() != null) { + tokens.addAll(visit(ctx.functions_returning_datetime())); + } else if (ctx.aggregate_expression() != null) { + tokens.addAll(visit(ctx.aggregate_expression())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.date_time_timestamp_literal() != null) { + tokens.addAll(visit(ctx.date_time_timestamp_literal())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitBoolean_expression(EqlParser.Boolean_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.boolean_literal() != null) { + tokens.addAll(visit(ctx.boolean_literal())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.function_invocation() != null) { + tokens.addAll(visit(ctx.function_invocation())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitEnum_expression(EqlParser.Enum_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.state_valued_path_expression() != null) { + tokens.addAll(visit(ctx.state_valued_path_expression())); + } else if (ctx.enum_literal() != null) { + tokens.addAll(visit(ctx.enum_literal())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } else if (ctx.case_expression() != null) { + tokens.addAll(visit(ctx.case_expression())); + } else if (ctx.subquery() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.subquery())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitEntity_expression(EqlParser.Entity_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } else if (ctx.simple_entity_expression() != null) { + tokens.addAll(visit(ctx.simple_entity_expression())); + } + + return tokens; + } + + @Override + public List visitSimple_entity_expression(EqlParser.Simple_entity_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.identification_variable() != null) { + tokens.addAll(visit(ctx.identification_variable())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } + + return tokens; + } + + @Override + public List visitEntity_type_expression(EqlParser.Entity_type_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.type_discriminator() != null) { + tokens.addAll(visit(ctx.type_discriminator())); + } else if (ctx.entity_type_literal() != null) { + tokens.addAll(visit(ctx.entity_type_literal())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } + + return tokens; + } + + @Override + public List visitType_discriminator(EqlParser.Type_discriminatorContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.TYPE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + + if (ctx.general_identification_variable() != null) { + tokens.addAll(visit(ctx.general_identification_variable())); + } else if (ctx.single_valued_object_path_expression() != null) { + tokens.addAll(visit(ctx.single_valued_object_path_expression())); + } else if (ctx.input_parameter() != null) { + tokens.addAll(visit(ctx.input_parameter())); + } + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitFunctions_returning_numerics( + EqlParser.Functions_returning_numericsContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.LENGTH() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LENGTH(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.LOCATE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LOCATE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.string_expression(1))); + NOSPACE(tokens); + if (ctx.arithmetic_expression() != null) { + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + } + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.ABS() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ABS(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.CEILING() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.CEILING(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.EXP() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.EXP(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.FLOOR() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.FLOOR(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.LN() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LN(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.SIGN() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.SIGN(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.SQRT() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.SQRT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.MOD() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.MOD(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + tokens.add(new JpaQueryParsingToken("/")); + tokens.addAll(visit(ctx.arithmetic_expression(1))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.POWER() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.POWER(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.arithmetic_expression(1))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.ROUND() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.ROUND(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.arithmetic_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.arithmetic_expression(1))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.SIZE() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.SIZE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.collection_valued_path_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.INDEX() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.INDEX(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.extract_datetime_field() != null) { + tokens.addAll(visit(ctx.extract_datetime_field())); + } + + return tokens; + } + + @Override + public List visitFunctions_returning_datetime( + EqlParser.Functions_returning_datetimeContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.CURRENT_DATE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_DATE())); + } else if (ctx.CURRENT_TIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_TIME())); + } else if (ctx.CURRENT_TIMESTAMP() != null) { + tokens.add(new JpaQueryParsingToken(ctx.CURRENT_TIMESTAMP())); + } else if (ctx.LOCAL() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LOCAL())); + + if (ctx.DATE() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DATE())); + } else if (ctx.TIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.TIME())); + } else if (ctx.DATETIME() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DATETIME())); + } + } else if (ctx.extract_datetime_part() != null) { + tokens.addAll(visit(ctx.extract_datetime_part())); + } + + return tokens; + } + + @Override + public List visitFunctions_returning_strings(EqlParser.Functions_returning_stringsContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.CONCAT() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.CONCAT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + ctx.string_expression().forEach(stringExpressionContext -> { + tokens.addAll(visit(stringExpressionContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.SUBSTRING() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.SUBSTRING(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + ctx.arithmetic_expression().forEach(arithmeticExpressionContext -> { + tokens.addAll(visit(arithmeticExpressionContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.TRIM() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.TRIM(), false)); + tokens.add(TOKEN_OPEN_PAREN); + if (ctx.trim_specification() != null) { + tokens.addAll(visit(ctx.trim_specification())); + } + if (ctx.trim_character() != null) { + tokens.addAll(visit(ctx.trim_character())); + } + if (ctx.FROM() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + } + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.LOWER() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.LOWER(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else if (ctx.UPPER() != null) { + + tokens.add(new JpaQueryParsingToken(ctx.UPPER(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.string_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + + return tokens; + } + + @Override + public List visitTrim_specification(EqlParser.Trim_specificationContext ctx) { + + if (ctx.LEADING() != null) { + return List.of(new JpaQueryParsingToken(ctx.LEADING())); + } else if (ctx.TRAILING() != null) { + return List.of(new JpaQueryParsingToken(ctx.TRAILING())); + } else { + return List.of(new JpaQueryParsingToken(ctx.BOTH())); + } + } + + @Override + public List visitCast_function(EqlParser.Cast_functionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CAST(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.single_valued_path_expression())); + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + + if (ctx.numeric_literal() != null) { + + tokens.add(TOKEN_OPEN_PAREN); + ctx.numeric_literal().forEach(numericLiteralContext -> { + tokens.addAll(visit(numericLiteralContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitFunction_invocation(EqlParser.Function_invocationContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.FUNCTION() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FUNCTION(), false)); + } else if (ctx.identification_variable() != null) { + + tokens.addAll(visit(ctx.identification_variable())); + NOSPACE(tokens); + } + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.function_name())); + NOSPACE(tokens); + ctx.function_arg().forEach(functionArgContext -> { + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(functionArgContext)); + NOSPACE(tokens); + }); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitExtract_datetime_field(EqlParser.Extract_datetime_fieldContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.EXTRACT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.datetime_field())); + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + tokens.addAll(visit(ctx.datetime_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitDatetime_field(EqlParser.Datetime_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitExtract_datetime_part(EqlParser.Extract_datetime_partContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.EXTRACT(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.datetime_part())); + tokens.add(new JpaQueryParsingToken(ctx.FROM())); + tokens.addAll(visit(ctx.datetime_expression())); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitDatetime_part(EqlParser.Datetime_partContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitFunction_arg(EqlParser.Function_argContext ctx) { + + if (ctx.literal() != null) { + return visit(ctx.literal()); + } else if (ctx.state_valued_path_expression() != null) { + return visit(ctx.state_valued_path_expression()); + } else if (ctx.input_parameter() != null) { + return visit(ctx.input_parameter()); + } else { + return visit(ctx.scalar_expression()); + } + } + + @Override + public List visitCase_expression(EqlParser.Case_expressionContext ctx) { + + if (ctx.general_case_expression() != null) { + return visit(ctx.general_case_expression()); + } else if (ctx.simple_case_expression() != null) { + return visit(ctx.simple_case_expression()); + } else if (ctx.coalesce_expression() != null) { + return visit(ctx.coalesce_expression()); + } else { + return visit(ctx.nullif_expression()); + } + } + + @Override + public List visitGeneral_case_expression(EqlParser.General_case_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CASE())); + + ctx.when_clause().forEach(whenClauseContext -> { + tokens.addAll(visit(whenClauseContext)); + }); + + tokens.add(new JpaQueryParsingToken(ctx.ELSE())); + tokens.addAll(visit(ctx.scalar_expression())); + tokens.add(new JpaQueryParsingToken(ctx.END())); + + return tokens; + } + + @Override + public List visitWhen_clause(EqlParser.When_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHEN())); + tokens.addAll(visit(ctx.conditional_expression())); + tokens.add(new JpaQueryParsingToken(ctx.THEN())); + tokens.addAll(visit(ctx.scalar_expression())); + + return tokens; + } + + @Override + public List visitSimple_case_expression(EqlParser.Simple_case_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.CASE())); + tokens.addAll(visit(ctx.case_operand())); + + ctx.simple_when_clause().forEach(simpleWhenClauseContext -> { + tokens.addAll(visit(simpleWhenClauseContext)); + }); + + tokens.add(new JpaQueryParsingToken(ctx.ELSE())); + tokens.addAll(visit(ctx.scalar_expression())); + tokens.add(new JpaQueryParsingToken(ctx.END())); + + return tokens; + } + + @Override + public List visitCase_operand(EqlParser.Case_operandContext ctx) { + + if (ctx.state_valued_path_expression() != null) { + return visit(ctx.state_valued_path_expression()); + } else { + return visit(ctx.type_discriminator()); + } + } + + @Override + public List visitSimple_when_clause(EqlParser.Simple_when_clauseContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WHEN())); + tokens.addAll(visit(ctx.scalar_expression(0))); + tokens.add(new JpaQueryParsingToken(ctx.THEN())); + tokens.addAll(visit(ctx.scalar_expression(1))); + + return tokens; + } + + @Override + public List visitCoalesce_expression(EqlParser.Coalesce_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.COALESCE(), false)); + tokens.add(TOKEN_OPEN_PAREN); + ctx.scalar_expression().forEach(scalarExpressionContext -> { + tokens.addAll(visit(scalarExpressionContext)); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + }); + CLIP(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitNullif_expression(EqlParser.Nullif_expressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.NULLIF(), false)); + tokens.add(TOKEN_OPEN_PAREN); + tokens.addAll(visit(ctx.scalar_expression(0))); + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + tokens.addAll(visit(ctx.scalar_expression(1))); + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + + return tokens; + } + + @Override + public List visitTrim_character(EqlParser.Trim_characterContext ctx) { + + if (ctx.CHARACTER() != null) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } else if (ctx.character_valued_input_parameter() != null) { + return visit(ctx.character_valued_input_parameter()); + } else { + return List.of(); + } + } + + @Override + public List visitIdentification_variable(EqlParser.Identification_variableContext ctx) { + + if (ctx.IDENTIFICATION_VARIABLE() != null) { + return List.of(new JpaQueryParsingToken(ctx.IDENTIFICATION_VARIABLE())); + } else if (ctx.f != null) { + return List.of(new JpaQueryParsingToken(ctx.f)); + } else { + return List.of(); + } + } + + @Override + public List visitConstructor_name(EqlParser.Constructor_nameContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.state_field_path_expression())); + NOSPACE(tokens); + + return tokens; + } + + @Override + public List visitLiteral(EqlParser.LiteralContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.STRINGLITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.STRINGLITERAL())); + } else if (ctx.INTLITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.INTLITERAL())); + } else if (ctx.FLOATLITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.FLOATLITERAL())); + } else if (ctx.LONGLITERAL() != null) { + tokens.add(new JpaQueryParsingToken(ctx.LONGLITERAL())); + } else if (ctx.boolean_literal() != null) { + tokens.addAll(visit(ctx.boolean_literal())); + } else if (ctx.entity_type_literal() != null) { + tokens.addAll(visit(ctx.entity_type_literal())); + } + + return tokens; + } + + @Override + public List visitInput_parameter(EqlParser.Input_parameterContext ctx) { + + List tokens = new ArrayList<>(); + + if (ctx.INTLITERAL() != null) { + + tokens.add(TOKEN_QUESTION_MARK); + tokens.add(new JpaQueryParsingToken(ctx.INTLITERAL())); + } else if (ctx.identification_variable() != null) { + + tokens.add(TOKEN_COLON); + tokens.addAll(visit(ctx.identification_variable())); + } + + return tokens; + } + + @Override + public List visitPattern_value(EqlParser.Pattern_valueContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.addAll(visit(ctx.string_expression())); + + return tokens; + } + + @Override + public List visitDate_time_timestamp_literal(EqlParser.Date_time_timestamp_literalContext ctx) { + + if (ctx.STRINGLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.STRINGLITERAL())); + } else if (ctx.DATELITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.DATELITERAL())); + } else if (ctx.TIMELITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.TIMELITERAL())); + } else if (ctx.TIMESTAMPLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.TIMESTAMPLITERAL())); + } else { + return List.of(); + } + } + + @Override + public List visitEntity_type_literal(EqlParser.Entity_type_literalContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitEscape_character(EqlParser.Escape_characterContext ctx) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } + + @Override + public List visitNumeric_literal(EqlParser.Numeric_literalContext ctx) { + + if (ctx.INTLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.INTLITERAL())); + } else if (ctx.FLOATLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.FLOATLITERAL())); + } else if (ctx.LONGLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.LONGLITERAL())); + } else { + return List.of(); + } + } + + @Override + public List visitBoolean_literal(EqlParser.Boolean_literalContext ctx) { + + if (ctx.TRUE() != null) { + return List.of(new JpaQueryParsingToken(ctx.TRUE())); + } else if (ctx.FALSE() != null) { + return List.of(new JpaQueryParsingToken(ctx.FALSE())); + } else { + return List.of(); + } + } + + @Override + public List visitEnum_literal(EqlParser.Enum_literalContext ctx) { + return visit(ctx.state_field_path_expression()); + } + + @Override + public List visitString_literal(EqlParser.String_literalContext ctx) { + + if (ctx.CHARACTER() != null) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } else if (ctx.STRINGLITERAL() != null) { + return List.of(new JpaQueryParsingToken(ctx.STRINGLITERAL())); + } else { + return List.of(); + } + } + + @Override + public List visitSingle_valued_embeddable_object_field( + EqlParser.Single_valued_embeddable_object_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitSubtype(EqlParser.SubtypeContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitCollection_valued_field(EqlParser.Collection_valued_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitSingle_valued_object_field(EqlParser.Single_valued_object_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitState_field(EqlParser.State_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitCollection_value_field(EqlParser.Collection_value_fieldContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitEntity_name(EqlParser.Entity_nameContext ctx) { + + List tokens = new ArrayList<>(); + + ctx.identification_variable().forEach(identificationVariableContext -> { + tokens.addAll(visit(identificationVariableContext)); + NOSPACE(tokens); + tokens.add(TOKEN_DOT); + }); + CLIP(tokens); + SPACE(tokens); + + return tokens; + } + + @Override + public List visitResult_variable(EqlParser.Result_variableContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitSuperquery_identification_variable( + EqlParser.Superquery_identification_variableContext ctx) { + return visit(ctx.identification_variable()); + } + + @Override + public List visitCollection_valued_input_parameter( + EqlParser.Collection_valued_input_parameterContext ctx) { + return visit(ctx.input_parameter()); + } + + @Override + public List visitSingle_valued_input_parameter( + EqlParser.Single_valued_input_parameterContext ctx) { + return visit(ctx.input_parameter()); + } + + @Override + public List visitFunction_name(EqlParser.Function_nameContext ctx) { + return visit(ctx.string_literal()); + } + + @Override + public List visitCharacter_valued_input_parameter( + EqlParser.Character_valued_input_parameterContext ctx) { + + if (ctx.CHARACTER() != null) { + return List.of(new JpaQueryParsingToken(ctx.CHARACTER())); + } else if (ctx.input_parameter() != null) { + return visit(ctx.input_parameter()); + } else { + return List.of(); + } + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryTransformer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryTransformer.java new file mode 100644 index 0000000000..4abc0a6609 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryTransformer.java @@ -0,0 +1,243 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +/** + * An ANTLR {@link org.antlr.v4.runtime.tree.ParseTreeVisitor} that transforms a parsed EQL query. + * + * @author Greg Turnquist + * @since 3.2 + */ +class EqlQueryTransformer extends EqlQueryRenderer { + + // TODO: Separate input from result parameters, encapsulation... + private final Sort sort; + private final boolean countQuery; + + private final @Nullable String countProjection; + + private @Nullable String primaryFromAlias = null; + + private List projection = Collections.emptyList(); + private boolean projectionProcessed; + + private boolean hasConstructorExpression = false; + + private JpaQueryTransformerSupport transformerSupport; + + EqlQueryTransformer() { + this(Sort.unsorted(), false, null); + } + + EqlQueryTransformer(Sort sort) { + this(sort, false, null); + } + + EqlQueryTransformer(boolean countQuery, @Nullable String countProjection) { + this(Sort.unsorted(), countQuery, countProjection); + } + + private EqlQueryTransformer(Sort sort, boolean countQuery, @Nullable String countProjection) { + + Assert.notNull(sort, "Sort must not be null"); + + this.sort = sort; + this.countQuery = countQuery; + this.countProjection = countProjection; + this.transformerSupport = new JpaQueryTransformerSupport(); + } + + @Nullable + public String getAlias() { + return this.primaryFromAlias; + } + + public List getProjection() { + return this.projection; + } + + public boolean hasConstructorExpression() { + return this.hasConstructorExpression; + } + + @Override + public List visitSelect_statement(EqlParser.Select_statementContext ctx) { + + List tokens = newArrayList(); + + tokens.addAll(visit(ctx.select_clause())); + tokens.addAll(visit(ctx.from_clause())); + + if (ctx.where_clause() != null) { + tokens.addAll(visit(ctx.where_clause())); + } + + if (ctx.groupby_clause() != null) { + tokens.addAll(visit(ctx.groupby_clause())); + } + + if (ctx.having_clause() != null) { + tokens.addAll(visit(ctx.having_clause())); + } + + if (!countQuery) { + + if (ctx.orderby_clause() != null) { + tokens.addAll(visit(ctx.orderby_clause())); + } + + if (sort.isSorted()) { + + if (ctx.orderby_clause() != null) { + + NOSPACE(tokens); + tokens.add(TOKEN_COMMA); + } else { + + SPACE(tokens); + tokens.add(TOKEN_ORDER_BY); + } + + tokens.addAll(transformerSupport.generateOrderByArguments(primaryFromAlias, sort)); + } + } + + return tokens; + } + + @Override + public List visitSelect_clause(EqlParser.Select_clauseContext ctx) { + + List tokens = newArrayList(); + + tokens.add(new JpaQueryParsingToken(ctx.SELECT())); + + if (countQuery) { + tokens.add(TOKEN_COUNT_FUNC); + } + + if (ctx.DISTINCT() != null) { + tokens.add(new JpaQueryParsingToken(ctx.DISTINCT())); + } + + List selectItemTokens = newArrayList(); + + ctx.select_item().forEach(selectItemContext -> { + selectItemTokens.addAll(visit(selectItemContext)); + NOSPACE(selectItemTokens); + selectItemTokens.add(TOKEN_COMMA); + }); + CLIP(selectItemTokens); + SPACE(selectItemTokens); + + if (countQuery) { + + if (countProjection != null) { + tokens.add(new JpaQueryParsingToken(countProjection)); + } else { + + if (ctx.DISTINCT() != null) { + + if (selectItemTokens.stream().anyMatch(jpqlToken -> jpqlToken.getToken().contains("new"))) { + // constructor + tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias)); + } else { + // keep all the select items to distinct against + tokens.addAll(selectItemTokens); + } + } else { + tokens.add(new JpaQueryParsingToken(() -> primaryFromAlias)); + } + } + + NOSPACE(tokens); + tokens.add(TOKEN_CLOSE_PAREN); + } else { + tokens.addAll(selectItemTokens); + } + + if (!projectionProcessed) { + projection = selectItemTokens; + projectionProcessed = true; + } + + return tokens; + } + + @Override + public List visitSelect_item(EqlParser.Select_itemContext ctx) { + + List tokens = super.visitSelect_item(ctx); + + if (ctx.result_variable() != null) { + transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + } + + return tokens; + } + + @Override + public List visitRange_variable_declaration(EqlParser.Range_variable_declarationContext ctx) { + + List tokens = newArrayList(); + + tokens.addAll(visit(ctx.entity_name())); + + if (ctx.AS() != null) { + tokens.add(new JpaQueryParsingToken(ctx.AS())); + } + + tokens.addAll(visit(ctx.identification_variable())); + + if (primaryFromAlias == null) { + primaryFromAlias = tokens.get(tokens.size() - 1).getToken(); + } + + return tokens; + } + + @Override + public List visitJoin(EqlParser.JoinContext ctx) { + + List tokens = super.visitJoin(ctx); + + transformerSupport.registerAlias(tokens.get(tokens.size() - 1).getToken()); + + return tokens; + } + + @Override + public List visitConstructor_expression(EqlParser.Constructor_expressionContext ctx) { + + hasConstructorExpression = true; + + return super.visitConstructor_expression(ctx); + } + + private static ArrayList newArrayList() { + return new ArrayList<>(); + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java index b44ca445ce..118b5af3da 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryEnhancer.java @@ -29,6 +29,7 @@ * @since 3.1 * @see JpqlQueryParser * @see HqlQueryParser + * @see EqlQueryParser */ class JpaQueryEnhancer implements QueryEnhancer { @@ -73,6 +74,20 @@ public static JpaQueryEnhancer forHql(DeclaredQuery query) { return new JpaQueryEnhancer(query, new HqlQueryParser(query.getQueryString())); } + /** + * Factory method to create a {@link JpaQueryParserSupport} for {@link DeclaredQuery} using EQL grammar. + * + * @param query must not be {@literal null}. + * @return a new {@link JpaQueryEnhancer} using EQL. + * @since 3.2 + */ + public static JpaQueryEnhancer forEql(DeclaredQuery query) { + + Assert.notNull(query, "DeclaredQuery must not be null!"); + + return new JpaQueryEnhancer(query, new EqlQueryParser(query.getQueryString())); + } + protected JpaQueryParserSupport getQueryParsingStrategy() { return queryParser; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java index 00717de3d3..4afe8bf3bd 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryParsingToken.java @@ -67,6 +67,12 @@ class JpaQueryParsingToken { public static final JpaQueryParsingToken TOKEN_MATERIALIZED = new JpaQueryParsingToken("materialized"); + public static final JpaQueryParsingToken TOKEN_NULLS = new JpaQueryParsingToken("NULLS"); + + public static final JpaQueryParsingToken TOKEN_FIRST = new JpaQueryParsingToken("FIRST"); + + public static final JpaQueryParsingToken TOKEN_LAST = new JpaQueryParsingToken("LAST"); + /** * The text value of the token. */ diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java index 74aa77e611..6ef6492b89 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactory.java @@ -17,6 +17,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.util.ClassUtils; /** @@ -34,18 +35,20 @@ public final class QueryEnhancerFactory { private static final boolean jSqlParserPresent = ClassUtils.isPresent("net.sf.jsqlparser.parser.JSqlParser", QueryEnhancerFactory.class.getClassLoader()); - private static final boolean hibernatePresent = ClassUtils.isPresent("org.hibernate.query.TypedParameterValue", - QueryEnhancerFactory.class.getClassLoader()); - static { if (jSqlParserPresent) { LOG.info("JSqlParser is in classpath; If applicable, JSqlParser will be used"); } - if (hibernatePresent) { + if (PersistenceProvider.ECLIPSELINK.isPresent()) { + LOG.info("EclipseLink is in classpath; If applicable, EQL parser will be used."); + } + + if (PersistenceProvider.HIBERNATE.isPresent()) { LOG.info("Hibernate is in classpath; If applicable, HQL parser will be used."); } + } private QueryEnhancerFactory() {} @@ -70,7 +73,13 @@ public static QueryEnhancer forQuery(DeclaredQuery query) { return new DefaultQueryEnhancer(query); } - return hibernatePresent ? JpaQueryEnhancer.forHql(query) : JpaQueryEnhancer.forJpql(query); + if (PersistenceProvider.HIBERNATE.isPresent()) { + return JpaQueryEnhancer.forHql(query); + } else if (PersistenceProvider.ECLIPSELINK.isPresent()) { + return JpaQueryEnhancer.forEql(query); + } else { + return JpaQueryEnhancer.forJpql(query); + } } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlComplianceTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlComplianceTests.java new file mode 100644 index 0000000000..44451ee2a2 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlComplianceTests.java @@ -0,0 +1,403 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.junit.jupiter.api.Test; + +/** + * Tests built around examples of EQL found in the EclipseLink's docs at + * https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL
    + *
    + * IMPORTANT: Purely verifies the parser without any transformations. + * + * @author Greg Turnquist + */ +class EqlComplianceTests { + + /** + * Parse the query using {@link EqlParser} then run it through the query-preserving {@link EqlQueryRenderer}. + * + * @param query + */ + private static String parseWithoutChanges(String query) { + + EqlLexer lexer = new EqlLexer(CharStreams.fromString(query)); + EqlParser parser = new EqlParser(new CommonTokenStream(lexer)); + + parser.addErrorListener(new BadJpqlGrammarErrorListener(query)); + + EqlParser.StartContext parsedQuery = parser.start(); + + return render(new EqlQueryRenderer().visit(parsedQuery)); + } + + private void assertQuery(String query) { + + String slimmedDownQuery = reduceWhitespace(query); + assertThat(parseWithoutChanges(slimmedDownQuery)).isEqualTo(slimmedDownQuery); + } + + private String reduceWhitespace(String original) { + + return original // + .replaceAll("[ \\t\\n]{1,}", " ") // + .trim(); + } + + @Test + void selectQueries() { + + assertQuery("Select e FROM Employee e WHERE e.salary > 100000"); + assertQuery("Select e FROM Employee e WHERE e.id = :id"); + assertQuery("Select MAX(e.salary) FROM Employee e"); + assertQuery("Select e.firstName FROM Employee e"); + assertQuery("Select e.firstName, e.lastName FROM Employee e"); + } + + @Test + void selectClause() { + + assertQuery("SELECT COUNT(e) FROM Employee e"); + assertQuery("SELECT MAX(e.salary) FROM Employee e"); + assertQuery("SELECT NEW com.acme.reports.EmpReport(e.firstName, e.lastName, e.salary) FROM Employee e"); + } + + @Test + void fromClause() { + + assertQuery("SELECT e FROM Employee e"); + assertQuery("SELECT e, a FROM Employee e, MailingAddress a WHERE e.address = a.address"); + assertQuery("SELECT e FROM com.acme.Employee e"); + } + + @Test + void join() { + + assertQuery("SELECT e FROM Employee e JOIN e.address a WHERE a.city = :city"); + assertQuery("SELECT e FROM Employee e JOIN e.projects p JOIN e.projects p2 WHERE p.name = :p1 AND p2.name = :p2"); + } + + @Test + void joinFetch() { + + assertQuery("SELECT e FROM Employee e JOIN FETCH e.address"); + assertQuery("SELECT e FROM Employee e JOIN FETCH e.address a ORDER BY a.city"); + } + + @Test + void leftJoin() { + assertQuery("SELECT e FROM Employee e LEFT JOIN e.address a ORDER BY a.city"); + } + + @Test + void on() { + + assertQuery("SELECT e FROM Employee e LEFT JOIN e.address ON a.city = :city"); + assertQuery("SELECT e FROM Employee e LEFT JOIN MailingAddress a ON e.address = a.address"); + } + + @Test + void subselectsInFromClause() { + assertQuery( + "SELECT e, c.city FROM Employee e, (SELECT DISTINCT a.city FROM Address a) c WHERE e.address.city = c.city"); + } + + @Test + void orderByClause() { + + assertQuery("SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC"); // Typo in EQL document + assertQuery("SELECT e FROM Employee e ORDER BY UPPER(e.lastName)"); + assertQuery("SELECT e FROM Employee e LEFT JOIN e.manager m ORDER BY m.lastName NULLS FIRST"); + assertQuery("SELECT e FROM Employee e ORDER BY e.address"); + } + + @Test + void groupByClause() { + + assertQuery("SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city"); + assertQuery("SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city ORDER BY AVG(e.salary)"); + assertQuery("SELECT e, COUNT(p) FROM Employee e LEFT JOIN e.projects p GROUP BY e"); + } + + @Test + void havingClause() { + assertQuery( + "SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city HAVING AVG(e.salary) > 100000"); + } + + @Test + void union() { + + assertQuery(""" + SELECT MAX(e.salary) FROM Employee e WHERE e.address.city = :city1 + UNION SELECT MAX(e.salary) FROM Employee e WHERE e.address.city = :city2 + """); + assertQuery(""" + SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode1 + INTERSECT SELECT e FROM Employee e JOIN e.phones p WHERE p.areaCode = :areaCode2 + """); + assertQuery(""" + SELECT e FROM Employee e + EXCEPT SELECT e FROM Employee e WHERE e.salary > e.manager.salary + """); + } + + @Test + void whereClause() { + // TBD + } + + @Test + void updateQueries() { + assertQuery("UPDATE Employee e SET e.salary = 60000 WHERE e.salary = 50000"); + } + + @Test + void deleteQueries() { + assertQuery("DELETE FROM Employee e WHERE e.department IS NULL"); + } + + @Test + void literals() { + + assertQuery("SELECT e FROM Employee e WHERE e.name = 'Bob'"); + assertQuery("SELECT e FROM Employee e WHERE e.id = 1234"); + assertQuery("SELECT e FROM Employee e WHERE e.id = 1234L"); + assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14F"); + assertQuery("SELECT s FROM Stat s WHERE s.ratio > 3.14e32D"); + assertQuery("SELECT e FROM Employee e WHERE e.active = TRUE"); + assertQuery("SELECT e FROM Employee e WHERE e.startDate = {d'2012-01-03'}"); + assertQuery("SELECT e FROM Employee e WHERE e.startTime = {t'09:00:00'}"); + assertQuery("SELECT e FROM Employee e WHERE e.version = {ts'2012-01-03 09:00:00.000000001'}"); + assertQuery("SELECT e FROM Employee e WHERE e.gender = org.acme.Gender.MALE"); + assertQuery("UPDATE Employee e SET e.manager = NULL WHERE e.manager = :manager"); + } + + @Test + void functionsInSelect() { + + assertQuery("SELECT e.salary - 1000 FROM Employee e"); + assertQuery("SELECT e.salary + 1000 FROM Employee e"); + assertQuery("SELECT e.salary*2 FROM Employee e"); + assertQuery("SELECT e.salary*2.0 FROM Employee e"); + assertQuery("SELECT e.salary/2 FROM Employee e"); + assertQuery("SELECT e.salary/2.0 FROM Employee e"); + assertQuery("SELECT ABS(e.salary - e.manager.salary) FROM Employee e"); + assertQuery( + "select e from Employee e where case e.firstName when 'Bob' then 'Robert' when 'Jill' then 'Gillian' else '' end = 'Robert'"); + assertQuery( + "select case when e.firstName = 'Bob' then 'Robert' when e.firstName = 'Jill' then 'Gillian' else '' end from Employee e where e.firstName = 'Bob' or e.firstName = 'Jill'"); + assertQuery( + "select e from Employee e where case when e.firstName = 'Bob' then 'Robert' when e.firstName = 'Jill' then 'Gillian' else '' end = 'Robert'"); + assertQuery("SELECT COALESCE(e.salary, 0) FROM Employee e"); + assertQuery("SELECT CONCAT(e.firstName, ' ', e.lastName) FROM Employee e"); + assertQuery("SELECT e.name, CURRENT_DATE FROM Employee e"); + assertQuery("SELECT e.name, CURRENT_TIME FROM Employee e"); + assertQuery("SELECT e.name, CURRENT_TIMESTAMP FROM Employee e"); + assertQuery("SELECT LENGTH(e.lastName) FROM Employee e"); + assertQuery("SELECT LOWER(e.lastName) FROM Employee e"); + assertQuery("SELECT MOD(e.hoursWorked / 8) FROM Employee e"); + assertQuery("SELECT NULLIF(e.salary, 0) FROM Employee e"); + assertQuery("SELECT SQRT(o.RESULT) FROM Output o"); + assertQuery("SELECT SUBSTRING(e.lastName, 0, 2) FROM Employee e"); + assertQuery( + "SELECT TRIM(TRAILING FROM e.lastName), TRIM(e.lastName), TRIM(LEADING '-' FROM e.lastName) FROM Employee e"); + assertQuery("SELECT UPPER(e.lastName) FROM Employee e"); + assertQuery("SELECT CAST(e.salary NUMERIC(10, 2)) FROM Employee e"); + assertQuery("SELECT EXTRACT(YEAR FROM e.startDate) FROM Employee e"); + assertQuery("SELECT e FROM Employee e WHERE e.lastName REGEXP '^Dr.*'"); + assertQuery("SELECT e FROM Employee e WHERE e.lastName REGEXP '^Dr\\.*'"); + } + + @Test + void functionsInWhere() { + + assertQuery("SELECT e FROM Employee e WHERE e.salary - 1000 > 0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary + 1000 > 0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary*2 > 0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary*2.0 > 0.0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary/2 > 0"); + assertQuery("SELECT e FROM Employee e WHERE e.salary/2.0 > 0.0"); + assertQuery("SELECT e FROM Employee e WHERE ABS(e.salary - e.manager.salary) > 0"); + assertQuery("SELECT e FROM Employee e WHERE COALESCE(e.salary, 0) > 0"); + assertQuery("SELECT e FROM Employee e WHERE CONCAT(e.firstName, ' ', e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE CURRENT_DATE > CURRENT_TIME"); + assertQuery("SELECT e FROM Employee e WHERE CURRENT_TIME > CURRENT_TIMESTAMP"); + assertQuery("SELECT e FROM Employee e WHERE LENGTH(e.lastName) > 0"); + assertQuery("SELECT e FROM Employee e WHERE LOWER(e.lastName) = 'bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE MOD(e.hoursWorked / 8) > 0"); + assertQuery("SELECT e FROM Employee e WHERE NULLIF(e.salary, 0) is null"); + assertQuery("SELECT e FROM Employee e WHERE SQRT(o.RESULT) > 0.0"); + assertQuery("SELECT e FROM Employee e WHERE SUBSTRING(e.lastName, 0, 2) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE TRIM(TRAILING FROM e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE TRIM(e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE TRIM(LEADING '-' FROM e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e WHERE UPPER(e.lastName) = 'BILBO'"); + assertQuery("SELECT e FROM Employee e WHERE CAST(e.salary NUMERIC(10, 2)) > 0.0"); + assertQuery("SELECT e FROM Employee e WHERE EXTRACT(YEAR FROM e.startDate) = '2023'"); + } + + @Test + void functionsInOrderBy() { + + assertQuery("SELECT e FROM Employee e ORDER BY e.salary - 1000"); + assertQuery("SELECT e FROM Employee e ORDER BY e.salary + 1000"); + assertQuery("SELECT e FROM Employee e ORDER BY e.salary*2"); + assertQuery("SELECT e FROM Employee e ORDER BY e.salary*2.0"); + assertQuery("SELECT e FROM Employee e ORDER BY e.salary/2"); + assertQuery("SELECT e FROM Employee e ORDER BY e.salary/2.0"); + assertQuery("SELECT e FROM Employee e ORDER BY ABS(e.salary - e.manager.salary)"); + assertQuery("SELECT e FROM Employee e ORDER BY COALESCE(e.salary, 0)"); + assertQuery("SELECT e FROM Employee e ORDER BY CONCAT(e.firstName, ' ', e.lastName)"); + assertQuery("SELECT e FROM Employee e ORDER BY CURRENT_DATE"); + assertQuery("SELECT e FROM Employee e ORDER BY CURRENT_TIME"); + assertQuery("SELECT e FROM Employee e ORDER BY CURRENT_TIMESTAMP"); + assertQuery("SELECT e FROM Employee e ORDER BY LENGTH(e.lastName)"); + assertQuery("SELECT e FROM Employee e ORDER BY LOWER(e.lastName)"); + assertQuery("SELECT e FROM Employee e ORDER BY MOD(e.hoursWorked / 8)"); + assertQuery("SELECT e FROM Employee e ORDER BY NULLIF(e.salary, 0)"); + assertQuery("SELECT e FROM Employee e ORDER BY SQRT(o.RESULT)"); + assertQuery("SELECT e FROM Employee e ORDER BY SUBSTRING(e.lastName, 0, 2)"); + assertQuery("SELECT e FROM Employee e ORDER BY TRIM(TRAILING FROM e.lastName)"); + assertQuery("SELECT e FROM Employee e ORDER BY TRIM(e.lastName)"); + assertQuery("SELECT e FROM Employee e ORDER BY TRIM(LEADING '-' FROM e.lastName)"); + assertQuery("SELECT e FROM Employee e ORDER BY UPPER(e.lastName)"); + assertQuery("SELECT e FROM Employee e ORDER BY CAST(e.salary NUMERIC(10, 2))"); + assertQuery("SELECT e FROM Employee e ORDER BY EXTRACT(YEAR FROM e.startDate)"); + } + + @Test + void functionsInGroupBy() { + + assertQuery("SELECT e FROM Employee e GROUP BY e.salary - 1000"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary + 1000"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary*2"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary*2.0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary/2"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary/2.0"); + assertQuery("SELECT e FROM Employee e GROUP BY ABS(e.salary - e.manager.salary)"); + assertQuery("SELECT e FROM Employee e GROUP BY COALESCE(e.salary, 0)"); + assertQuery("SELECT e FROM Employee e GROUP BY CONCAT(e.firstName, ' ', e.lastName)"); + assertQuery("SELECT e FROM Employee e GROUP BY CURRENT_DATE"); + assertQuery("SELECT e FROM Employee e GROUP BY CURRENT_TIME"); + assertQuery("SELECT e FROM Employee e GROUP BY CURRENT_TIMESTAMP"); + assertQuery("SELECT e FROM Employee e GROUP BY LENGTH(e.lastName)"); + assertQuery("SELECT e FROM Employee e GROUP BY LOWER(e.lastName)"); + assertQuery("SELECT e FROM Employee e GROUP BY MOD(e.hoursWorked / 8)"); + assertQuery("SELECT e FROM Employee e GROUP BY NULLIF(e.salary, 0)"); + assertQuery("SELECT e FROM Employee e GROUP BY SQRT(o.RESULT)"); + assertQuery("SELECT e FROM Employee e GROUP BY SUBSTRING(e.lastName, 0, 2)"); + assertQuery("SELECT e FROM Employee e GROUP BY TRIM(TRAILING FROM e.lastName)"); + assertQuery("SELECT e FROM Employee e GROUP BY TRIM(e.lastName)"); + assertQuery("SELECT e FROM Employee e GROUP BY TRIM(LEADING '-' FROM e.lastName)"); + assertQuery("SELECT e FROM Employee e GROUP BY UPPER(e.lastName)"); + assertQuery("SELECT e FROM Employee e GROUP BY CAST(e.salary NUMERIC(10, 2))"); + assertQuery("SELECT e FROM Employee e GROUP BY EXTRACT(YEAR FROM e.startDate)"); + } + + @Test + void functionsInHaving() { + + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING e.salary - 1000 > 0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING e.salary + 1000 > 0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING e.salary*2 > 0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING e.salary*2.0 > 0.0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING e.salary/2 > 0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING e.salary/2.0 > 0.0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING ABS(e.salary - e.manager.salary) > 0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING COALESCE(e.salary, 0) > 0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING CONCAT(e.firstName, ' ', e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING CURRENT_DATE > CURRENT_TIME"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING CURRENT_TIME > CURRENT_TIMESTAMP"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING LENGTH(e.lastName) > 0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING LOWER(e.lastName) = 'bilbo'"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING MOD(e.hoursWorked / 8) > 0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING NULLIF(e.salary, 0) is null"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING SQRT(o.RESULT) > 0.0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING SUBSTRING(e.lastName, 0, 2) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING TRIM(TRAILING FROM e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING TRIM(e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING TRIM(LEADING '-' FROM e.lastName) = 'Bilbo'"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING UPPER(e.lastName) = 'BILBO'"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING CAST(e.salary NUMERIC(10, 2)) > 0.0"); + assertQuery("SELECT e FROM Employee e GROUP BY e.salary HAVING EXTRACT(YEAR FROM e.startDate) = '2023'"); + } + + @Test + void specialOperators() { + + assertQuery("SELECT toDo FROM Employee e JOIN e.toDoList toDo WHERE INDEX(toDo) = 1"); + assertQuery("SELECT p FROM Employee e JOIN e.priorities p WHERE KEY(p) = 'high'"); + assertQuery("SELECT e FROM Employee e WHERE SIZE(e.managedEmployees) < 2"); + assertQuery("SELECT e FROM Employee e WHERE e.managedEmployees IS EMPTY"); + assertQuery("SELECT e FROM Employee e WHERE 'write code' MEMBER OF e.responsibilities"); + assertQuery("SELECT p FROM Project p WHERE TYPE(p) = LargeProject"); + + /** + * NOTE: The following query has been altered to properly align with EclipseLink test code despite NOT matching + * their ref docs. See https://github.com/eclipse-ee4j/eclipselink/issues/1949 for more details. + */ + assertQuery("SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) p WHERE p.budget > 1000000"); + + assertQuery("SELECT p FROM Phone p WHERE FUNCTION('TO_NUMBER', p.areaCode) > 613"); + } + + @Test + void eclipseLinkSpecialOperators() { + + assertQuery("SELECT p FROM Phone p WHERE FUNC('TO_NUMBER', e.areaCode) > 613"); + assertQuery("SELECT FUNC('YEAR', e.startDate) AS YEAR, COUNT(e) FROM Employee e GROUP BY YEAR"); + assertQuery( + "SELECT a FROM Asset a, Geography geo WHERE geo.id = :id AND a.id IN :id_list AND FUNC('ST_INTERSECTS', a.geometry, geo.geometry) = 'TRUE'"); + assertQuery( + "SELECT s FROM SimpleSpatial s WHERE FUNC('MDSYS.SDO_RELATE', s.jGeometry, :otherGeometry, :params) = 'TRUE' ORDER BY s.id ASC"); + assertQuery("SELECT e FROM Employee e WHERE OPERATOR('ExtractXml', e.resume, '@years-experience') > 10"); + } + + @Test + void sql() { + + assertQuery("SELECT p FROM Phone p WHERE SQL('CAST(? AS CHAR(3))', e.areaCode) = '613'"); + assertQuery("SELECT SQL('EXTRACT(YEAR FROM ?)', e.startDate) AS YEAR, COUNT(e) FROM Employee e GROUP BY YEAR"); + assertQuery("SELECT e FROM Employee e ORDER BY SQL('? NULLS FIRST', e.startDate)"); + assertQuery("SELECT e FROM Employee e WHERE e.startDate = SQL('(SELECT SYSDATE FROM DUAL)')"); + } + + @Test + void column() { + + assertQuery("SELECT e FROM Employee e WHERE COLUMN('MANAGER_ID', e) = :id"); + assertQuery("SELECT e FROM Employee e WHERE COLUMN('ROWID', e) = :id"); + } + + @Test + void table() { + assertQuery( + "SELECT e, a.LAST_UPDATE_USER FROM Employee e, TABLE('AUDIT') a WHERE a.TABLE = 'EMPLOYEE' AND a.ROWID = COLUMN('ROWID', e)"); + } + + @Test // GH-3175 + void coalesceFunctions() { + + assertQuery("SELECT b FROM Bundle b WHERE coalesce(b.deleted, false) AND b.latestImport = true"); + assertQuery("SELECT b FROM Bundle b WHERE NOT coalesce(b.deleted, false) AND b.latestImport = true"); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlParserQueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlParserQueryEnhancerUnitTests.java new file mode 100644 index 0000000000..c0ff059955 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlParserQueryEnhancerUnitTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assumptions.*; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * TCK Tests for {@link EqlQueryParser} mixed into {@link JpaQueryEnhancer}. + * + * @author Greg Turnquist + */ +public class EqlParserQueryEnhancerUnitTests extends QueryEnhancerTckTests { + + @Override + QueryEnhancer createQueryEnhancer(DeclaredQuery query) { + + assumeThat(query.isNativeQuery()).isFalse(); + + return JpaQueryEnhancer.forEql(query); + } + + @Override + @ParameterizedTest // GH-2773 + @MethodSource("jpqlCountQueries") + void shouldDeriveJpqlCountQuery(String query, String expected) { + + assumeThat(query).as("EqlParser replaces the column name with alias name for count queries") // + .doesNotContain("SELECT name FROM table_name some_alias"); + + assumeThat(query).as("EqlParser does not support simple JPQL syntax") // + .doesNotStartWithIgnoringCase("FROM"); + + assumeThat(expected).as("EqlParser does turn 'select a.b' into 'select count(a.b)'") // + .doesNotContain("select count(a.b"); + + super.shouldDeriveJpqlCountQuery(query, expected); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java new file mode 100644 index 0000000000..72635392a8 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryRendererTests.java @@ -0,0 +1,995 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jpa.repository.query.JpaQueryParsingToken.*; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Tests built around examples of EQL found in the JPA spec + * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc
    + *
    + * IMPORTANT: Purely verifies the parser without any transformations. + * + * @author Greg Turnquist + */ +class EqlQueryRendererTests { + + private static final String SPEC_FAULT = "Disabled due to spec fault> "; + + /** + * Parse the query using {@link EqlParser} then run it through the query-preserving {@link EqlQueryRenderer}. + * + * @param query + */ + private static String parseWithoutChanges(String query) { + + EqlLexer lexer = new EqlLexer(CharStreams.fromString(query)); + EqlParser parser = new EqlParser(new CommonTokenStream(lexer)); + + parser.addErrorListener(new BadJpqlGrammarErrorListener(query)); + + EqlParser.StartContext parsedQuery = parser.start(); + + return render(new EqlQueryRenderer().visit(parsedQuery)); + } + + private void assertQuery(String query) { + + String slimmedDownQuery = reduceWhitespace(query); + assertThat(parseWithoutChanges(slimmedDownQuery)).isEqualTo(slimmedDownQuery); + } + + private String reduceWhitespace(String original) { + + return original // + .replaceAll("[ \\t\\n]{1,}", " ") // + .trim(); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + */ + @Test + void joinExample1() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order AS o JOIN o.lineItems AS l + WHERE l.shipped = FALSE + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables + */ + @Test + void joinExample2() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l JOIN l.product p + WHERE p.productType = 'office_supplies' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations + */ + @Test + void rangeVariableDeclarations() { + + assertQuery(""" + SELECT DISTINCT o1 + FROM Order o1, Order o2 + WHERE o1.quantity > o2.quantity AND + o2.customer.lastname = 'Smith' AND + o2.customer.firstname = 'John' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample1() { + + assertQuery(""" + SELECT i.name, VALUE(p) + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample2() { + + assertQuery(""" + SELECT i.name, p + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample3() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo.phones p + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample4() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE e.contactInfo.address.zipcode = '95054' + """); + } + + @Test + void pathExpressionSyntaxExample1() { + + assertQuery(""" + SELECT DISTINCT l.product + FROM Order AS o JOIN o.lineItems l + """); + } + + @Test + void joinsExample1() { + + assertQuery(""" + SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize + """); + } + + @Test + void joinsExample2() { + + assertQuery(""" + SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInnerExample() { + + assertQuery(""" + SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInExample() { + + assertQuery(""" + SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 + """); + } + + @Test + void doubleJoinExample() { + + assertQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE c.address.zipcode = '95054' + """); + } + + @Test + void leftJoinExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + GROUP BY s.name + """); + } + + @Test + void leftJoinOnExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + ON p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinWhereExample() { + + assertQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + WHERE p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinFetchExample() { + + assertQuery(""" + SELECT d + FROM Department d LEFT JOIN FETCH d.employees + WHERE d.deptno = 1 + """); + } + + @Test + void collectionMemberExample() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void collectionMemberInExample() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o, IN(o.lineItems) l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void fromClauseExample() { + + assertQuery(""" + SELECT o + FROM Order AS o JOIN o.lineItems l JOIN l.product p + """); + } + + @Test + void fromClauseDowncastingExample1() { + + assertQuery(""" + SELECT b.name, b.ISBN + FROM Order o JOIN TREAT(o.product AS Book) b + """); + } + + @Test + void fromClauseDowncastingExample2() { + + assertQuery(""" + SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp + WHERE lp.budget > 1000 + """); + } + + /** + * @see #fromClauseDowncastingExample3fixed() + */ + @Test + @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") + void fromClauseDowncastingExample3_SPEC_BUG() { + + assertQuery(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE "cost overrun" + """); + } + + @Test + void fromClauseDowncastingExample3fixed() { + + assertQuery(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE 'cost overrun' + """); + } + + @Test + void fromClauseDowncastingExample4() { + + assertQuery(""" + SELECT e FROM Employee e + WHERE TREAT(e AS Exempt).vacationDays > 10 + OR TREAT(e AS Contractor).hours > 100 + """); + } + + @Test + void pathExpressionsNamedParametersExample() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE c.status = :stat + """); + } + + @Test + void betweenExpressionsExample() { + + assertQuery(""" + SELECT t + FROM CreditCard c JOIN c.transactionHistory t + WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 + """); + } + + @Test + void isEmptyExample() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void memberOfExample() { + + assertQuery(""" + SELECT p + FROM Person p + WHERE 'Joe' MEMBER OF p.nicknames + """); + } + + @Test + void existsSubSelectExample1() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS (SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void allExample() { + + assertQuery(""" + SELECT emp + FROM Employee emp + WHERE emp.salary > ALL (SELECT m.salary + FROM Manager m + WHERE m.department = emp.department) + """); + } + + @Test + void existsSubSelectExample2() { + + assertQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS (SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void subselectNumericComparisonExample1() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 + """); + } + + @Test + void subselectNumericComparisonExample2() { + + assertQuery(""" + SELECT goodCustomer + FROM Customer goodCustomer + WHERE goodCustomer.balanceOwed < (SELECT AVG(c.balanceOwed)/2.0 FROM Customer c) + """); + } + + @Test + void indexExample() { + + assertQuery(""" + SELECT w.name + FROM Course c JOIN c.studentWaitlist w + WHERE c.name = 'Calculus' + AND INDEX(w) = 0 + """); + } + + /** + * @see #functionInvocationExampleWithCorrection() + */ + @Test + @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") + void functionInvocationExample_SPEC_BUG() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) + """); + } + + @Test + void functionInvocationExampleWithCorrection() { + + assertQuery(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE + """); + } + + @Test + void updateCaseExample1() { + + assertQuery(""" + UPDATE Employee e + SET e.salary = + CASE WHEN e.rating = 1 THEN e.salary*1.1 + WHEN e.rating = 2 THEN e.salary*1.05 + ELSE e.salary*1.01 + END + """); + } + + @Test + void updateCaseExample2() { + + assertQuery(""" + UPDATE Employee e + SET e.salary = + CASE e.rating WHEN 1 THEN e.salary*1.1 + WHEN 2 THEN e.salary*1.05 + ELSE e.salary*1.01 + END + """); + } + + @Test + void selectCaseExample1() { + + assertQuery(""" + SELECT e.name, + CASE TYPE(e) WHEN Exempt THEN 'Exempt' + WHEN Contractor THEN 'Contractor' + WHEN Intern THEN 'Intern' + ELSE 'NonExempt' + END + FROM Employee e + WHERE e.dept.name = 'Engineering' + """); + } + + @Test + void selectCaseExample2() { + + assertQuery(""" + SELECT e.name, + f.name, + CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' + WHEN f.annualMiles > 25000 THEN 'Gold ' + ELSE '' + END, + 'Frequent Flyer') + FROM Employee e JOIN e.frequentFlierPlan f + """); + } + + @Test + void inClauseWithTypeLiteralsShouldWork() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (Exempt, Contractor) + """); + } + + @Test + void inClauseWithParametersShouldWork() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (:empType1, :empType2) + """); + } + + @Test + void inClauseWithSingleParameterShouldWork() { + + assertQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN :empTypes + """); + } + + @Test + void notEqualsForTypeShouldWork() { + + assertQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) <> Exempt + """); + } + + @Test + void havingWithInClauseShouldWork() { + + assertQuery(""" + SELECT c.status, AVG(c.filledOrderCount), COUNT(c) + FROM Customer c + GROUP BY c.status + HAVING c.status IN (1, 2) + """); + } + + @Test + void havingClauseWithComparisonShouldWork() { + + assertQuery(""" + SELECT c.country, COUNT(c) + FROM Customer c + GROUP BY c.country + HAVING COUNT(c) > 30 + """); + } + + @Test + void havingClauseWithAnotherComparisonShouldWork() { + + assertQuery(""" + SELECT c, COUNT(o) + FROM Customer c JOIN c.orders o + GROUP BY c + HAVING COUNT(o) >= 5 + """); + } + + @Test + void whereClauseWithComparisonShouldWork() { + + assertQuery(""" + SELECT c.id, c.status + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void keyValueFunctionsShouldWork() { + + assertQuery(""" + SELECT v.location.street, KEY(i).title, VALUE(i) + FROM VideoStore v JOIN v.videoInventory i + WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 + """); + } + + @Test + void fromClauseWithAsShouldWork() { + + assertQuery(""" + SELECT o.lineItems FROM Order AS o + """); + } + + @Test + void countFunctionWithAsClauseShouldWork() { + + assertQuery(""" + SELECT c, COUNT(l) AS itemCount + FROM Customer c JOIN c.Orders o JOIN o.lineItems l + WHERE c.address.state = 'CA' + GROUP BY c + ORDER BY itemCount + """); + } + + @Test + void objectConstructionShouldWork() { + + assertQuery(""" + SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void selectWithAsClauseShouldWork() { + + assertQuery(""" + SELECT e.address AS addr + FROM Employee e + """); + } + + @Test + void averageFunctionShouldWork() { + + assertQuery(""" + SELECT AVG(o.quantity) FROM Order o + """); + } + + @Test + void sumFunctionShouldWork() { + + assertQuery(""" + SELECT SUM(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void countFunctionShouldWork() { + + assertQuery(""" + SELECT COUNT(o) FROM Order o + """); + } + + @Test + void countFunctionOnSubElementShouldWork() { + + assertQuery(""" + SELECT COUNT(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void equivalentCountFunctionShouldAlsoWork() { + + assertQuery(""" + SELECT COUNT(l) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL + """); + } + + @Test + void orderByBasedOnSelectClauseShouldWork() { + + assertQuery(""" + SELECT o + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity DESC, o.totalcost + """); + } + + @Test + void orderByThatMatchesSelectClauseShouldWork() { + + assertQuery(""" + SELECT o.quantity, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity, a.zipcode + """); + } + + @Test + void orderByThatMatchesAllSelectAliasesShouldWork() { + + assertQuery(""" + SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' AND a.county = 'Santa Clara' + ORDER BY o.quantity, taxedCost, a.zipcode + """); + } + + @Test + void orderByThatMatchesSelectFunctionAliasShouldWork() { + + assertQuery(""" + SELECT AVG(o.quantity) as q, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + GROUP BY a.zipcode + ORDER BY q DESC + """); + } + + /** + * NOTE: This query is specifically dubbed illegal in the spec. However, it's not due to a grammar failure but instead + * for semantic reasons. Our parser does NOT check if the ORDER BY clause matches the SELECT or not. Hence, this is + * left to the JPA provider. + */ + @Test + void orderByClauseThatIsNotReflectedInTheSelectClause() { + + assertQuery(""" + SELECT p.product_name + FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY p.price + """); + } + + /** + * NOTE: This query is specifically dubbed illegal in the spec. It may actually be failing for a different reason. + */ + @Test + void orderByClauseThatIsNotReflectedInTheSelectClauseButAlsoHasAnInClauseInTheFromClause() { + + assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> { + assertQuery(""" + SELECT p.product_name + FROM Order o, IN(o.lineItems) l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY o.quantity + """); + }); + } + + @Test + void simpleDeleteShouldWork() { + + assertQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + """); + } + + @Test + void deleteWithMoreComplexCriteriaShouldWork() { + + assertQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + AND c.orders IS EMPTY + """); + } + + @Test + void simpleUpdateShouldWork() { + + assertQuery(""" + UPDATE Customer c + SET c.status = 'outstanding' + WHERE c.balance < 10000 + """); + } + + @Test + void moreComplexUpdateShouldWork() { + + assertQuery(""" + UPDATE Employee e + SET e.address.building = 22 + WHERE e.address.building = 14 + AND e.address.city = 'Santa Clara' + AND e.project = 'Jakarta EE' + """); + } + + @Test + void simpleSelectShouldWork() { + + assertQuery(""" + SELECT o + FROM Order o + """); + } + + @Test + void selectWithWhereClauseShouldWork() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.shippingAddress.state = 'CA' + """); + } + + @Test + void selectWithDistinctSubElementShouldWork() { + + assertQuery(""" + SELECT DISTINCT o.shippingAddress.state + FROM Order o + """); + } + + @Test + void selectWithSimpleDistinctShouldWork() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + """); + } + + @Test + void selectWithIsNotEmptyCriteriaShouldWork() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS NOT EMPTY + """); + } + + @Test + void selectWithIsEmptyCriteriaShouldWork() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void findAllPendingOrders() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.shipped = FALSE + """); + } + + @Test + void findAllOrdersWhereShippingAddressDoesNotMatchBillingAddress() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE + NOT (o.shippingAddress.state = o.billingAddress.state AND + o.shippingAddress.city = o.billingAddress.city AND + o.shippingAddress.street = o.billingAddress.street) + """); + } + + @Test + void simplerVersionOfShippingAddressNotMatchingBillingAddress() { + + assertQuery(""" + SELECT o + FROM Order o + WHERE o.shippingAddress <> o.billingAddress + """); + } + + @Test + void findOrdersThatHaveProductNamedByAParameter() { + + assertQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.name = ?1 + """); + } + + @Test // GH-2982 + void floorShouldBeValidEntityName() { + + assertQuery(""" + SELECT f + FROM Floor f + WHERE f.name = :name + """); + + assertQuery(""" + SELECT r + FROM Room r + JOIN r.floor f + WHERE f.name = :name + """); + } + + @Test // GH-2994 + void queryWithSignShouldWork() { + assertQuery("select t.sign from TestEntity t"); + } + + @Test // GH-3028 + void queryWithValueShouldWork() { + assertQuery("select t.value from TestEntity t"); + } + + @Test // GH-3062, GH-3056 + void typeShouldBeAValidParameter() { + + assertQuery("select e from Employee e where e.type = :_type"); + assertQuery("select te from TestEntity te where te.type = :type"); + } + + @Test // GH-3061 + void alternateNotEqualsOperatorShouldWork() { + assertQuery("select e from Employee e where e.firstName != :name"); + } + + @Test // GH-3092 + void dateAndFromShouldBeValidNames() { + assertQuery("SELECT e FROM Entity e WHERE e.embeddedId.date BETWEEN :from AND :to"); + } + + @Test // GH-3092 + void timeShouldBeAValidParameterName() { + assertQuery(""" + UPDATE Lock L + SET L.isLocked = TRUE, L.forceUnlockTime = :forceUnlockTime + WHERE L.isLocked = FALSE OR L.forceUnlockTime < :time + """); + } + + @Test // GH-3128 + void newShouldBeLegalAsPartOfAStateFieldPathExpression() { + + assertQuery(""" + SELECT j + FROM AgentUpdateTask j + WHERE j.creationTimestamp < :date + AND (j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.NEW + OR + j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.STARTED + OR + j.status = com.ca.apm.acc.configserver.core.domain.jobs.AgentUpdateTaskStatus.QUEUED) + ORDER BY j.id + """); + } + + @Test // GH-3143 + void powerShouldBeLegalInAQuery() { + assertQuery("select e.power.id from MyEntity e"); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryTransformerTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryTransformerTests.java new file mode 100644 index 0000000000..5ec8f08cb0 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlQueryTransformerTests.java @@ -0,0 +1,783 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import java.util.stream.Stream; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.JpaSort; +import org.springframework.lang.Nullable; + +/** + * Verify that EQL queries are properly transformed through the {@link JpaQueryEnhancer} and the {@link EqlQueryParser}. + * + * @author Greg Turnquist + */ +class EqlQueryTransformerTests { + + private static final String QUERY = "select u from User u"; + private static final String SIMPLE_QUERY = "select u from User u"; + private static final String COUNT_QUERY = "select count(u) from User u"; + private static final String QUERY_WITH_AS = "select u from User as u where u.username = ?1"; + + @Test + void applyingSortShouldIntroduceOrderByCriteriaWhereNoneExists() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name"; + var sort = Sort.by("first_name", "last_name"); + + // when + var results = createQueryFor(original, sort); + + // then + assertThat(original).doesNotContainIgnoringCase("order by"); + assertThat(results).contains("order by e.first_name asc, e.last_name asc"); + } + + @Test + void applyingSortShouldCreateAdditionalOrderByCriteria() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.role, e.hire_date"; + var sort = Sort.by("first_name", "last_name"); + + // when + var results = createQueryFor(original, sort); + + // then + assertThat(results).contains("ORDER BY e.role, e.hire_date, e.first_name asc, e.last_name asc"); + } + + @Test + void applyCountToSimpleQuery() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("SELECT count(e) FROM Employee e where e.name = :name"); + } + + @Test + void applyCountToMoreComplexQuery() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.modified_date"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("SELECT count(e) FROM Employee e where e.name = :name"); + } + + @Test + void applyCountToAlreadySorteQuery() { + + // given + var original = "SELECT e FROM Employee e where e.name = :name ORDER BY e.modified_date"; + + // when + var results = createCountQueryFor(original); + + // then + assertThat(results).isEqualTo("SELECT count(e) FROM Employee e where e.name = :name"); + } + + @Test + void multipleAliasesShouldBeGathered() { + + // given + var original = "select e from Employee e join e.manager m"; + + // when + var results = createQueryFor(original, Sort.unsorted()); + + // then + assertThat(results).isEqualTo("select e from Employee e join e.manager m"); + } + + @Test + void createsCountQueryCorrectly() { + assertCountQuery(QUERY, COUNT_QUERY); + } + + @Test + void createsCountQueriesCorrectlyForCapitalLetterEQL() { + + assertCountQuery("select u FROM User u WHERE u.foo.bar = ?1", "select count(u) FROM User u WHERE u.foo.bar = ?1"); + assertCountQuery("SELECT u FROM User u where u.foo.bar = ?1", "SELECT count(u) FROM User u where u.foo.bar = ?1"); + } + + @Test + void createsCountQueryForDistinctQueries() { + + assertCountQuery("select distinct u from User u where u.foo = ?1", + "select count(distinct u) from User u where u.foo = ?1"); + } + + @Test + void createsCountQueryForConstructorQueries() { + + assertCountQuery("select distinct new com.example.User(u.name) from User u where u.foo = ?1", + "select count(distinct u) from User u where u.foo = ?1"); + } + + @Test + void createsCountQueryForJoins() { + + assertCountQuery("select distinct new com.User(u.name) from User u left outer join u.roles r WHERE r = ?1", + "select count(distinct u) from User u left outer join u.roles r WHERE r = ?1"); + } + + @Test + void createsCountQueryForQueriesWithSubSelects() { + + assertCountQuery("select u from User u left outer join u.roles r where r in (select r from Role r)", + "select count(u) from User u left outer join u.roles r where r in (select r from Role r)"); + } + + @Test + void createsCountQueryForAliasesCorrectly() { + assertCountQuery("select u from User as u", "select count(u) from User as u"); + } + + @Test + void allowsShortJpaSyntax() { + assertCountQuery(SIMPLE_QUERY, COUNT_QUERY); + } + + @Test // GH-2260 + void detectsAliasCorrectly() { + + assertThat(alias(QUERY)).isEqualTo("u"); + assertThat(alias(SIMPLE_QUERY)).isEqualTo("u"); + assertThat(alias(COUNT_QUERY)).isEqualTo("u"); + assertThat(alias(QUERY_WITH_AS)).isEqualTo("u"); + assertThat(alias("SELECT u FROM USER U")).isEqualTo("U"); + assertThat(alias("select u from User u")).isEqualTo("u"); + assertThat(alias("select new com.acme.UserDetails(u.id, u.name) from User u")).isEqualTo("u"); + assertThat(alias("select u from T05User u")).isEqualTo("u"); + assertThat(alias("select u from User u where not exists (select m from User m where m = u.manager) ")) + .isEqualTo("u"); + assertThat(alias("select u from User u where not exists (select u2 from User u2)")).isEqualTo("u"); + assertThat(alias( + "select u from User u where not exists (select u2 from User u2 where not exists (select u3 from User u3))")) + .isEqualTo("u"); + } + + @Test // GH-2557 + void applySortingAccountsForNewlinesInSubselect() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + assertThat(newParser(""" + select u + from user u + where exists (select u2 + from user u2 + ) + """).applySorting(sort)).isEqualToIgnoringWhitespace(""" + select u + from user u + where exists (select u2 + from user u2 + ) + order by u.age desc"""); + } + + @Test // GH-2563 + void aliasDetectionProperlyHandlesNewlinesInSubselects() { + + assertThat(alias(""" + SELECT o + FROM Order o + WHERE EXISTS( SELECT 1 + FROM Vehicle vehicle + WHERE vehicle.vehicleOrderId = o.id + AND LOWER(COALESCE(vehicle.make, '')) LIKE :query) + """)).isEqualTo("o"); + } + + @Test // DATAJPA-252, GH-664, GH-1066, GH-2960 + void doesNotPrefixOrderReferenceIfOuterJoinAliasDetected() { + + String query = "select p from Person p left join p.address address"; + Sort sort = Sort.by("address.city"); + assertThat(createQueryFor(query, sort)).endsWith("order by address.city asc"); + } + + @Test // DATAJPA-252 + void extendsExistingOrderByClausesCorrectly() { + + String query = "select p from Person p order by p.lastname asc"; + Sort sort = Sort.by("firstname"); + assertThat(createQueryFor(query, sort)).endsWith("order by p.lastname asc, p.firstname asc"); + } + + @Test // DATAJPA-296 + void appliesIgnoreCaseOrderingCorrectly() { + + String query = "select p from Person p"; + Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); + + assertThat(createQueryFor(query, sort)).endsWith("order by lower(p.firstname) asc"); + } + + @Test // DATAJPA-296 + void appendsIgnoreCaseOrderingCorrectly() { + + String query = "select p from Person p order by p.lastname asc"; + Sort sort = Sort.by(Sort.Order.by("firstname").ignoreCase()); + + assertThat(createQueryFor(query, sort)) + .isEqualTo("select p from Person p order by p.lastname asc, lower(p.firstname) asc"); + } + + @Test // DATAJPA-342 + void usesReturnedVariableInCountProjectionIfSet() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 order by m.genre asc", + "select count(distinct m.genre) from Media m where m.user = ?1"); + } + + @Test // DATAJPA-343 + void projectsCountQueriesForQueriesWithSubselects() { + + // given + var original = "select o from Foo o where cb.id in (select b from Bar b)"; + + // when + var results = createQueryFor(original, Sort.by("first_name", "last_name")); + + // then + assertThat(results).isEqualTo( + "select o from Foo o where cb.id in (select b from Bar b) order by o.first_name asc, o.last_name asc"); + + assertCountQuery("select o from Foo o where cb.id in (select b from Bar b)", + "select count(o) from Foo o where cb.id in (select b from Bar b)"); + } + + @Test // DATAJPA-148 + void doesNotPrefixSortsIfFunction() { + + Sort sort = Sort.by("sum(foo)"); + + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> createQueryFor("select p from Person p", sort)); + } + + @Test // DATAJPA-377 + void removesOrderByInGeneratedCountQueryFromOriginalQueryIfPresent() { + + assertCountQuery("select distinct m.genre from Media m where m.user = ?1 OrDer By m.genre ASC", + "select count(distinct m.genre) from Media m where m.user = ?1"); + } + + @Test // DATAJPA-375 + void findsExistingOrderByIndependentOfCase() { + + Sort sort = Sort.by("lastname"); + String query = createQueryFor("select p from Person p ORDER BY p.firstname", sort); + assertThat(query).endsWith("ORDER BY p.firstname, p.lastname asc"); + } + + @Test // DATAJPA-409 + void createsCountQueryForNestedReferenceCorrectly() { + assertCountQuery("select a.b from A a", "select count(a) from A a"); + } + + @Test // DATAJPA-420 + void createsCountQueryForScalarSelects() { + assertCountQuery("select p.lastname,p.firstname from Person p", "select count(p) from Person p"); + } + + @Test // DATAJPA-456 + void createCountQueryFromTheGivenCountProjection() { + + assertThat(createCountQueryFor("select p.lastname,p.firstname from Person p", "p.lastname")) + .isEqualTo("select count(p.lastname) from Person p"); + } + + @Test // DATAJPA-736 + void supportsNonAsciiCharactersInEntityNames() { + assertThat(createCountQueryFor("select u from Usèr u")).isEqualTo("select count(u) from Usèr u"); + } + + @Test // DATAJPA-798 + void detectsAliasInQueryContainingLineBreaks() { + assertThat(alias("select \n u \n from \n User \nu")).isEqualTo("u"); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionInDistinctQuery() { + assertThat(hasConstructorExpression("select distinct new com.example.Foo(b.name) from Bar b")).isTrue(); + } + + @Test // DATAJPA-938 + void detectsComplexConstructorExpression() { + + // + assertThat(hasConstructorExpression( + """ + select new foo.bar.Foo(ip.id, ip.name, sum(lp.amount)) + from Bar lp join lp.investmentProduct ip + where (lp.toDate is null and lp.fromDate <= :now and lp.fromDate is not null) and lp.accountId = :accountId group by ip.id, ip.name, lp.accountId + order by ip.name ASC""")) + .isTrue(); + } + + @Test // DATAJPA-938 + void detectsConstructorExpressionWithLineBreaks() { + assertThat(hasConstructorExpression("select new foo.bar.FooBar(\na.id) from DtoA a ")).isTrue(); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotAllowWhitespaceInSort() { + + Sort sort = Sort.by("case when foo then bar"); + assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) + .isThrownBy(() -> createQueryFor("select p from Person p", sort)); + } + + @Test // DATAJPA-965, DATAJPA-970 + void doesNotPrefixUnsafeJpaSortFunctionCalls() { + + JpaSort sort = JpaSort.unsafe("sum(foo)"); + assertThat(createQueryFor("select p from Person p", sort)).endsWith("order by sum(foo) asc"); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void doesNotPrefixMultipleAliasedFunctionCalls() { + + String query = "SELECT AVG(m.price) AS avgPrice, SUM(m.stocks) AS sumStocks FROM Magazine m"; + Sort sort = Sort.by("avgPrice", "sumStocks"); + + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc, sumStocks asc"); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void doesNotPrefixSingleAliasedFunctionCalls() { + + String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("avgPrice"); + + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void prefixesSingleNonAliasedFunctionCallRelatedSortProperty() { + + String query = "SELECT AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("someOtherProperty"); + + assertThat(createQueryFor(query, sort)).endsWith("order by m.someOtherProperty asc"); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void prefixesNonAliasedFunctionCallRelatedSortPropertyWhenSelectClauseContainsAliasedFunctionForDifferentProperty() { + + String query = "SELECT m.name, AVG(m.price) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("name", "avgPrice"); + + assertThat(createQueryFor(query, sort)).endsWith("order by m.name asc, avgPrice asc"); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void doesNotPrefixAliasedFunctionCallNameWithMultipleNumericParameters() { + + String query = "SELECT SUBSTRING(m.name, 2, 5) AS trimmedName FROM Magazine m"; + Sort sort = Sort.by("trimmedName"); + + assertThat(createQueryFor(query, sort)).endsWith("order by trimmedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void doesNotPrefixAliasedFunctionCallNameWithMultipleStringParameters() { + + String query = "SELECT CONCAT(m.name, 'foo') AS extendedName FROM Magazine m"; + Sort sort = Sort.by("extendedName"); + + assertThat(createQueryFor(query, sort)).endsWith("order by extendedName asc"); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void doesNotPrefixAliasedFunctionCallNameWithUnderscores() { + + String query = "SELECT AVG(m.price) AS avg_price FROM Magazine m"; + Sort sort = Sort.by("avg_price"); + + assertThat(createQueryFor(query, sort)).endsWith("order by avg_price asc"); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void doesNotPrefixAliasedFunctionCallNameWithDots() { + + String query = "SELECT AVG(m.price) AS m.avg FROM Magazine m"; + Sort sort = Sort.by("m.avg"); + + assertThatIllegalArgumentException().isThrownBy(() -> createQueryFor(query, sort)); + } + + @Test // DATAJPA-965, DATAJPA-970, GH-2863 + void doesNotPrefixAliasedFunctionCallNameWhenQueryStringContainsMultipleWhiteSpaces() { + + String query = "SELECT AVG( m.price ) AS avgPrice FROM Magazine m"; + Sort sort = Sort.by("avgPrice"); + + assertThat(createQueryFor(query, sort)).endsWith("order by avgPrice asc"); + } + + @Test // DATAJPA-1506 + void detectsAliasWithGroupAndOrderBy() { + + assertThat(alias("select * from User group by name")).isNull(); + assertThat(alias("select * from User order by name")).isNull(); + assertThat(alias("select u from User u group by name")).isEqualTo("u"); + assertThat(alias("select u from User u order by name")).isEqualTo("u"); + } + + @Test // DATAJPA-1500 + void createCountQuerySupportsWhitespaceCharacters() { + + assertThat(createCountQueryFor(""" + select user from User user + where user.age = 18 + order by user.name + """)).isEqualToIgnoringWhitespace(""" + select count(user) from User user + where user.age = 18 + """); + } + + @Test + void createCountQuerySupportsLineBreaksInSelectClause() { + + assertThat(createCountQueryFor(""" + select user.age, + user.name + from User user + where user.age = 18 + order + by + user.name + """)).isEqualToIgnoringWhitespace(""" + select count(user) from User user + where user.age = 18 + """); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFieldAliases() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("authorName"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by authorName asc"); + } + + @Test // GH-2280 + void appliesOrderingCorrectlyForFieldAliasWithIgnoreCase() { + + String query = "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer"; + Sort sort = Sort.by(Sort.Order.by("name").ignoreCase()); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).isEqualTo( + "SELECT customer.id as id, customer.name as name FROM CustomerEntity customer order by lower(name) asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForFunctionAliases() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("title"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by title asc"); + } + + @Test // DATAJPA-1061 + void appliesSortCorrectlyForSimpleField() { + + String query = "SELECT m.price, lower(m.title) AS title, a.name as authorName FROM Magazine m INNER JOIN m.author a"; + Sort sort = Sort.by("price"); + + String fullQuery = createQueryFor(query, sort); + + assertThat(fullQuery).endsWith("order by m.price asc"); + } + + @Test + void createCountQuerySupportsLineBreakRightAfterDistinct() { + + assertThat(createCountQueryFor(""" + select + distinct + user.age, + user.name + from + User + user""")).isEqualTo(createCountQueryFor(""" + select + distinct user.age, + user.name + from + User + user""")); + } + + @Test + void detectsAliasWithGroupAndOrderByWithLineBreaks() { + + assertThat(alias("select * from User group\nby name")).isNull(); + assertThat(alias("select * from User order\nby name")).isNull(); + assertThat(alias("select u from User u group\nby name")).isEqualTo("u"); + assertThat(alias("select u from User u order\nby name")).isEqualTo("u"); + assertThat(alias("select u from User\nu\norder \n by name")).isEqualTo("u"); + } + + @Test // DATAJPA-1679 + void findProjectionClauseWithDistinct() { + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(projection("select a,b,c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select a, b, c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select distinct a, b, c from Entity x")).isEqualTo("a, b, c"); + softly.assertThat(projection("select DISTINCT a, b, c from Entity x")).isEqualTo("a, b, c"); + }); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithSubselect() { + + // This is not a required behavior, in fact the opposite is, + // but it documents a current limitation. + // to fix this without breaking findProjectionClauseWithIncludedFrom we need a more sophisticated parser. + assertThat(projection("select * from (select x from y)")).isNotEqualTo("*"); + } + + @Test // DATAJPA-1696 + void findProjectionClauseWithIncludedFrom() { + assertThat(projection("select x, frommage, y from Element t")).isEqualTo("x, frommage, y"); + } + + @Test // GH-2341 + void countProjectionDistrinctQueryIncludesNewLineAfterFromAndBeforeJoin() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1\nLEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test // GH-2341 + void countProjectionDistinctQueryIncludesNewLineAfterEntity() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key"); + } + + @Test // GH-2341 + void countProjectionDistinctQueryIncludesNewLineAfterEntityAndBeforeWhere() { + + String originalQuery = "SELECT DISTINCT entity1\nFROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key\nwhere entity1.id = 1799"; + assertCountQuery(originalQuery, + "SELECT count(DISTINCT entity1) FROM Entity1 entity1 LEFT JOIN entity1.entity2 entity2 ON entity1.key = entity2.key where entity1.id = 1799"); + } + + @Test // GH-2393 + void createCountQueryStartsWithWhitespace() { + + assertThat(createCountQueryFor(" \nselect u from User u where u.age > :age")) + .isEqualTo("select count(u) from User u where u.age > :age"); + + assertThat(createCountQueryFor(" \nselect u from User u where u.age > :age")) + .isEqualTo("select count(u) from User u where u.age > :age"); + } + + @Test // GH-2260 + void applySortingAccountsForNativeWindowFunction() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + // order by absent + assertThat(createQueryFor("select u from user u", sort)).isEqualTo("select u from user u order by u.age desc"); + + // order by present + assertThat(createQueryFor("select u from user u order by u.lastname", sort)) + .isEqualTo("select u from user u order by u.lastname, u.age desc"); + } + + @Test // GH-2511 + void countQueryUsesCorrectVariable() { + + assertThat(createCountQueryFor("SELECT e FROM User e WHERE created_at > $1")) + .isEqualTo("SELECT count(e) FROM User e WHERE created_at > $1"); + + assertThat( + createCountQueryFor("SELECT t FROM mytable t WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'")) + .isEqualTo("SELECT count(t) FROM mytable t WHERE nr = :number AND kon = :kon AND datum >= '2019-01-01'"); + + assertThat(createCountQueryFor("select s FROM users_statuses s WHERE (user_created_at BETWEEN $1 AND $2)")) + .isEqualTo("select count(s) FROM users_statuses s WHERE (user_created_at BETWEEN $1 AND $2)"); + + assertThat( + createCountQueryFor("SELECT us FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)")) + .isEqualTo("SELECT count(us) FROM users_statuses us WHERE (user_created_at BETWEEN :fromDate AND :toDate)"); + } + + @Test // GH-2496, GH-2522, GH-2537, GH-2045 + void orderByShouldWorkWithSubSelectStatements() { + + Sort sort = Sort.by(Sort.Order.desc("age")); + + assertThat(createQueryFor(""" + select r + From DataRecord r + where + ( + r.adusrId = :userId + or EXISTS( select 1 FROM DataRecordDvsRight dr WHERE dr.adusrId = :userId AND dr.dataRecord = r ) + )""", sort)).endsWith("order by r.age desc"); + + assertThat(createQueryFor(""" + select distinct u + from FooBar u + where u.role = 'redacted' + and ( + not exists ( + select g from FooBarGroup g + where g in :excludedGroups + ) + )""", sort)).endsWith("order by u.age desc"); + + assertThat(createQueryFor(""" + SELECT i + FROM Item i + WHERE i.id IN ( + SELECT max(i2.id) FROM Item i2 + WHERE i2.field.id = :fieldId + GROUP BY i2.field.id, i2.version)""", sort)).endsWith("order by i.age desc"); + } + + @Test // GH-2074 + void queryParserPicksCorrectAliasAmidstMultipleAliases() { + assertThat(alias("select u from User as u left join u.roles as r")).isEqualTo("u"); + } + + @ParameterizedTest + @MethodSource("queriesWithReservedWordsAsIdentifiers") // GH-2864 + void usingReservedWordAsRelationshipNameShouldWork(String relationshipName, String joinAlias) { + + EqlQueryParser.parseQuery(String.format(""" + select u + from UserAccountEntity u + join u.lossInspectorLimitConfiguration lil + join u.companyTeam ct + where exists ( + select iu + from UserAccountEntity iu + join iu.roles u2r + join u2r.role r + join r.rights r2r + join r2r.%s %s + where + %s.code = :rightCode + and iu = u + ) + and ct.id = :teamId + """, relationshipName, joinAlias, joinAlias)); + } + + @Test // GH-664, GH-1066, GH-2960 + void sortingRecognizesJoinAliases() { + + String query = "select p from Customer c join c.productOrder p where p.delayed = true"; + + assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("lastName")))).isEqualToIgnoringWhitespace(""" + select p from Customer c + join c.productOrder p + where p.delayed = true + order by c.lastName desc + """); + + assertThat(createQueryFor(query, Sort.by(Sort.Order.desc("p.lineItems")))).isEqualToIgnoringWhitespace(""" + select p from Customer c + join c.productOrder p + where p.delayed = true + order by p.lineItems desc + """); + } + + static Stream queriesWithReservedWordsAsIdentifiers() { + + return Stream.of( // + Arguments.of("right", "rt"), // + Arguments.of("left", "lt"), // + Arguments.of("outer", "ou"), // + Arguments.of("full", "full"), // + Arguments.of("inner", "inr")); + } + + private void assertCountQuery(String originalQuery, String countQuery) { + assertThat(createCountQueryFor(originalQuery)).isEqualTo(countQuery); + } + + private String createQueryFor(String query, Sort sort) { + return newParser(query).applySorting(sort); + } + + private String createCountQueryFor(String query) { + return createCountQueryFor(query, null); + } + + private String createCountQueryFor(String original, @Nullable String countProjection) { + return newParser(original).createCountQueryFor(countProjection); + } + + private String alias(String query) { + return newParser(query).detectAlias(); + } + + private boolean hasConstructorExpression(String query) { + return newParser(query).hasConstructorExpression(); + } + + private String projection(String query) { + return newParser(query).getProjection(); + } + + private QueryEnhancer newParser(String query) { + return JpaQueryEnhancer.forEql(DeclaredQuery.of(query, false)); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java new file mode 100644 index 0000000000..81f2d565f1 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlSpecificationTests.java @@ -0,0 +1,887 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Tests built around examples of EQL found in the JPA spec + * https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc
    + *
    + * IMPORTANT: Purely verifies the parser without any transformations. + * + * @author Greg Turnquist + */ +class EqlSpecificationTests { + + private static final String SPEC_FAULT = "Disabled due to spec fault> "; + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + */ + @Test + void joinExample1() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o + FROM Order AS o JOIN o.lineItems AS l + WHERE l.shipped = FALSE + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#example + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#identification-variables + */ + @Test + void joinExample2() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l JOIN l.product p + WHERE p.productType = 'office_supplies' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#range-variable-declarations + */ + @Test + void rangeVariableDeclarations() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o1 + FROM Order o1, Order o2 + WHERE o1.quantity > o2.quantity AND + o2.customer.lastname = 'Smith' AND + o2.customer.firstname= 'John' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample1() { + + EqlQueryParser.parseQuery(""" + SELECT i.name, VALUE(p) + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample2() { + + EqlQueryParser.parseQuery(""" + SELECT i.name, p + FROM Item i JOIN i.photos p + WHERE KEY(p) LIKE '%egret' + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample3() { + + EqlQueryParser.parseQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo.phones p + """); + } + + /** + * @see https://github.com/jakartaee/persistence/blob/master/spec/src/main/asciidoc/ch04-query-language.adoc#path-expressions + */ + @Test + void pathExpressionsExample4() { + + EqlQueryParser.parseQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE e.contactInfo.address.zipcode = '95054' + """); + } + + @Test + void pathExpressionSyntaxExample1() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT l.product + FROM Order AS o JOIN o.lineItems l + """); + } + + @Test + void joinsExample1() { + + EqlQueryParser.parseQuery(""" + SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize + """); + } + + @Test + void joinsExample2() { + + EqlQueryParser.parseQuery(""" + SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInnerExample() { + + EqlQueryParser.parseQuery(""" + SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 + """); + } + + @Test + void joinsInExample() { + + EqlQueryParser.parseQuery(""" + SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 + """); + } + + @Test + void doubleJoinExample() { + + EqlQueryParser.parseQuery(""" + SELECT p.vendor + FROM Employee e JOIN e.contactInfo c JOIN c.phones p + WHERE c.address.zipcode = '95054' + """); + } + + @Test + void leftJoinExample() { + + EqlQueryParser.parseQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + GROUP BY s.name + """); + } + + @Test + void leftJoinOnExample() { + + EqlQueryParser.parseQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + ON p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinWhereExample() { + + EqlQueryParser.parseQuery(""" + SELECT s.name, COUNT(p) + FROM Suppliers s LEFT JOIN s.products p + WHERE p.status = 'inStock' + GROUP BY s.name + """); + } + + @Test + void leftJoinFetchExample() { + + EqlQueryParser.parseQuery(""" + SELECT d + FROM Department d LEFT JOIN FETCH d.employees + WHERE d.deptno = 1 + """); + } + + @Test + void collectionMemberExample() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void collectionMemberInExample() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o + FROM Order o, IN(o.lineItems) l + WHERE l.product.productType = 'office_supplies' + """); + } + + @Test + void fromClauseExample() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Order AS o JOIN o.lineItems l JOIN l.product p + """); + } + + @Test + void fromClauseDowncastingExample1() { + + EqlQueryParser.parseQuery(""" + SELECT b.name, b.ISBN + FROM Order o JOIN TREAT(o.product AS Book) b + """); + } + + @Test + void fromClauseDowncastingExample2() { + + EqlQueryParser.parseQuery(""" + SELECT e FROM Employee e JOIN TREAT(e.projects AS LargeProject) lp + WHERE lp.budget > 1000 + """); + } + + /** + * @see #fromClauseDowncastingExample3fixed() + */ + @Test + @Disabled(SPEC_FAULT + "Use double-quotes when it should be using single-quotes for a string literal") + void fromClauseDowncastingExample3_SPEC_BUG() { + + EqlQueryParser.parseQuery(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE "cost overrun" + """); + } + + @Test + void fromClauseDowncastingExample3fixed() { + + EqlQueryParser.parseQuery(""" + SELECT e FROM Employee e JOIN e.projects p + WHERE TREAT(p AS LargeProject).budget > 1000 + OR TREAT(p AS SmallProject).name LIKE 'Persist%' + OR p.description LIKE 'cost overrun' + """); + } + + @Test + void fromClauseDowncastingExample4() { + + EqlQueryParser.parseQuery(""" + SELECT e FROM Employee e + WHERE TREAT(e AS Exempt).vacationDays > 10 + OR TREAT(e AS Contractor).hours > 100 + """); + } + + @Test + void pathExpressionsNamedParametersExample() { + + EqlQueryParser.parseQuery(""" + SELECT c + FROM Customer c + WHERE c.status = :stat + """); + } + + @Test + void betweenExpressionsExample() { + + EqlQueryParser.parseQuery(""" + SELECT t + FROM CreditCard c JOIN c.transactionHistory t + WHERE c.holder.name = 'John Doe' AND INDEX(t) BETWEEN 0 AND 9 + """); + } + + @Test + void isEmptyExample() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void memberOfExample() { + + EqlQueryParser.parseQuery(""" + SELECT p + FROM Person p + WHERE 'Joe' MEMBER OF p.nicknames + """); + } + + @Test + void existsSubSelectExample1() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS ( + SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void allExample() { + + EqlQueryParser.parseQuery(""" + SELECT emp + FROM Employee emp + WHERE emp.salary > ALL ( + SELECT m.salary + FROM Manager m + WHERE m.department = emp.department) + """); + } + + @Test + void existsSubSelectExample2() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT emp + FROM Employee emp + WHERE EXISTS ( + SELECT spouseEmp + FROM Employee spouseEmp + WHERE spouseEmp = emp.spouse) + """); + } + + @Test + void subselectNumericComparisonExample1() { + + EqlQueryParser.parseQuery(""" + SELECT c + FROM Customer c + WHERE (SELECT AVG(o.price) FROM c.orders o) > 100 + """); + } + + @Test + void subselectNumericComparisonExample2() { + + EqlQueryParser.parseQuery(""" + SELECT goodCustomer + FROM Customer goodCustomer + WHERE goodCustomer.balanceOwed < ( + SELECT AVG(c.balanceOwed)/2.0 FROM Customer c) + """); + } + + @Test + void indexExample() { + + EqlQueryParser.parseQuery(""" + SELECT w.name + FROM Course c JOIN c.studentWaitlist w + WHERE c.name = 'Calculus' + AND INDEX(w) = 0 + """); + } + + /** + * @see #functionInvocationExampleWithCorrection() + */ + @Test + @Disabled(SPEC_FAULT + "FUNCTION calls needs a comparator") + void functionInvocationExample_SPEC_BUG() { + + EqlQueryParser.parseQuery(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) + """); + } + + @Test + void functionInvocationExampleWithCorrection() { + + EqlQueryParser.parseQuery(""" + SELECT c + FROM Customer c + WHERE FUNCTION('hasGoodCredit', c.balance, c.creditLimit) = TRUE + """); + } + + @Test + void updateCaseExample1() { + + EqlQueryParser.parseQuery(""" + UPDATE Employee e + SET e.salary = + CASE WHEN e.rating = 1 THEN e.salary * 1.1 + WHEN e.rating = 2 THEN e.salary * 1.05 + ELSE e.salary * 1.01 + END + """); + } + + @Test + void updateCaseExample2() { + + EqlQueryParser.parseQuery(""" + UPDATE Employee e + SET e.salary = + CASE e.rating WHEN 1 THEN e.salary * 1.1 + WHEN 2 THEN e.salary * 1.05 + ELSE e.salary * 1.01 + END + """); + } + + @Test + void selectCaseExample1() { + + EqlQueryParser.parseQuery(""" + SELECT e.name, + CASE TYPE(e) WHEN Exempt THEN 'Exempt' + WHEN Contractor THEN 'Contractor' + WHEN Intern THEN 'Intern' + ELSE 'NonExempt' + END + FROM Employee e + WHERE e.dept.name = 'Engineering' + """); + } + + @Test + void selectCaseExample2() { + + EqlQueryParser.parseQuery(""" + SELECT e.name, + f.name, + CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum ' + WHEN f.annualMiles > 25000 THEN 'Gold ' + ELSE '' + END, + 'Frequent Flyer') + FROM Employee e JOIN e.frequentFlierPlan f + """); + } + + @Test + void theRest() { + + EqlQueryParser.parseQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (Exempt, Contractor) + """); + } + + @Test + void theRest2() { + + EqlQueryParser.parseQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN (:empType1, :empType2) + """); + } + + @Test + void theRest3() { + + EqlQueryParser.parseQuery(""" + SELECT e + FROM Employee e + WHERE TYPE(e) IN :empTypes + """); + } + + @Test + void theRest4() { + + EqlQueryParser.parseQuery(""" + SELECT TYPE(e) + FROM Employee e + WHERE TYPE(e) <> Exempt + """); + } + + @Test + void theRest5() { + + EqlQueryParser.parseQuery(""" + SELECT c.status, AVG(c.filledOrderCount), COUNT(c) + FROM Customer c + GROUP BY c.status + HAVING c.status IN (1, 2) + """); + } + + @Test + void theRest6() { + + EqlQueryParser.parseQuery(""" + SELECT c.country, COUNT(c) + FROM Customer c + GROUP BY c.country + HAVING COUNT(c) > 30 + """); + } + + @Test + void theRest7() { + + EqlQueryParser.parseQuery(""" + SELECT c, COUNT(o) + FROM Customer c JOIN c.orders o + GROUP BY c + HAVING COUNT(o) >= 5 + """); + } + + @Test + void theRest8() { + + EqlQueryParser.parseQuery(""" + SELECT c.id, c.status + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest9() { + + EqlQueryParser.parseQuery(""" + SELECT v.location.street, KEY(i).title, VALUE(i) + FROM VideoStore v JOIN v.videoInventory i + WHERE v.location.zipcode = '94301' AND VALUE(i) > 0 + """); + } + + @Test + void theRest10() { + + EqlQueryParser.parseQuery(""" + SELECT o.lineItems FROM Order AS o + """); + } + + @Test + void theRest11() { + + EqlQueryParser.parseQuery(""" + SELECT c, COUNT(l) AS itemCount + FROM Customer c JOIN c.Orders o JOIN o.lineItems l + WHERE c.address.state = 'CA' + GROUP BY c + ORDER BY itemCount + """); + } + + @Test + void theRest12() { + + EqlQueryParser.parseQuery(""" + SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) + FROM Customer c JOIN c.orders o + WHERE o.count > 100 + """); + } + + @Test + void theRest13() { + + EqlQueryParser.parseQuery(""" + SELECT e.address AS addr + FROM Employee e + """); + } + + @Test + void theRest14() { + + EqlQueryParser.parseQuery(""" + SELECT AVG(o.quantity) FROM Order o + """); + } + + @Test + void theRest15() { + + EqlQueryParser.parseQuery(""" + SELECT SUM(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest16() { + + EqlQueryParser.parseQuery(""" + SELECT COUNT(o) FROM Order o + """); + } + + @Test + void theRest17() { + + EqlQueryParser.parseQuery(""" + SELECT COUNT(l.price) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + """); + } + + @Test + void theRest18() { + + EqlQueryParser.parseQuery(""" + SELECT COUNT(l) + FROM Order o JOIN o.lineItems l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' AND l.price IS NOT NULL + """); + } + + @Test + void theRest19() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity DESC, o.totalcost + """); + } + + @Test + void theRest20() { + + EqlQueryParser.parseQuery(""" + SELECT o.quantity, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + ORDER BY o.quantity, a.zipcode + """); + } + + @Test + void theRest21() { + + EqlQueryParser.parseQuery(""" + SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' AND a.county = 'Santa Clara' + ORDER BY o.quantity, taxedCost, a.zipcode + """); + } + + @Test + void theRest22() { + + EqlQueryParser.parseQuery(""" + SELECT AVG(o.quantity) as q, a.zipcode + FROM Customer c JOIN c.orders o JOIN c.address a + WHERE a.state = 'CA' + GROUP BY a.zipcode + ORDER BY q DESC + """); + } + + @Test + void theRest23() { + + EqlQueryParser.parseQuery(""" + SELECT p.product_name + FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY p.price + """); + } + + /** + * This query is specifically dubbed illegal in the spec. It may actually be failing for a different reason. + */ + @Test + void theRest24() { + + assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> { + EqlQueryParser.parseQuery(""" + SELECT p.product_name + FROM Order o, IN(o.lineItems) l JOIN o.customer c + WHERE c.lastname = 'Smith' AND c.firstname = 'John' + ORDER BY o.quantity + """); + }); + } + + @Test + void theRest25() { + + EqlQueryParser.parseQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + """); + } + + @Test + void theRest26() { + + EqlQueryParser.parseQuery(""" + DELETE + FROM Customer c + WHERE c.status = 'inactive' + AND c.orders IS EMPTY + """); + } + + @Test + void theRest27() { + + EqlQueryParser.parseQuery(""" + UPDATE Customer c + SET c.status = 'outstanding' + WHERE c.balance < 10000 + """); + } + + @Test + void theRest28() { + + EqlQueryParser.parseQuery(""" + UPDATE Employee e + SET e.address.building = 22 + WHERE e.address.building = 14 + AND e.address.city = 'Santa Clara' + AND e.project = 'Jakarta EE' + """); + } + + @Test + void theRest29() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Order o + """); + } + + @Test + void theRest30() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Order o + WHERE o.shippingAddress.state = 'CA' + """); + } + + @Test + void theRest31() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o.shippingAddress.state + FROM Order o + """); + } + + @Test + void theRest32() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + """); + } + + @Test + void theRest33() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS NOT EMPTY + """); + } + + @Test + void theRest34() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Order o + WHERE o.lineItems IS EMPTY + """); + } + + @Test + void theRest35() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.shipped = FALSE + """); + } + + @Test + void theRest36() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Order o + WHERE + NOT (o.shippingAddress.state = o.billingAddress.state AND + o.shippingAddress.city = o.billingAddress.city AND + o.shippingAddress.street = o.billingAddress.street) + """); + } + + @Test + void theRest37() { + + EqlQueryParser.parseQuery(""" + SELECT o + FROM Order o + WHERE o.shippingAddress <> o.billingAddress + """); + } + + @Test + void theRest38() { + + EqlQueryParser.parseQuery(""" + SELECT DISTINCT o + FROM Order o JOIN o.lineItems l + WHERE l.product.name = ?1 + """); + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java index 403f7ffaed..de8b922d30 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryRendererTests.java @@ -523,7 +523,7 @@ void selectCaseExample2() { } @Test - void theRest() { + void inClauseWithTypeLiteralsShouldWork() { assertQuery(""" SELECT e @@ -533,7 +533,7 @@ WHERE TYPE(e) IN (Exempt, Contractor) } @Test - void theRest2() { + void inClauseWithParametersShouldWork() { assertQuery(""" SELECT e @@ -543,7 +543,7 @@ WHERE TYPE(e) IN (:empType1, :empType2) } @Test - void theRest3() { + void inClauseWithSingleParameterShouldWork() { assertQuery(""" SELECT e @@ -553,7 +553,7 @@ WHERE TYPE(e) IN :empTypes } @Test - void theRest4() { + void notEqualsForTypeShouldWork() { assertQuery(""" SELECT TYPE(e) @@ -563,7 +563,7 @@ WHERE TYPE(e) <> Exempt } @Test - void theRest5() { + void havingWithInClauseShouldWork() { assertQuery(""" SELECT c.status, AVG(c.filledOrderCount), COUNT(c) @@ -574,7 +574,7 @@ HAVING c.status IN (1, 2) } @Test - void theRest6() { + void havingClauseWithComparisonShouldWork() { assertQuery(""" SELECT c.country, COUNT(c) @@ -585,7 +585,7 @@ HAVING COUNT(c) > 30 } @Test - void theRest7() { + void havingClauseWithAnotherComparisonShouldWork() { assertQuery(""" SELECT c, COUNT(o) @@ -596,7 +596,7 @@ HAVING COUNT(o) >= 5 } @Test - void theRest8() { + void whereClauseWithComparisonShouldWork() { assertQuery(""" SELECT c.id, c.status @@ -606,7 +606,7 @@ void theRest8() { } @Test - void theRest9() { + void keyValueFunctionsShouldWork() { assertQuery(""" SELECT v.location.street, KEY(i).title, VALUE(i) @@ -616,7 +616,7 @@ SELECT v.location.street, KEY(i).title, VALUE(i) } @Test - void theRest10() { + void fromClauseWithAsShouldWork() { assertQuery(""" SELECT o.lineItems FROM Order AS o @@ -624,7 +624,7 @@ void theRest10() { } @Test - void theRest11() { + void countFunctionWithAsClauseShouldWork() { assertQuery(""" SELECT c, COUNT(l) AS itemCount @@ -636,7 +636,7 @@ SELECT c, COUNT(l) AS itemCount } @Test - void theRest12() { + void objectConstructionShouldWork() { assertQuery(""" SELECT NEW com.acme.example.CustomerDetails(c.id, c.status, o.count) @@ -646,7 +646,7 @@ void theRest12() { } @Test - void theRest13() { + void selectWithAsClauseShouldWork() { assertQuery(""" SELECT e.address AS addr @@ -655,7 +655,7 @@ void theRest13() { } @Test - void theRest14() { + void averageFunctionShouldWork() { assertQuery(""" SELECT AVG(o.quantity) FROM Order o @@ -663,7 +663,7 @@ SELECT AVG(o.quantity) FROM Order o } @Test - void theRest15() { + void sumFunctionShouldWork() { assertQuery(""" SELECT SUM(l.price) @@ -673,7 +673,7 @@ SELECT SUM(l.price) } @Test - void theRest16() { + void countFunctionShouldWork() { assertQuery(""" SELECT COUNT(o) FROM Order o @@ -681,7 +681,7 @@ SELECT COUNT(o) FROM Order o } @Test - void theRest17() { + void countFunctionOnSubElementShouldWork() { assertQuery(""" SELECT COUNT(l.price) @@ -691,7 +691,7 @@ SELECT COUNT(l.price) } @Test - void theRest18() { + void equivalentCountFunctionShouldAlsoWork() { assertQuery(""" SELECT COUNT(l) @@ -701,7 +701,7 @@ SELECT COUNT(l) } @Test - void theRest19() { + void orderByBasedOnSelectClauseShouldWork() { assertQuery(""" SELECT o @@ -712,7 +712,7 @@ void theRest19() { } @Test - void theRest20() { + void orderByThatMatchesSelectClauseShouldWork() { assertQuery(""" SELECT o.quantity, a.zipcode @@ -723,7 +723,7 @@ void theRest20() { } @Test - void theRest21() { + void orderByThatMatchesAllSelectAliasesShouldWork() { assertQuery(""" SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode @@ -734,7 +734,7 @@ void theRest21() { } @Test - void theRest22() { + void orderByThatMatchesSelectFunctionAliasShouldWork() { assertQuery(""" SELECT AVG(o.quantity) as q, a.zipcode @@ -745,8 +745,13 @@ SELECT AVG(o.quantity) as q, a.zipcode """); } + /** + * NOTE: This query is specifically dubbed illegal in the spec. However, it's not due to a grammar failure but instead + * for semantic reasons. Our parser does NOT check if the ORDER BY matches the SELECT or not. Hence, this is left to + * the JPA provider. + */ @Test - void theRest23() { + void orderByClauseThatIsNotReflectedInTheSelectClause() { assertQuery(""" SELECT p.product_name @@ -757,10 +762,10 @@ void theRest23() { } /** - * This query is specifically dubbed illegal in the spec. It may actually be failing for a different reason. + * NOTE: This query is specifically dubbed illegal in the spec. It may actually be failing for a different reason. */ @Test - void theRest24() { + void orderByClauseThatIsNotReflectedInTheSelectClauseButAlsoHasAnInClauseInTheFromClause() { assertThatExceptionOfType(BadJpqlGrammarException.class).isThrownBy(() -> { assertQuery(""" @@ -773,7 +778,7 @@ FROM Order o, IN(o.lineItems) l JOIN o.customer c } @Test - void theRest25() { + void simpleDeleteShouldWork() { assertQuery(""" DELETE @@ -783,7 +788,7 @@ void theRest25() { } @Test - void theRest26() { + void deleteWithMoreComplexCriteriaShouldWork() { assertQuery(""" DELETE @@ -794,7 +799,7 @@ void theRest26() { } @Test - void theRest27() { + void simpleUpdateShouldWork() { assertQuery(""" UPDATE Customer c @@ -804,7 +809,7 @@ void theRest27() { } @Test - void theRest28() { + void moreComplexUpdateShouldWork() { assertQuery(""" UPDATE Employee e @@ -816,7 +821,7 @@ void theRest28() { } @Test - void theRest29() { + void simpleSelectShouldWork() { assertQuery(""" SELECT o @@ -825,7 +830,7 @@ void theRest29() { } @Test - void theRest30() { + void selectWithWhereClauseShouldWork() { assertQuery(""" SELECT o @@ -835,7 +840,7 @@ void theRest30() { } @Test - void theRest31() { + void selectWithDistinctSubElementShouldWork() { assertQuery(""" SELECT DISTINCT o.shippingAddress.state @@ -844,7 +849,7 @@ void theRest31() { } @Test - void theRest32() { + void selectWithSimpleDistinctShouldWork() { assertQuery(""" SELECT DISTINCT o @@ -853,7 +858,7 @@ void theRest32() { } @Test - void theRest33() { + void selectWithIsNotEmptyCriteriaShouldWork() { assertQuery(""" SELECT o @@ -863,7 +868,7 @@ void theRest33() { } @Test - void theRest34() { + void selectWithIsEmptyCriteriaShouldWork() { assertQuery(""" SELECT o @@ -873,7 +878,7 @@ void theRest34() { } @Test - void theRest35() { + void findAllPendingOrders() { assertQuery(""" SELECT DISTINCT o @@ -883,7 +888,7 @@ void theRest35() { } @Test - void theRest36() { + void findAllOrdersWhereShippingAddressDoesNotMatchBillingAddress() { assertQuery(""" SELECT o @@ -896,7 +901,7 @@ void theRest36() { } @Test - void theRest37() { + void simplerVersionOfShippingAddressNotMatchingBillingAddress() { assertQuery(""" SELECT o @@ -906,7 +911,7 @@ void theRest37() { } @Test - void theRest38() { + void findOrdersThatHaveProductNamedByAParameter() { assertQuery(""" SELECT DISTINCT o From 1d49fe9271e58c3055152a89ae5e14020c3ac343 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 28 Sep 2023 11:36:47 +0200 Subject: [PATCH 481/821] Polishing. Simplify PersistenceProvider by removing PresenceDetector interface. Refine presence detection to make present field final. Add warning suppressions as we know that we duplicate code (similar code) and that ANTLR doesn't generate methods with nullable annotations. See #3170 Original pull request: #3176 --- .../jpa/provider/PersistenceProvider.java | 17 +++++++----- .../data/jpa/provider/PresenceDetector.java | 27 ------------------- .../repository/query/EqlQueryRenderer.java | 1 + .../repository/query/HqlQueryRenderer.java | 1 + .../repository/query/JpqlQueryRenderer.java | 1 + 5 files changed, 13 insertions(+), 34 deletions(-) delete mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PresenceDetector.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index 379e30b55e..537b8023ba 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -55,7 +55,7 @@ * @author Greg Turnquist * @author Yuriy Tsarkov */ -public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor, QueryComment, PresenceDetector { +public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor, QueryComment { /** * Hibernate persistence provider. @@ -204,7 +204,7 @@ public String getCommentHintKey() { private final Iterable entityManagerClassNames; private final Iterable metamodelClassNames; - private boolean present; + private final boolean present; /** * Creates a new {@link PersistenceProvider}. @@ -218,12 +218,16 @@ public String getCommentHintKey() { this.entityManagerClassNames = entityManagerClassNames; this.metamodelClassNames = metamodelClassNames; - this.present = false; - entityManagerClassNames.forEach(entityManagerClassName -> { + boolean present = false; + for (String entityManagerClassName : entityManagerClassNames) { + if (ClassUtils.isPresent(entityManagerClassName, PersistenceProvider.class.getClassLoader())) { - this.present = true; + present = true; + break; } - }); + } + + this.present = present; } /** @@ -340,7 +344,6 @@ public static Object unwrapTypedParameterValue(@Nullable Object value) { : value; } - @Override public boolean isPresent() { return this.present; } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PresenceDetector.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PresenceDetector.java deleted file mode 100644 index e6bd19e703..0000000000 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/provider/PresenceDetector.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - - */ -package org.springframework.data.jpa.provider; - -/** - * Define operations needed to detect the presence of a given JPA provider. - * - * @author Greg Turnquist - * @since 3.2 - */ -interface PresenceDetector { - boolean isPresent(); -} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java index 91ede2389f..a1efef4042 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/EqlQueryRenderer.java @@ -26,6 +26,7 @@ * @author Greg Turnquist * @since 3.2 */ +@SuppressWarnings({ "ConstantConditions", "DuplicatedCode" }) class EqlQueryRenderer extends EqlBaseVisitor> { @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index 3f5aa00df6..ca2e934b82 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -26,6 +26,7 @@ * @author Greg Turnquist * @since 3.1 */ +@SuppressWarnings({ "ConstantConditions", "DuplicatedCode" }) class HqlQueryRenderer extends HqlBaseVisitor> { @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java index 7ff5a6f393..bac7e24003 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java @@ -26,6 +26,7 @@ * @author Greg Turnquist * @since 3.1 */ +@SuppressWarnings({ "ConstantConditions", "DuplicatedCode" }) class JpqlQueryRenderer extends JpqlBaseVisitor> { @Override From 867de7d8cfc117037922cdbd2d48d3570633342d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 29 Sep 2023 09:27:06 +0200 Subject: [PATCH 482/821] Upgrade to OpenWebBeans 4.0. Closes #3179 --- spring-data-jpa/pom.xml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index b21b03c313..2c9677329f 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -224,31 +224,9 @@ true - org.apache.openwebbeans openwebbeans-se - jakarta - test - - - org.apache.openwebbeans - openwebbeans-impl - jakarta - test - - - org.apache.openwebbeans - openwebbeans-spi - jakarta test From 8923704eb4d043ddc8754e4317d86247e606d0a5 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 4 Oct 2023 16:58:05 -0700 Subject: [PATCH 483/821] Upgrade to Maven Wrapper 3.9.5. See #3182 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 7f90a55adb..e399fe3728 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Mon Aug 14 08:53:19 EDT 2023 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.4/apache-maven-3.9.4-bin.zip +#Wed Oct 04 16:58:05 PDT 2023 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip From 5bb474386bdcfc3726970dd405e8df9d2a8a9dc2 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 4 Oct 2023 17:24:10 -0700 Subject: [PATCH 484/821] Update CI properties. See #3164 --- ci/pipeline.properties | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index c02f2a1231..afea58c5af 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -7,15 +7,16 @@ docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/ecli docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/openjdk:${java.next.tag} # Supported versions of MongoDB -docker.mongodb.4.4.version=4.4.23 -docker.mongodb.5.0.version=5.0.19 -docker.mongodb.6.0.version=6.0.8 +docker.mongodb.4.4.version=4.4.25 +docker.mongodb.5.0.version=5.0.21 +docker.mongodb.6.0.version=6.0.10 +docker.mongodb.7.0.version=7.0.2 # Supported versions of Redis docker.redis.6.version=6.2.13 # Supported versions of Cassandra -docker.cassandra.3.version=3.11.15 +docker.cassandra.3.version=3.11.16 # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home @@ -25,5 +26,3 @@ docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock - docker.registry= docker.credentials=hub.docker.com-springbuildmaster artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c -gradle-enterprise-cache.credentials=gradle_enterprise_cache_user -gradle-enterprise.access-key=gradle_enterprise_secret_access_key From 35dc37124f21b97280246005bdcd2b2cf076050e Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 5 Oct 2023 09:07:02 +0200 Subject: [PATCH 485/821] Restore ci properties. Restore recently dropped ci properties. --- ci/pipeline.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index afea58c5af..427c3ad9e4 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -26,3 +26,5 @@ docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock - docker.registry= docker.credentials=hub.docker.com-springbuildmaster artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c +gradle-enterprise-cache.credentials=gradle_enterprise_cache_user +gradle-enterprise.access-key=gradle_enterprise_secret_access_key From de2373ba3b09642825fd00fbd71d9b11d7539856 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 13 Oct 2023 08:43:54 -0700 Subject: [PATCH 486/821] Prepare 3.2 RC1 (2023.1.0). See #3164 --- pom.xml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 94542470ab..c919f8c83a 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-SNAPSHOT + 3.2.0-RC1 @@ -37,7 +37,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-SNAPSHOT + 3.2.0-RC1 0.10.3 org.hibernate @@ -237,16 +237,6 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - spring-milestone https://repo.spring.io/milestone From c4a616149bbbf21e1fc84e52c48096c97faf1609 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 13 Oct 2023 08:44:25 -0700 Subject: [PATCH 487/821] Release version 3.2 RC1 (2023.1.0). See #3164 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index c919f8c83a..0bfc8833c4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-RC1 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index f239d6394b..c7f891506a 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-SNAPSHOT + 3.2.0-RC1 org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-RC1 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a458953182..532dfe74fb 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-RC1 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 2c9677329f..c1be1f367e 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-SNAPSHOT + 3.2.0-RC1 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0-RC1 ../pom.xml From 58ce0f331fe12d3cdfb8f665f85af928d1f6c650 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 13 Oct 2023 08:51:58 -0700 Subject: [PATCH 488/821] Prepare next development iteration. See #3164 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 0bfc8833c4..c919f8c83a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-RC1 + 3.2.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index c7f891506a..f239d6394b 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-RC1 + 3.2.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.2.0-RC1 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 532dfe74fb..a458953182 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-RC1 + 3.2.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index c1be1f367e..2c9677329f 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-RC1 + 3.2.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-RC1 + 3.2.0-SNAPSHOT ../pom.xml From 527c3f4d3d0015c06933cf2444c9ceed7f8cdf30 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 13 Oct 2023 08:52:00 -0700 Subject: [PATCH 489/821] After release cleanups. See #3164 --- pom.xml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c919f8c83a..94542470ab 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-RC1 + 3.2.0-SNAPSHOT @@ -37,7 +37,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-RC1 + 3.2.0-SNAPSHOT 0.10.3 org.hibernate @@ -237,6 +237,16 @@ + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + spring-milestone https://repo.spring.io/milestone From f52aa754082a23556fdef9524d6b60a0a0f09d70 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 16 Oct 2023 09:47:02 -0500 Subject: [PATCH 490/821] Use spring-builds+jenkins for ge.spring.io. Related: #3142 --- Jenkinsfile | 2 +- ci/test.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 52f4f2cf2f..f655933ed6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -148,7 +148,7 @@ pipeline { "-Dartifactory.staging-repository=libs-snapshot-local " + "-Dartifactory.build-name=spring-data-jpa " + "-Dartifactory.build-number=${BUILD_NUMBER} " + - '-Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa-enterprise ' + + '-Duser.name=spring-builds+jenkins -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa-enterprise ' + '-Dmaven.test.skip=true clean deploy -U -B ' } diff --git a/ci/test.sh b/ci/test.sh index dfb14eff39..2f5db3d48c 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -10,11 +10,11 @@ export GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} export GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} export GRADLE_ENTERPRISE_ACCESS_KEY=${GRADLE_ENTERPRISE_ACCESS_KEY} -MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \ +MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" \ ./mvnw -s settings.xml \ -P${PROFILE} clean dependency:list test -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa -MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \ +MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" \ ./mvnw -s settings.xml clean -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa chown -R 1001:1001 /tmp/jenkins-home/.m2/.gradle-enterprise From 5c244e1f544ed0bc2ef502be92e59b3b5b209310 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 16 Oct 2023 13:28:20 -0500 Subject: [PATCH 491/821] Update CI properties. See #3197 --- ci/pipeline.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 427c3ad9e4..36a6392c22 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,10 +1,10 @@ # Java versions java.main.tag=17.0.8_7-jdk-focal -java.next.tag=21-jdk-bullseye +java.next.tag=21_35-jdk-jammy # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} -docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/openjdk:${java.next.tag} +docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag} # Supported versions of MongoDB docker.mongodb.4.4.version=4.4.25 From 685107d4e16f34800a5c78b92dd33a24b36f4ef1 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Tue, 17 Oct 2023 12:46:14 +0200 Subject: [PATCH 492/821] Improved documentation of deleteAllInBatch. Closes #3198 --- .../springframework/data/jpa/repository/JpaRepository.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java index 062c41ea67..b138c6b0c6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaRepository.java @@ -79,7 +79,9 @@ default void deleteInBatch(Iterable entities) { * Deletes the given entities in a batch which means it will create a single query. This kind of operation leaves JPAs * first level cache and the database out of sync. Consider flushing the {@link EntityManager} before calling this * method. - * + *

    + * It will also NOT honor cascade semantics of JPA, nor will it emit JPA lifecycle events. + *

    * @param entities entities to be deleted. Must not be {@literal null}. * @since 2.5 */ From 6b07163a5f2aaee29b5635c8761087b1427aedfe Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 19 Oct 2023 09:38:55 -0500 Subject: [PATCH 493/821] Update CI properties. See #3197 --- ci/pipeline.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 36a6392c22..d65c9db587 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -26,5 +26,5 @@ docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock - docker.registry= docker.credentials=hub.docker.com-springbuildmaster artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c -gradle-enterprise-cache.credentials=gradle_enterprise_cache_user -gradle-enterprise.access-key=gradle_enterprise_secret_access_key +develocity.cache.credentials=gradle_enterprise_cache_user +develocity.access-key=gradle_enterprise_secret_access_key From c2b77606b74843bb15246d706cf4b310c99ecd46 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 19 Oct 2023 09:52:07 -0500 Subject: [PATCH 494/821] Change from Gradle Enterprise to Develocity. Gradle Enterprise has been rebranding as Develocity. See #3197. --- .mvn/gradle-enterprise.xml | 4 ++-- Jenkinsfile | 30 +++++++++++++++--------------- ci/test.sh | 8 +++++--- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.mvn/gradle-enterprise.xml b/.mvn/gradle-enterprise.xml index 135c118348..f7a3fb6f95 100644 --- a/.mvn/gradle-enterprise.xml +++ b/.mvn/gradle-enterprise.xml @@ -21,11 +21,11 @@ spring-builds+jenkins - ${env.GRADLE_ENTERPRISE_CACHE_PASSWORD} + ${env.DEVELOCITY_CACHE_PASSWORD} true - #{env['GRADLE_ENTERPRISE_CACHE_USERNAME'] != null and env['GRADLE_ENTERPRISE_CACHE_PASSWORD'] != null} + #{env['DEVELOCITY_CACHE_USERNAME'] != null and env['DEVELOCITY_CACHE_PASSWORD'] != null} \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index f655933ed6..ac69653a68 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -32,8 +32,8 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES') } environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { @@ -62,8 +62,8 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { @@ -81,8 +81,8 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { @@ -100,8 +100,8 @@ pipeline { options { timeout(time: 30, unit: 'MINUTES')} environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } steps { @@ -130,17 +130,17 @@ pipeline { environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") + DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ' + - 'GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} ' + - 'GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} ' + - 'GRADLE_ENTERPRISE_ACCESS_KEY=${GRADLE_ENTERPRISE_ACCESS_KEY} ' + + sh 'MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" ' + + 'DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} ' + + 'DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} ' + + 'GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} ' + './mvnw -s settings.xml -Pci,artifactory ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + @@ -148,7 +148,7 @@ pipeline { "-Dartifactory.staging-repository=libs-snapshot-local " + "-Dartifactory.build-name=spring-data-jpa " + "-Dartifactory.build-number=${BUILD_NUMBER} " + - '-Duser.name=spring-builds+jenkins -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa-enterprise ' + + '-Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa-enterprise ' + '-Dmaven.test.skip=true clean deploy -U -B ' } diff --git a/ci/test.sh b/ci/test.sh index 2f5db3d48c..87954b8b3d 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -6,9 +6,11 @@ mkdir -p /tmp/jenkins-home/.m2/spring-data-jpa mkdir -p /tmp/jenkins-home/.m2/.gradle-enterprise chown -R 1001:1001 . -export GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USR} -export GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PSW} -export GRADLE_ENTERPRISE_ACCESS_KEY=${GRADLE_ENTERPRISE_ACCESS_KEY} +export DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} +export DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} + +# The environment variable to configure access key is still GRADLE_ENTERPRISE_ACCESS_KEY +export GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" \ ./mvnw -s settings.xml \ From 018c2acd86d0a901353036dcdf44eda0bc625bca Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Thu, 19 Oct 2023 16:33:05 -0500 Subject: [PATCH 495/821] Polishing. --- .mvn/gradle-enterprise.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/gradle-enterprise.xml b/.mvn/gradle-enterprise.xml index f7a3fb6f95..bbc0073bfe 100644 --- a/.mvn/gradle-enterprise.xml +++ b/.mvn/gradle-enterprise.xml @@ -20,7 +20,7 @@ - spring-builds+jenkins + ${env.DEVELOCITY_CACHE_USERNAME} ${env.DEVELOCITY_CACHE_PASSWORD} From 0aa5be8ba2d24d8e659cc2d7404bf9641fa05e27 Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Mon, 6 Nov 2023 10:47:01 -0600 Subject: [PATCH 496/821] Properly handle EXTRACT function in HQL. HQL's extract function supports things like "day of week", "day of month", and "week of year". Extend support to these alternate expressions. See #3219. --- .../data/jpa/repository/query/Hql.g4 | 3 ++ .../repository/query/HqlQueryRenderer.java | 39 ++++++++++++++++++- .../query/HqlQueryRendererTests.java | 30 ++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 index 1e80b10fec..6d6f7567d6 100644 --- a/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 +++ b/spring-data-jpa/src/main/antlr4/org/springframework/data/jpa/repository/query/Hql.g4 @@ -382,6 +382,9 @@ expression | expression op=('*' | '/') expression # MultiplicationExpression | expression op=('+' | '-') expression # AdditionExpression | expression '||' expression # HqlConcatenationExpression + | DAY OF WEEK # DayOfWeekExpression + | DAY OF MONTH # DayOfMonthExpression + | WEEK OF YEAR # WeekOfYearExpression ; primaryExpression diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java index ca2e934b82..bfb2991df6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/HqlQueryRenderer.java @@ -1223,6 +1223,42 @@ public List visitHqlConcatenationExpression(HqlParser.HqlC return tokens; } + @Override + public List visitDayOfWeekExpression(HqlParser.DayOfWeekExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.DAY())); + tokens.add(new JpaQueryParsingToken(ctx.OF())); + tokens.add(new JpaQueryParsingToken(ctx.WEEK())); + + return tokens; + } + + @Override + public List visitDayOfMonthExpression(HqlParser.DayOfMonthExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.DAY())); + tokens.add(new JpaQueryParsingToken(ctx.OF())); + tokens.add(new JpaQueryParsingToken(ctx.MONTH())); + + return tokens; + } + + @Override + public List visitWeekOfYearExpression(HqlParser.WeekOfYearExpressionContext ctx) { + + List tokens = new ArrayList<>(); + + tokens.add(new JpaQueryParsingToken(ctx.WEEK())); + tokens.add(new JpaQueryParsingToken(ctx.OF())); + tokens.add(new JpaQueryParsingToken(ctx.YEAR())); + + return tokens; + } + @Override public List visitGroupedExpression(HqlParser.GroupedExpressionContext ctx) { @@ -1915,11 +1951,12 @@ public List visitExtractFunction(HqlParser.ExtractFunction if (ctx.EXTRACT() != null) { - tokens.add(new JpaQueryParsingToken(ctx.EXTRACT())); + tokens.add(new JpaQueryParsingToken(ctx.EXTRACT(), false)); tokens.add(TOKEN_OPEN_PAREN); tokens.addAll(visit(ctx.expression(0))); tokens.add(new JpaQueryParsingToken(ctx.FROM())); tokens.addAll(visit(ctx.expression(1))); + NOSPACE(tokens); tokens.add(TOKEN_CLOSE_PAREN); } else if (ctx.dateTimeFunction() != null) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java index 39ce36c605..5d3ef0da1f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlQueryRendererTests.java @@ -1605,4 +1605,34 @@ void newShouldBeLegalAsPartOfAStateFieldPathExpression() { void powerShouldBeLegalInAQuery() { assertQuery("select e.power.id from MyEntity e"); } + + @Test // GH-3219 + void extractFunctionShouldSupportAdditionalExtensions() { + + assertQuery(""" + select extract(day of week from departureTime) AS day, sum(duration) as duration from JourneyEntity + group by extract(day of week from departureTime) + """); + assertQuery(""" + select extract(day of month from departureTime) AS day, sum(duration) as duration from JourneyEntity + group by extract(day of month from departureTime) + """); + assertQuery(""" + select extract(week of year from departureTime) AS day, sum(duration) as duration from JourneyEntity + group by extract(week of year from departureTime) + """); + + assertQuery(""" + select extract(date from departureTime) AS date + group by extract(date from departureTime) + """); + assertQuery(""" + select extract(time from departureTime) AS time + group by extract(time from departureTime) + """); + assertQuery(""" + select extract(epoch from departureTime) AS epoch + group by extract(epoch from departureTime) + """); + } } From c38b8ac2c3aaab4a674548ca86f44b60d5ee65db Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 Nov 2023 14:28:28 +0100 Subject: [PATCH 497/821] Prepare 3.2 GA (2023.1.0). See #3197 --- pom.xml | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 94542470ab..c798b49816 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0-SNAPSHOT + 3.2.0 @@ -37,7 +37,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0-SNAPSHOT + 3.2.0 0.10.3 org.hibernate @@ -237,20 +237,8 @@ - - spring-snapshot - https://repo.spring.io/snapshot - - true - - - false - - - - spring-milestone - https://repo.spring.io/milestone - + + From 76a03e2d06c5c59a0e39841c68a90a8f7571f17d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 Nov 2023 14:29:58 +0100 Subject: [PATCH 498/821] Release version 3.2 GA (2023.1.0). See #3197 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index c798b49816..9b9256e421 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0 pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index f239d6394b..b25ada4834 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0-SNAPSHOT + 3.2.0 org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index a458953182..869bba36b9 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 2c9677329f..de3174f271 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0-SNAPSHOT + 3.2.0 Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0-SNAPSHOT + 3.2.0 ../pom.xml From 21d307db3f7b421d35c6a9641e638671d2f55cbb Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 Nov 2023 14:33:24 +0100 Subject: [PATCH 499/821] Prepare next development iteration. See #3197 --- pom.xml | 2 +- spring-data-envers/pom.xml | 4 ++-- spring-data-jpa-distribution/pom.xml | 2 +- spring-data-jpa/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 9b9256e421..ef5c54f461 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0 + 3.3.0-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index b25ada4834..470678d048 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.2.0 + 3.3.0-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.2.0 + 3.3.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 869bba36b9..6bd074181c 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0 + 3.3.0-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index de3174f271..ea9590bf8e 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.2.0 + 3.3.0-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.2.0 + 3.3.0-SNAPSHOT ../pom.xml From 83cb82ba07345fffc44042bae7f9fae02e338239 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 17 Nov 2023 14:33:26 +0100 Subject: [PATCH 500/821] After release cleanups. See #3197 --- pom.xml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index ef5c54f461..670a46eefe 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.springframework.data.build spring-data-parent - 3.2.0 + 3.3.0-SNAPSHOT @@ -37,7 +37,7 @@ 4.5 8.0.33 42.6.0 - 3.2.0 + 3.3.0-SNAPSHOT 0.10.3 org.hibernate @@ -237,8 +237,20 @@ - - + + spring-snapshot + https://repo.spring.io/snapshot + + true + + + false + + + + spring-milestone + https://repo.spring.io/milestone + From adaae1c04066fbdc413547688b3e00c2a56e5ad9 Mon Sep 17 00:00:00 2001 From: Runbing Date: Sun, 19 Nov 2023 18:02:14 +0800 Subject: [PATCH 501/821] Fixed the URL for the Spring Data Commons documentation. I fixed an incorrect URL in the **antora.yml** file that was preventing other pages from linking to the Spring Data Commons documentation. Closes #3232 --- src/main/antora/resources/antora-resources/antora.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/antora/resources/antora-resources/antora.yml b/src/main/antora/resources/antora-resources/antora.yml index 61386b7d27..4b911037b3 100644 --- a/src/main/antora/resources/antora-resources/antora.yml +++ b/src/main/antora/resources/antora-resources/antora.yml @@ -9,7 +9,7 @@ asciidoc: attribute-missing: 'warn' commons: ${springdata.commons.docs} include-xml-namespaces: false - spring-data-commons-docs-url: https://docs.spring.io/spring-data-commons/reference + spring-data-commons-docs-url: https://docs.spring.io/spring-data/commons/reference spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/commons/docs/${springdata.commons}/api/ springdocsurl: https://docs.spring.io/spring-framework/reference/{springversionshort} springjavadocurl: https://docs.spring.io/spring-framework/docs/${spring}/javadoc-api From 6df5bd76852b24e2aa277c2beb13a9abf886dc12 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 11 Oct 2023 23:27:11 +0200 Subject: [PATCH 502/821] Add build profile for Hibernate 6.4 snapshots. Original pull request: #3201 See #3239 --- Jenkinsfile | 19 +++++++++++++++++++ pom.xml | 20 ++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ac69653a68..4b75791d6e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -74,6 +74,25 @@ pipeline { } } } + stage("test: baseline (hibernate 6.4.x snapshots)") { + agent { + label 'data' + } + options { timeout(time: 30, unit: 'MINUTES')} + environment { + ARTIFACTORY = credentials("${p['artifactory.credentials']}") + GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") + GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") + TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' + } + steps { + script { + docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { + sh 'PROFILE=all-dbs,hibernate-64-next ci/test.sh' + } + } + } + } stage("test: java.next (next)") { agent { label 'data' diff --git a/pom.xml b/pom.xml index 670a46eefe..610ca0b850 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,8 @@ 3.0.3 4.0.2 6.3.1.Final - 6.3.2-SNAPSHOT + 6.4.0-SNAPSHOT + 6.3.2-SNAPSHOT 2.7.1

    2.2.220

    4.5 @@ -54,10 +55,25 @@ + + hibernate-64-next + + ${hibernate-64-next-snapshots} + + + + sonatype-oss + https://oss.sonatype.org/content/repositories/snapshots + + false + + + + hibernate-63-next - ${hibernate-next-snapshots} + ${hibernate-63-next-snapshots} From 9dc87933e538a70758a86102621d642e58e6fdd5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 24 Nov 2023 08:47:41 +0100 Subject: [PATCH 503/821] Upgrade to Hibernate 6.4. Closes #3239 Original pull request: #3201 --- Jenkinsfile | 21 +-------------------- pom.xml | 20 ++------------------ 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4b75791d6e..68fd53ba84 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -55,7 +55,7 @@ pipeline { } parallel { - stage("test: baseline (hibernate 6.3.x snapshots)") { + stage("test: baseline (hibernate 6.4.x snapshots)") { agent { label 'data' } @@ -66,25 +66,6 @@ pipeline { DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' } - steps { - script { - docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { - sh 'PROFILE=all-dbs,hibernate-63-next ci/test.sh' - } - } - } - } - stage("test: baseline (hibernate 6.4.x snapshots)") { - agent { - label 'data' - } - options { timeout(time: 30, unit: 'MINUTES')} - environment { - ARTIFACTORY = credentials("${p['artifactory.credentials']}") - GRADLE_ENTERPRISE_CACHE = credentials("${p['gradle-enterprise-cache.credentials']}") - GRADLE_ENTERPRISE_ACCESS_KEY = credentials("${p['gradle-enterprise.access-key']}") - TESTCONTAINERS_IMAGE_SUBSTITUTOR = 'org.springframework.data.jpa.support.ProxyImageNameSubstitutor' - } steps { script { docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { diff --git a/pom.xml b/pom.xml index 610ca0b850..fb6b36a176 100644 --- a/pom.xml +++ b/pom.xml @@ -30,9 +30,8 @@ 4.10.1 3.0.3 4.0.2 - 6.3.1.Final - 6.4.0-SNAPSHOT - 6.3.2-SNAPSHOT + 6.4.0.Final + 6.4.1-SNAPSHOT 2.7.1

    2.2.220

    4.5 @@ -70,21 +69,6 @@
    - - hibernate-63-next - - ${hibernate-63-next-snapshots} - - - - sonatype-oss - https://oss.sonatype.org/content/repositories/snapshots - - false - - - - all-dbs From bffd7cf34ea1574cd632c186fbf76475953a700e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 24 Nov 2023 09:51:28 +0100 Subject: [PATCH 504/821] Introduce property for Jenkins user and Artifactory server details. Closes #3240 --- Jenkinsfile | 33 +++++++++++++++++++-------------- ci/pipeline.properties | 5 +++-- ci/test.sh | 5 +++-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 68fd53ba84..3b5c9dfd21 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -39,7 +39,9 @@ pipeline { steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { - sh 'PROFILE=all-dbs ci/test.sh' + sh "PROFILE=all-dbs " + + "JENKINS_USER_NAME=${p['jenkins.user.name']} " + + "ci/test.sh" } } } @@ -69,7 +71,9 @@ pipeline { steps { script { docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { - sh 'PROFILE=all-dbs,hibernate-64-next ci/test.sh' + sh "PROFILE=all-dbs,hibernate-64-next " + + "JENKINS_USER_NAME=${p['jenkins.user.name']} " + + "ci/test.sh" } } } @@ -88,7 +92,9 @@ pipeline { steps { script { docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { - sh 'PROFILE=all-dbs ci/test.sh' + sh "PROFILE=all-dbs " + + "JENKINS_USER_NAME=${p['jenkins.user.name']} " + + "ci/test.sh" } } } @@ -107,7 +113,9 @@ pipeline { steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { - sh 'PROFILE=all-dbs,eclipselink-next ci/test.sh' + sh "PROFILE=all-dbs,eclipselink-next " + + "JENKINS_USER_NAME=${p['jenkins.user.name']} " + + "ci/test.sh" } } } @@ -127,30 +135,27 @@ pipeline { label 'data' } options { timeout(time: 20, unit: 'MINUTES') } - environment { ARTIFACTORY = credentials("${p['artifactory.credentials']}") DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") } - steps { script { docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { - sh 'MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" ' + - 'DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} ' + - 'DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} ' + - 'GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} ' + - './mvnw -s settings.xml -Pci,artifactory ' + - '-Dartifactory.server=https://repo.spring.io ' + + sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' + + "DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} " + + "DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} " + + "GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} " + + "./mvnw -s settings.xml -Pci,artifactory " + + "-Dartifactory.server=${p['artifactory.url']} " + "-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " + - "-Dartifactory.staging-repository=libs-snapshot-local " + + "-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " + "-Dartifactory.build-name=spring-data-jpa " + "-Dartifactory.build-number=${BUILD_NUMBER} " + '-Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa-enterprise ' + '-Dmaven.test.skip=true clean deploy -U -B ' - } } } diff --git a/ci/pipeline.properties b/ci/pipeline.properties index d65c9db587..0a9b63b2db 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -17,14 +17,15 @@ docker.redis.6.version=6.2.13 # Supported versions of Cassandra docker.cassandra.3.version=3.11.16 - # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home - # Credentials docker.registry= docker.credentials=hub.docker.com-springbuildmaster artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c +artifactory.url=https://repo.spring.io +artifactory.repository.snapshot=libs-snapshot-local develocity.cache.credentials=gradle_enterprise_cache_user develocity.access-key=gradle_enterprise_secret_access_key +jenkins.user.name=spring-builds+jenkins diff --git a/ci/test.sh b/ci/test.sh index 87954b8b3d..72d596e2c3 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -8,15 +8,16 @@ chown -R 1001:1001 . export DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} export DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} +export JENKINS_USER=${JENKINS_USER_NAME} # The environment variable to configure access key is still GRADLE_ENTERPRISE_ACCESS_KEY export GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} -MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" \ +MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \ ./mvnw -s settings.xml \ -P${PROFILE} clean dependency:list test -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa -MAVEN_OPTS="-Duser.name=spring-builds+jenkins -Duser.home=/tmp/jenkins-home" \ +MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \ ./mvnw -s settings.xml clean -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa chown -R 1001:1001 /tmp/jenkins-home/.m2/.gradle-enterprise From bac05b7392210f4ff222b45e2dcc06a8c80d2106 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 24 Nov 2023 10:26:44 +0100 Subject: [PATCH 505/821] Upgrade to Antlr 4.13.0. Align with Hibernate 6.4.0. See #3239 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fb6b36a176..a3be6252ed 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ - 4.10.1 + 4.13.0 3.0.3 4.0.2 6.4.0.Final From 970b7f0312993375a746153eefbd47c21d98fedc Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 29 Nov 2023 11:30:05 +0100 Subject: [PATCH 506/821] Disable Gradle Enterprise scanning for clean run. See #3240 --- ci/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test.sh b/ci/test.sh index 72d596e2c3..7e1fecebf0 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -18,6 +18,6 @@ MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \ -P${PROFILE} clean dependency:list test -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \ - ./mvnw -s settings.xml clean -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa + ./mvnw -s settings.xml clean -Dscan=false -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-jpa chown -R 1001:1001 /tmp/jenkins-home/.m2/.gradle-enterprise From 94da1837d45dc86064fd10c220d04a09952fbb32 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 4 Dec 2023 16:03:52 +0100 Subject: [PATCH 507/821] Adopt `JpaParameters` to reflect the actual parameter type when using generics. Closes #3254 --- .../jpa/repository/query/JpaParameters.java | 45 +++++++++++++++---- .../jpa/repository/query/JpaQueryMethod.java | 5 ++- ...BindableJpaParametersIntegrationTests.java | 19 ++++---- ...aParametersParameterAccessorUnitTests.java | 6 +-- .../JpaParametersParameterAccessorTests.java | 7 +-- .../query/JpaParametersUnitTests.java | 12 +++-- .../query/JpaQueryExecutionUnitTests.java | 32 +++++++------ .../query/JpaQueryMethodUnitTests.java | 35 ++++++++++----- .../repository/query/NamedQueryUnitTests.java | 13 +++--- .../query/ParameterBinderUnitTests.java | 38 +++++++++------- .../ParameterExpressionProviderTests.java | 3 +- ...meterMetadataProviderIntegrationTests.java | 9 ++-- .../query/SimpleJpaQueryUnitTests.java | 2 + 13 files changed, 143 insertions(+), 83 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index b41144a3f4..d29ba39bdb 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -20,12 +20,15 @@ import java.lang.reflect.Method; import java.util.Date; import java.util.List; +import java.util.function.Function; import org.springframework.core.MethodParameter; import org.springframework.data.jpa.repository.Temporal; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; +import org.springframework.data.repository.query.ParametersSource; +import org.springframework.data.util.TypeInformation; import org.springframework.lang.Nullable; /** @@ -37,24 +40,33 @@ */ public class JpaParameters extends Parameters { + /** + * Creates a new {@link JpaParameters} instance from the given {@link ParametersSource}. + * + * @param parametersSource must not be {@literal null}. + * @since 3.2.1 + */ + public JpaParameters(ParametersSource parametersSource) { + super(parametersSource, + methodParameter -> new JpaParameter(methodParameter, parametersSource.getDomainTypeInformation())); + } + /** * Creates a new {@link JpaParameters} instance from the given {@link Method}. * - * @param method must not be {@literal null}. + * @param parametersSource must not be {@literal null}. + * @param parameterFactory must not be {@literal null}. + * @since 3.2.1 */ - public JpaParameters(Method method) { - super(method); + protected JpaParameters(ParametersSource parametersSource, + Function parameterFactory) { + super(parametersSource, parameterFactory); } private JpaParameters(List parameters) { super(parameters); } - @Override - protected JpaParameter createParameter(MethodParameter parameter) { - return new JpaParameter(parameter); - } - @Override protected JpaParameters createFrom(List parameters) { return new JpaParameters(parameters); @@ -82,14 +94,31 @@ public static class JpaParameter extends Parameter { * Creates a new {@link JpaParameter}. * * @param parameter must not be {@literal null}. + * @deprecated since 3.2.1 */ + @Deprecated(since = "3.2.1", forRemoval = true) protected JpaParameter(MethodParameter parameter) { super(parameter); this.annotation = parameter.getParameterAnnotation(Temporal.class); this.temporalType = null; + if (!isDateParameter() && hasTemporalParamAnnotation()) { + throw new IllegalArgumentException( + Temporal.class.getSimpleName() + " annotation is only allowed on Date parameter"); + } + } + + /** + * Creates a new {@link JpaParameter}. + * + * @param parameter must not be {@literal null}. + */ + protected JpaParameter(MethodParameter parameter, TypeInformation domainType) { + super(parameter, domainType); + this.annotation = parameter.getParameterAnnotation(Temporal.class); + this.temporalType = null; if (!isDateParameter() && hasTemporalParamAnnotation()) { throw new IllegalArgumentException( Temporal.class.getSimpleName() + " annotation is only allowed on Date parameter"); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index ed566ba52c..5660951cf0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -42,6 +42,7 @@ import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; +import org.springframework.data.repository.query.ParametersSource; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.util.QueryExecutionConverters; import org.springframework.data.util.Lazy; @@ -447,8 +448,8 @@ private T getMergedOrDefaultAnnotationValue(String attribute, Class annotati } @Override - protected JpaParameters createParameters(Method method) { - return new JpaParameters(method); + protected Parameters createParameters(ParametersSource parametersSource) { + return new JpaParameters(parametersSource); } @Override diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java index 0ddbbdf14b..887698f8f1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/CustomNonBindableJpaParametersIntegrationTests.java @@ -37,6 +37,8 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.query.ParametersSource; +import org.springframework.data.util.TypeInformation; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -70,8 +72,8 @@ private static class NonBindableAwareJpaParameter extends JpaParameters.JpaParam private final MethodParameter parameter; - NonBindableAwareJpaParameter(MethodParameter parameter) { - super(parameter); + NonBindableAwareJpaParameter(MethodParameter parameter, TypeInformation domainType) { + super(parameter, domainType); this.parameter = parameter; } @@ -83,14 +85,11 @@ public boolean isBindable() { private static class NonBindableAwareJpaParameters extends JpaParameters { - NonBindableAwareJpaParameters(Method method) { - super(method); + NonBindableAwareJpaParameters(ParametersSource parametersSource) { + super(parametersSource, + param -> new NonBindableAwareJpaParameter(param, parametersSource.getDomainTypeInformation())); } - @Override - protected JpaParameter createParameter(MethodParameter parameter) { - return new NonBindableAwareJpaParameter(parameter); - } } private static class NonBindableAwareJpaQueryMethod extends JpaQueryMethod { @@ -101,8 +100,8 @@ private static class NonBindableAwareJpaQueryMethod extends JpaQueryMethod { } @Override - protected JpaParameters createParameters(Method method) { - return new NonBindableAwareJpaParameters(method); + protected JpaParameters createParameters(ParametersSource source) { + return new NonBindableAwareJpaParameters(source); } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessorUnitTests.java index 0e7135c5c6..6ec33ba71f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HibernateJpaParametersParameterAccessorUnitTests.java @@ -7,9 +7,9 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; + import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.jpa.repository.query.HibernateJpaParametersParameterAccessor; -import org.springframework.data.jpa.repository.query.JpaParameters; +import org.springframework.data.repository.query.ParametersSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; @@ -48,7 +48,7 @@ void withinTransaction() throws Exception { private void parametersCanGetAccessesOutsideTransaction() throws NoSuchMethodException { Method method = EntityManager.class.getMethod("flush"); - JpaParameters parameters = new JpaParameters(method); + JpaParameters parameters = new JpaParameters(ParametersSource.of(method)); HibernateJpaParametersParameterAccessor accessor = new HibernateJpaParametersParameterAccessor(parameters, new Object[] {}, em); Assertions.assertEquals(0, accessor.getValues().length); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java index 0a702eaed4..a49c24787a 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessorTests.java @@ -15,8 +15,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; + import org.springframework.data.jpa.domain.sample.User; -import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.repository.query.ParametersSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -42,7 +43,7 @@ void createsJpaParametersParameterAccessor() throws Exception { Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); Object[] values = { null }; - JpaParameters parameters = new JpaParameters(withNativeQuery); + JpaParameters parameters = new JpaParameters(ParametersSource.of(withNativeQuery)); JpaParametersParameterAccessor accessor = new JpaParametersParameterAccessor(parameters, values); bind(parameters, accessor); @@ -55,7 +56,7 @@ void createsHibernateParametersParameterAccessor() throws Exception { Method withNativeQuery = SampleRepository.class.getMethod("withNativeQuery", Integer.class); Object[] values = { null }; - JpaParameters parameters = new JpaParameters(withNativeQuery); + JpaParameters parameters = new JpaParameters(ParametersSource.of(withNativeQuery)); JpaParametersParameterAccessor accessor = new HibernateJpaParametersParameterAccessor(parameters, values, em); bind(parameters, accessor); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java index 14d3fe3749..3d2916f626 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaParametersUnitTests.java @@ -18,20 +18,24 @@ import static jakarta.persistence.TemporalType.*; import static org.assertj.core.api.Assertions.*; +import jakarta.persistence.TemporalType; + import java.lang.reflect.Method; import java.util.Date; -import jakarta.persistence.TemporalType; - import org.junit.jupiter.api.Test; + import org.springframework.data.jpa.repository.Temporal; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.query.ParametersSource; /** * Unit tests for {@link JpaParameters}. * * @author Oliver Gierke * @author Jens Schauder + * @author Mark Paluch */ class JpaParametersUnitTests { @@ -40,7 +44,7 @@ void findsTemporalParameterConfiguration() throws Exception { Method method = SampleRepository.class.getMethod("foo", Date.class, String.class); - JpaParameters parameters = new JpaParameters(method); + JpaParameters parameters = new JpaParameters(ParametersSource.of(method)); JpaParameter parameter = parameters.getBindableParameter(0); assertThat(parameter.isSpecialParameter()).isFalse(); @@ -51,7 +55,7 @@ void findsTemporalParameterConfiguration() throws Exception { assertThat(parameter.isTemporalParameter()).isFalse(); } - interface SampleRepository { + interface SampleRepository extends Repository { void foo(@Temporal(TIMESTAMP) Date date, String firstname); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java index 7be1ec4d9e..8dd45b61e7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java @@ -20,16 +20,15 @@ import static org.mockito.Mockito.*; import io.vavr.control.Try; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.Optional; -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; -import jakarta.persistence.TypedQuery; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -48,6 +47,7 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; +import org.springframework.data.repository.query.ParametersSource; /** * Unit test for {@link JpaQueryExecution}. @@ -177,7 +177,8 @@ void modifyingExecutionRejectsNonIntegerOrVoidReturnType() { @Test // DATAJPA-124, DATAJPA-912 void pagedExecutionRetrievesObjectsForPageableOutOfRange() throws Exception { - JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + JpaParameters parameters = new JpaParameters( + ParametersSource.of(getClass().getMethod("sampleMethod", Pageable.class))); when(jpaQuery.createCountQuery(Mockito.any())).thenReturn(countQuery); when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(countQuery.getResultList()).thenReturn(Arrays.asList(20L)); @@ -193,7 +194,8 @@ void pagedExecutionRetrievesObjectsForPageableOutOfRange() throws Exception { @Test // DATAJPA-477, DATAJPA-912 void pagedExecutionShouldNotGenerateCountQueryIfQueryReportedNoResults() throws Exception { - JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + JpaParameters parameters = new JpaParameters( + ParametersSource.of(getClass().getMethod("sampleMethod", Pageable.class))); when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Arrays.asList(0L)); @@ -208,7 +210,8 @@ void pagedExecutionShouldNotGenerateCountQueryIfQueryReportedNoResults() throws @Test // DATAJPA-912 void pagedExecutionShouldUseCountFromResultIfOffsetIsZeroAndResultsWithinPageSize() throws Exception { - JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + JpaParameters parameters = new JpaParameters( + ParametersSource.of(getClass().getMethod("sampleMethod", Pageable.class))); when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Arrays.asList(new Object(), new Object(), new Object(), new Object())); @@ -222,7 +225,8 @@ void pagedExecutionShouldUseCountFromResultIfOffsetIsZeroAndResultsWithinPageSiz @Test // DATAJPA-912 void pagedExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSize() throws Exception { - JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + JpaParameters parameters = new JpaParameters( + ParametersSource.of(getClass().getMethod("sampleMethod", Pageable.class))); when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Arrays.asList(new Object(), new Object(), new Object(), new Object())); @@ -234,10 +238,10 @@ void pagedExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSize() } @Test // DATAJPA-912 - void pagedExecutionShouldUseRequestCountFromResultWithOffsetAndResultsHitLowerPageSizeBounds() - throws Exception { + void pagedExecutionShouldUseRequestCountFromResultWithOffsetAndResultsHitLowerPageSizeBounds() throws Exception { - JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + JpaParameters parameters = new JpaParameters( + ParametersSource.of(getClass().getMethod("sampleMethod", Pageable.class))); when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Collections.emptyList()); when(jpaQuery.createCountQuery(Mockito.any())).thenReturn(query); @@ -251,10 +255,10 @@ void pagedExecutionShouldUseRequestCountFromResultWithOffsetAndResultsHitLowerPa } @Test // DATAJPA-912 - void pagedExecutionShouldUseRequestCountFromResultWithOffsetAndResultsHitUpperPageSizeBounds() - throws Exception { + void pagedExecutionShouldUseRequestCountFromResultWithOffsetAndResultsHitUpperPageSizeBounds() throws Exception { - JpaParameters parameters = new JpaParameters(getClass().getMethod("sampleMethod", Pageable.class)); + JpaParameters parameters = new JpaParameters( + ParametersSource.of(getClass().getMethod("sampleMethod", Pageable.class))); when(jpaQuery.createQuery(Mockito.any())).thenReturn(query); when(query.getResultList()).thenReturn(Arrays.asList(new Object(), new Object(), new Object(), new Object())); when(jpaQuery.createCountQuery(Mockito.any())).thenReturn(query); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java index 91b92738d5..76c34bd7af 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryMethodUnitTests.java @@ -68,6 +68,7 @@ * @author Mark Paluch * @author Erik Pellizzon */ +@SuppressWarnings({"rawtypes", "unchecked"}) @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class JpaQueryMethodUnitTests { @@ -156,6 +157,8 @@ void returnsQueryIfAvailable() throws Exception { void rejectsInvalidReturntypeOnPagebleFinder() { when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + when(metadata.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(User.class)); + when(metadata.getRepositoryInterface()).thenReturn((Class) InvalidRepository.class); assertThatIllegalStateException() .isThrownBy(() -> new JpaQueryMethod(invalidReturnType, metadata, factory, extractor)); @@ -165,6 +168,8 @@ void rejectsInvalidReturntypeOnPagebleFinder() { void rejectsPageableAndSortInFinderMethod() { when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + when(metadata.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(User.class)); + when(metadata.getRepositoryInterface()).thenReturn((Class) InvalidRepository.class); assertThatIllegalStateException() .isThrownBy(() -> new JpaQueryMethod(pageableAndSort, metadata, factory, extractor)); @@ -318,8 +323,10 @@ void detectsLockAndQueryHintsOnIfUsedAsMetaAnnotation() throws Exception { @Test // DATAJPA-466 void shouldStoreJpa21FetchGraphInformationAsHint() { - doReturn(User.class).when(metadata).getDomainType(); - doReturn(User.class).when(metadata).getReturnedDomainClass(queryMethodWithCustomEntityFetchGraph); + when(metadata.getDomainType()).thenReturn((Class) User.class); + when(metadata.getReturnedDomainClass(queryMethodWithCustomEntityFetchGraph)).thenReturn((Class) User.class); + when(metadata.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(User.class)); + when(metadata.getRepositoryInterface()).thenReturn((Class) InvalidRepository.class); JpaQueryMethod method = new JpaQueryMethod(queryMethodWithCustomEntityFetchGraph, metadata, factory, extractor); @@ -331,8 +338,10 @@ void shouldStoreJpa21FetchGraphInformationAsHint() { @Test // DATAJPA-612 void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethod() throws Exception { - doReturn(User.class).when(metadata).getDomainType(); - doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any()); + when(metadata.getDomainType()).thenReturn((Class) User.class); + when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + when(metadata.getReturnedDomainClass(queryMethodWithCustomEntityFetchGraph)).thenReturn((Class) User.class); + when(metadata.getRepositoryInterface()).thenReturn((Class) JpaRepositoryOverride.class); JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findAll"), metadata, factory, extractor); @@ -345,8 +354,10 @@ void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethod() thro @Test // DATAJPA-689 void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethodFindOne() throws Exception { - doReturn(User.class).when(metadata).getDomainType(); - doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any()); + when(metadata.getDomainType()).thenReturn((Class) User.class); + when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + when(metadata.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(User.class)); + when(metadata.getRepositoryInterface()).thenReturn((Class) InvalidRepository.class); JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("findOne", Integer.class), metadata, factory, extractor); @@ -362,8 +373,10 @@ void shouldFindEntityGraphAnnotationOnOverriddenSimpleJpaRepositoryMethodFindOne @Test void shouldFindEntityGraphAnnotationOnQueryMethodGetOneByWithDerivedName() throws Exception { - doReturn(User.class).when(metadata).getDomainType(); - doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any()); + when(metadata.getDomainType()).thenReturn((Class) User.class); + when(metadata.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(User.class)); + when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + when(metadata.getRepositoryInterface()).thenReturn((Class) JpaRepositoryOverride.class); JpaQueryMethod method = new JpaQueryMethod(JpaRepositoryOverride.class.getMethod("getOneById", Integer.class), metadata, factory, extractor); @@ -473,8 +486,10 @@ void usesAliasedValueForQueryNativeQuery() throws Exception { @Test // DATAJPA-871 void usesAliasedValueForEntityGraph() throws Exception { - doReturn(User.class).when(metadata).getDomainType(); - doReturn(User.class).when(metadata).getReturnedDomainClass((Method) any()); + when(metadata.getDomainType()).thenReturn((Class) User.class); + when(metadata.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(User.class)); + when(metadata.getReturnedDomainClass(any())).thenReturn((Class) User.class); + when(metadata.getRepositoryInterface()).thenReturn((Class) JpaRepositoryOverride.class); JpaQueryMethod method = new JpaQueryMethod( JpaRepositoryOverride.class.getMethod("getOneWithCustomEntityGraphAnnotation"), metadata, factory, extractor); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java index 4d6f4ac4b9..3f0b7190b0 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/NamedQueryUnitTests.java @@ -15,14 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; @@ -38,6 +33,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; + import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.provider.QueryExtractor; @@ -75,6 +71,7 @@ void setUp() throws SecurityException, NoSuchMethodException { method = SampleRepository.class.getMethod("foo", Pageable.class); when(metadata.getDomainType()).thenReturn((Class) String.class); + when(metadata.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(String.class)); when(metadata.getReturnedDomainClass(method)).thenReturn((Class) String.class); when(metadata.getReturnType(any(Method.class))) .thenAnswer(invocation -> TypeInformation.fromReturnTypeOf(invocation.getArgument(0))); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java index bc22024d07..0a8e865c8d 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterBinderUnitTests.java @@ -15,23 +15,23 @@ */ package org.springframework.data.jpa.repository.query; -import static java.util.Collections.*; import static jakarta.persistence.TemporalType.*; +import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.Mockito.any; -import java.lang.reflect.Method; -import java.util.Date; -import java.util.List; -import java.util.Optional; - import jakarta.persistence.Embeddable; import jakarta.persistence.Parameter; import jakarta.persistence.Query; import jakarta.persistence.TemporalType; +import java.lang.reflect.Method; +import java.util.Date; +import java.util.List; +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -44,7 +44,9 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.Temporal; +import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.Param; +import org.springframework.data.repository.query.ParametersSource; /** * Unit test for {@link ParameterBinder}. @@ -78,7 +80,7 @@ static class User { } - interface SampleRepository { + interface SampleRepository extends Repository { User useIndexedParameters(String lastname); @@ -149,7 +151,7 @@ void usesParameterNameIfAnnotated() { void bindsEmbeddableCorrectly() throws Exception { Method method = getClass().getMethod("findByEmbeddable", SampleEmbeddable.class); - JpaParameters parameters = new JpaParameters(method); + JpaParameters parameters = createParameters(method); SampleEmbeddable embeddable = new SampleEmbeddable(); Object[] values = { embeddable }; @@ -162,7 +164,7 @@ void bindsEmbeddableCorrectly() throws Exception { void shouldSetTemporalQueryParameterToDate() throws Exception { Method method = SampleRepository.class.getMethod("validWithDefaultTemporalTypeParameter", Date.class); - JpaParameters parameters = new JpaParameters(method); + JpaParameters parameters = createParameters(method); Date date = new Date(); Object[] values = { date }; @@ -175,7 +177,7 @@ void shouldSetTemporalQueryParameterToDate() throws Exception { void shouldSetTemporalQueryParameterToTimestamp() throws Exception { Method method = SampleRepository.class.getMethod("validWithCustomTemporalTypeParameter", Date.class); - JpaParameters parameters = new JpaParameters(method); + JpaParameters parameters = createParameters(method); Date date = new Date(); Object[] values = { date }; @@ -188,14 +190,14 @@ void shouldSetTemporalQueryParameterToTimestamp() throws Exception { void shouldThrowIllegalArgumentExceptionIfIsAnnotatedWithTemporalParamAndParameterTypeIsNotDate() throws Exception { Method method = SampleRepository.class.getMethod("invalidWithTemporalTypeParameter", String.class); - assertThatIllegalArgumentException().isThrownBy(() -> new JpaParameters(method)); + assertThatIllegalArgumentException().isThrownBy(() -> createParameters(method)); } @Test // DATAJPA-461 void shouldAllowBindingOfVarArgsAsIs() throws Exception { Method method = SampleRepository.class.getMethod("validWithVarArgs", Integer[].class); - JpaParameters parameters = new JpaParameters(method); + JpaParameters parameters = createParameters(method); Integer[] ids = new Integer[] { 1, 2, 3 }; Object[] values = { ids }; bind(method, parameters, values); @@ -207,7 +209,7 @@ void shouldAllowBindingOfVarArgsAsIs() throws Exception { void unwrapsOptionalParameter() throws Exception { Method method = SampleRepository.class.getMethod("optionalParameter", Optional.class); - JpaParameters parameters = new JpaParameters(method); + JpaParameters parameters = createParameters(method); Object[] values = { Optional.of("Foo") }; bind(method, parameters, values); @@ -221,14 +223,14 @@ void doesNotBindExcessParameters() throws Exception { Method method = SampleRepository.class.getMethod("withQuery", String.class, String.class); Object[] values = { "foo", "superfluous" }; - bind(method, new JpaParameters(method), values); + bind(method, createParameters(method), values); verify(query).setParameter(eq(1), any()); verify(query, never()).setParameter(eq(2), any()); } private void bind(Method method, Object[] values) { - bind(method, new JpaParameters(method), values); + bind(method, createParameters(method), values); } private void bind(Method method, JpaParameters parameters, Object[] values) { @@ -237,7 +239,11 @@ private void bind(Method method, JpaParameters parameters, Object[] values) { } private JpaParametersParameterAccessor getAccessor(Method method, Object... values) { - return new JpaParametersParameterAccessor(new JpaParameters(method), values); + return new JpaParametersParameterAccessor(createParameters(method), values); + } + + private static JpaParameters createParameters(Method method) { + return new JpaParameters(ParametersSource.of(method)); } // needs to be public diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java index f3b16833c2..d5628eca67 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java @@ -30,6 +30,7 @@ import org.springframework.data.repository.query.DefaultParameters; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; +import org.springframework.data.repository.query.ParametersSource; import org.springframework.data.repository.query.parser.Part; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -51,7 +52,7 @@ class ParameterExpressionProviderTests { void createsParameterExpressionWithMostConcreteType() throws Exception { Method method = SampleRepository.class.getMethod("findByIdGreaterThan", int.class); - Parameters parameters = new DefaultParameters(method); + Parameters parameters = new DefaultParameters(ParametersSource.of(method)); ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, new Object[] { 1 }); Part part = new Part("IdGreaterThan", User.class); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java index 85f2816992..17d5aa4e66 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/ParameterMetadataProviderIntegrationTests.java @@ -17,12 +17,12 @@ import static org.assertj.core.api.Assertions.*; -import java.lang.reflect.Method; -import java.util.List; - import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import java.lang.reflect.Method; +import java.util.List; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -30,6 +30,7 @@ import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Parameters; +import org.springframework.data.repository.query.ParametersSource; import org.springframework.data.repository.query.parser.Part; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -77,7 +78,7 @@ void doesNotApplyLikeExpansionOnNonStringProperties() throws Exception { private ParameterMetadataProvider createProvider(Method method) { - JpaParameters parameters = new JpaParameters(method); + JpaParameters parameters = new JpaParameters(ParametersSource.of(method)); simulateDiscoveredParametername(parameters); return new ParameterMetadataProvider(em.getCriteriaBuilder(), parameters, EscapeCharacter.DEFAULT); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java index 80c362c4ef..c7805a0e58 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java @@ -97,7 +97,9 @@ void setUp() throws SecurityException, NoSuchMethodException { when(em.getEntityManagerFactory()).thenReturn(emf); when(em.getDelegate()).thenReturn(em); when(emf.createEntityManager()).thenReturn(em); + when(metadata.getRepositoryInterface()).thenReturn((Class) SampleRepository.class); when(metadata.getDomainType()).thenReturn((Class) User.class); + when(metadata.getDomainTypeInformation()).thenReturn((TypeInformation) TypeInformation.of(User.class)); when(metadata.getReturnedDomainClass(Mockito.any(Method.class))).thenReturn((Class) User.class); when(metadata.getReturnType(Mockito.any(Method.class))) .thenAnswer(invocation -> TypeInformation.fromReturnTypeOf(invocation.getArgument(0))); From 42a20a0c151f52de0f0c75cfb8c278a619baddec Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 8 Dec 2023 16:22:27 +0100 Subject: [PATCH 508/821] Add assertion to verify Hibernate vs. our ANTLR versions. See #3262 --- .../data/jpa/AntlrVersionTests.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/AntlrVersionTests.java diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/AntlrVersionTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/AntlrVersionTests.java new file mode 100644 index 0000000000..3c4f8f6c92 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/AntlrVersionTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa; + +import static org.assertj.core.api.Assertions.*; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.antlr.v4.runtime.RuntimeMetaData; +import org.hibernate.grammars.hql.HqlParser; +import org.junit.jupiter.api.Test; + +import org.springframework.asm.ClassReader; +import org.springframework.asm.ClassVisitor; +import org.springframework.asm.MethodVisitor; +import org.springframework.asm.Opcodes; +import org.springframework.lang.Nullable; + +/** + * Test to verify that we use the same Antlr version as Hibernate. We parse {@code org.hibernate.grammars.hql.HqlParser} + * byte code to extract the constant that represents the Antlr version. + *

    + * If this test fails, we should check and upgrade the Antlr version in our project. + * + * @author Mark Paluch + */ +class AntlrVersionTests { + + @Test + void antlrVersionConvergence() throws Exception { + + ClassReader reader = new ClassReader(HqlParser.class.getName()); + ExpectedAntlrVersionVisitor visitor = new ExpectedAntlrVersionVisitor(); + reader.accept(visitor, 0); + + String expectedVersion = visitor.getExpectedVersion(); + String description = String.format("ANTLR version '%s' expected by Hibernate while our ANTLR version '%s'", + expectedVersion, RuntimeMetaData.VERSION); + + assertThat(expectedVersion).isNotNull(); + assertThat(RuntimeMetaData.VERSION) // + .describedAs(description) // + .isEqualTo(expectedVersion); + } + + private static class ExpectedAntlrVersionVisitor extends ClassVisitor { + + private static final Pattern versionPattern = Pattern.compile("\\d+\\.\\d+\\.\\d+"); + private static final int API = Opcodes.ASM10_EXPERIMENTAL; + + private @Nullable String expectedVersion; + + ExpectedAntlrVersionVisitor() { + super(API); + } + + @Nullable + String getExpectedVersion() { + return expectedVersion; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, + String[] exceptions) { + + // static block + if (!name.equals("")) { + return null; + } + + return new MethodVisitor(API) { + + /** + * Intercept Load constant. First one is the generating version, second is the compile version. + */ + @Override + public void visitLdcInsn(Object value) { + + if (value instanceof String && expectedVersion == null) { + + Matcher matcher = versionPattern.matcher(value.toString()); + if (matcher.matches()) { + expectedVersion = value.toString(); + } + } + super.visitLdcInsn(value); + } + }; + } + } +} From 688e78b85167c6a99283355c62e428be8ba1cd6a Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 11 Dec 2023 10:44:08 +0100 Subject: [PATCH 509/821] Render entire statement after applying sort. This commit makes sure to render the entire statement after applying the sort expression via the QueryEnhancer. Previously only parts, the actual statement body, had been considered. Closes: #3263 Original Pull Request: #3264 --- .../jpa/repository/query/JSqlParserQueryEnhancer.java | 2 +- .../jpa/repository/query/QueryEnhancerUnitTests.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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 03a92fed5e..7b885b98e2 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 @@ -141,7 +141,7 @@ public String applySorting(Sort sort, @Nullable String alias) { selectBody.getOrderByElements().addAll(orderByElements); - return selectBody.toString(); + return selectStatement.toString(); } 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 6140b313bf..b2956cc749 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 @@ -179,6 +179,17 @@ void findsExistingOrderByIndependentOfCase() { assertThat(query).endsWithIgnoringCase("ORDER BY p.firstname, p.lastname asc"); } + @Test // GH-3263 + void preserveSourceQueryWhenAddingSort() { + + StringQuery query = new StringQuery("WITH all_projects AS (SELECT * FROM projects) SELECT * FROM all_projects p", + true); + + assertThat(getEnhancer(query).applySorting(Sort.by("name"), "p")) // + .startsWithIgnoringCase(query.getQueryString()) + .endsWithIgnoringCase("ORDER BY p.name ASC"); + } + @Test // GH-2812 void createCountQueryFromDeleteQuery() { From aa8a8a9ac695a7d1c404529fe66693dbd610cde9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 14 Dec 2023 08:40:35 +0100 Subject: [PATCH 510/821] Upgrade to Maven Wrapper 3.9.6. See #3268 --- .mvn/wrapper/maven-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index e399fe3728..15f4332d38 100755 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -#Wed Oct 04 16:58:05 PDT 2023 -distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip +#Thu Dec 14 08:40:35 CET 2023 +distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip From c9442a23d56583052d674f32d338ab0e9f8fbaab Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 14 Dec 2023 08:50:30 +0100 Subject: [PATCH 511/821] Update CI properties. See #3229 --- ci/pipeline.properties | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index 0a9b63b2db..60057f2659 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,6 +1,6 @@ # Java versions -java.main.tag=17.0.8_7-jdk-focal -java.next.tag=21_35-jdk-jammy +java.main.tag=17.0.9_9-jdk-focal +java.next.tag=21.0.1_12-jdk-jammy # Docker container images - standard docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} @@ -17,9 +17,11 @@ docker.redis.6.version=6.2.13 # Supported versions of Cassandra docker.cassandra.3.version=3.11.16 + # Docker environment settings docker.java.inside.basic=-v $HOME:/tmp/jenkins-home docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home + # Credentials docker.registry= docker.credentials=hub.docker.com-springbuildmaster From b3312deb00e7cc8f0a2518aec63e4b9933a5651b Mon Sep 17 00:00:00 2001 From: EdoardoP92 Date: Sun, 17 Dec 2023 11:31:27 +0100 Subject: [PATCH 512/821] Update SECURITY.adoc markup. Update section markup to AsciiDoc. Closes: #3273 --- SECURITY.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SECURITY.adoc b/SECURITY.adoc index 132faa9202..de17b3ec0c 100644 --- a/SECURITY.adoc +++ b/SECURITY.adoc @@ -1,9 +1,9 @@ -# Security Policy += Security Policy -## Supported Versions +== Supported Versions Please see the https://spring.io/projects/spring-data-jpa[Spring Data JPA] project page for supported versions. -## Reporting a Vulnerability +== Reporting a Vulnerability Please don't raise security vulnerabilities here. Head over to https://pivotal.io/security to learn how to disclose them responsibly. From 40c5f44161cbe10abd510c39d739586c9e5c9440 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Thu, 21 Dec 2023 08:32:27 +0700 Subject: [PATCH 513/821] Compile-time weaving requires aspectjrt, not aspectjweaver. Aspect-enhanced classes need aspectjrt on the class path. If it is not, the AspectJ Compiler usually complains, either warning or even failing the build, also via AspectJ Maven. The aspectjweaver, however, is for load-time weaving. It is a superset of aspectjrt, but bigger than necessary. Also, the weaver was only test-scoped, but also during normal runtime aspectjrt is necessary. Without it, it can only work by the lucky chance that Spring already depends on aspectjweaver, which is not good dependency management. Each Maven module should be self-consistent. Aspectjrt was a plugin dependency for AJ Maven, which is also superfluous, because aspectjtools already is a superset of aspectjweaver, i.e. it also contains aspectjrt. Hence, aspectjtools is all AJ Maven needs, but the compiled application is who really needs aspectjrt. See #3282 --- spring-data-jpa/pom.xml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index ea9590bf8e..b39293042a 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -81,9 +81,8 @@ org.aspectj - aspectjweaver + aspectjrt ${aspectj} - test @@ -362,11 +361,6 @@ aspectj-maven-plugin 1.14.0 - - org.aspectj - aspectjrt - ${aspectj} - org.aspectj aspectjtools From 4b1ea8c5dfe0a962660f0c3d29956d341ca17f68 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Thu, 21 Dec 2023 08:40:39 +0700 Subject: [PATCH 514/821] Get rid of 'forceAjcCompile' workaround and special includes. by separation of concerns: Let - Maven Compiler do annotation processing without compilation and - AspectJ Maven compilation of all Java sources and aspects without annotation processing. Actually, we could let AJ Maven do all the work, but it would be difficult to configure everything correctly in JDK 9+, because AJ Maven is incomplete regarding automatically putting everything on the right module paths. so, this separation of concerns saves tedious configuration work. Relates to https://github.com/mojohaus/aspectj-maven-plugin/issues/15. See #3282 --- spring-data-jpa/pom.xml | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index b39293042a..6889823431 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -356,6 +356,13 @@ + + maven-compiler-plugin + + only + + + org.codehaus.mojo aspectj-maven-plugin @@ -369,32 +376,37 @@ + aspectj-compile compile process-classes + + + + + aspectj-test-compile + + test-compile + + process-test-classes + + + + none + + true true - - true + true org.springframework spring-aspects - - **/domain/support/AuditingEntityListener.java - ${source.level} ${source.level} ${source.level} From 8eee9c52f9140fc86ed5e2e38f535f293e8052a2 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 27 Dec 2023 09:35:26 +0100 Subject: [PATCH 515/821] Polishing. Move AspectJ dependency from test into optional. Add annotation processor paths to compiler plugin for discovery by Gradle Enterprise. Rollback replacer/conditional antlr paths. See #3282 --- spring-data-jpa/pom.xml | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 6889823431..ae3f2a928c 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -83,6 +83,7 @@ org.aspectj aspectjrt ${aspectj} + true @@ -329,6 +330,7 @@ generate-sources true + ${project.basedir}/src/main/antlr4 @@ -344,22 +346,37 @@ replace + + ${project.build.directory}/generated-sources + + antlr4/**/*.java + + + public class=class,public interface=interface + + - - - target/generated-sources/antlr4/**/*.java - - - public class=class,public interface=interface - - + org.apache.maven.plugins maven-compiler-plugin only + + + com.querydsl + querydsl-apt + ${querydsl} + jakarta + + + org.hibernate.orm + hibernate-jpamodelgen + ${hibernate} + + @@ -381,8 +398,6 @@ compile process-classes - - aspectj-test-compile @@ -390,8 +405,6 @@ test-compile process-test-classes - - From 9c578632f229f0508d5e777327aa3c79a56d80ef Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 28 Dec 2023 10:11:37 +0100 Subject: [PATCH 516/821] Add custom ANTLR template to avoid Maven Replacer plugin. Closes #3285 --- .../v4/tool/templates/codegen/Java/Java.stg | 1006 +++++++++++++++++ spring-data-jpa/pom.xml | 23 - .../repository/query/HqlParserUnitTests.java | 35 + 3 files changed, 1041 insertions(+), 23 deletions(-) create mode 100644 org/antlr/v4/tool/templates/codegen/Java/Java.stg create mode 100644 spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlParserUnitTests.java diff --git a/org/antlr/v4/tool/templates/codegen/Java/Java.stg b/org/antlr/v4/tool/templates/codegen/Java/Java.stg new file mode 100644 index 0000000000..fc455cfa1d --- /dev/null +++ b/org/antlr/v4/tool/templates/codegen/Java/Java.stg @@ -0,0 +1,1006 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012-2016 Terence Parr + * Copyright (c) 2012-2016 Sam Harwell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +javaTypeInitMap ::= [ + "int":"0", + "long":"0", + "float":"0.0f", + "double":"0.0", + "boolean":"false", + "byte":"0", + "short":"0", + "char":"0", + default:"null" // anything other than a primitive type is an object +] + +// args must be , + +ParserFile(file, parser, namedActions, contextSuperClass) ::= << + + +package ; + + +import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.misc.*; +import org.antlr.v4.runtime.tree.*; +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + + +>> + +ListenerFile(file, header, namedActions) ::= << + + +package ; + +

    +import org.antlr.v4.runtime.tree.ParseTreeListener; + +/** + * This interface defines a complete listener for a parse tree produced by + * {@link }. + */ +interface Listener extends ParseTreeListener { + + * Enter a parse tree produced by the {@code \} + * labeled alternative in {@link #\}. + + * Enter a parse tree produced by {@link #\}. + + * @param ctx the parse tree + */ +void enter(.Context ctx); +/** + + * Exit a parse tree produced by the {@code \} + * labeled alternative in {@link #\}. + + * Exit a parse tree produced by {@link #\}. + + * @param ctx the parse tree + */ +void exit(.Context ctx);}; separator="\n"> +} +>> + +BaseListenerFile(file, header, namedActions) ::= << + + +package ; + +
    + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * This class provides an empty implementation of {@link Listener}, + * which can be extended to create a listener which only needs to handle a subset + * of the available methods. + */ +@SuppressWarnings("CheckReturnValue") +class BaseListener implements Listener { + The default implementation does nothing.\

    + */ +@Override public void enter(.Context ctx) { \} +/** + * {@inheritDoc\} + * + * \

    The default implementation does nothing.\

    + */ +@Override public void exit(.Context ctx) { \}}; separator="\n"> + + /** + * {@inheritDoc\} + * + * \

    The default implementation does nothing.\

    + */ + @Override public void enterEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc\} + * + * \

    The default implementation does nothing.\

    + */ + @Override public void exitEveryRule(ParserRuleContext ctx) { } + /** + * {@inheritDoc\} + * + * \

    The default implementation does nothing.\

    + */ + @Override public void visitTerminal(TerminalNode node) { } + /** + * {@inheritDoc\} + * + * \

    The default implementation does nothing.\

    + */ + @Override public void visitErrorNode(ErrorNode node) { } +} +>> + +VisitorFile(file, header, namedActions) ::= << + + +package ; + +
    +import org.antlr.v4.runtime.tree.ParseTreeVisitor; + +/** + * This interface defines a complete generic visitor for a parse tree produced + * by {@link }. + * + * @param \ The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +interface Visitor\ extends ParseTreeVisitor\ { + + * Visit a parse tree produced by the {@code \} + * labeled alternative in {@link #\}. + + * Visit a parse tree produced by {@link #\}. + + * @param ctx the parse tree + * @return the visitor result + */ +T visit(.Context ctx);}; separator="\n"> +} +>> + +BaseVisitorFile(file, header, namedActions) ::= << + + +package ; + +
    +import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; + +/** + * This class provides an empty implementation of {@link Visitor}, + * which can be extended to create a visitor which only needs to handle a subset + * of the available methods. + * + * @param \ The return type of the visit operation. Use {@link Void} for + * operations with no return type. + */ +@SuppressWarnings("CheckReturnValue") +class BaseVisitor\ extends AbstractParseTreeVisitor\ implements Visitor\ { + The default implementation returns the result of calling + * {@link #visitChildren\} on {@code ctx\}.\

    + */ +@Override public T visit(.Context ctx) { return visitChildren(ctx); \}}; separator="\n"> +} +>> + +fileHeader(grammarFileName, ANTLRVersion) ::= << +// Generated from by ANTLR +>> + +Parser(parser, funcs, atn, sempredFuncs, superClass) ::= << + +>> + +Parser_(parser, funcs, atn, sempredFuncs, ctor, superClass) ::= << +@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) +class extends { + // Customization: Suppress version check + // static { RuntimeMetaData.checkVersion("", RuntimeMetaData.VERSION); } + + protected static final DFA[] _decisionToDFA; + protected static final PredictionContextCache _sharedContextCache = + new PredictionContextCache(); + + public static final int + =}; separator=", ", wrap, anchor>; + + + public static final int + = }; separator=", ", wrap, anchor>; + + private static String[] makeRuleNames() { + return new String[] { + "}; separator=", ", wrap, anchor> + }; + } + public static final String[] ruleNames = makeRuleNames(); + + + + @Override + public String getGrammarFileName() { return ""; } + + @Override + public String[] getRuleNames() { return ruleNames; } + + @Override + public String getSerializedATN() { return _serializedATN; } + + @Override + public ATN getATN() { return _ATN; } + + + + + + + public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { + switch (ruleIndex) { + : + return _sempred(()_localctx, predIndex);}; separator="\n"> + } + return true; + } + + + + +} +>> + +vocabulary(literalNames, symbolicNames) ::= << +private static String[] makeLiteralNames() { + return new String[] { + }; null="null", separator=", ", wrap, anchor> + }; +} +private static final String[] _LITERAL_NAMES = makeLiteralNames(); +private static String[] makeSymbolicNames() { + return new String[] { + }; null="null", separator=", ", wrap, anchor> + }; +} +private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); +public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); + +/** + * @deprecated Use {@link #VOCABULARY} instead. + */ +@Deprecated +public static final String[] tokenNames; +static { + tokenNames = new String[_SYMBOLIC_NAMES.length]; + for (int i = 0; i \< tokenNames.length; i++) { + tokenNames[i] = VOCABULARY.getLiteralName(i); + if (tokenNames[i] == null) { + tokenNames[i] = VOCABULARY.getSymbolicName(i); + } + + if (tokenNames[i] == null) { + tokenNames[i] = "\"; + } + } +} + +@Override +@Deprecated +public String[] getTokenNames() { + return tokenNames; +} + +@Override + +public Vocabulary getVocabulary() { + return VOCABULARY; +} +>> + +dumpActions(recog, argFuncs, actionFuncs, sempredFuncs) ::= << + +@Override +public void action(RuleContext _localctx, int ruleIndex, int actionIndex) { + switch (ruleIndex) { + : + _action(()_localctx, actionIndex); + break;}; separator="\n"> + } +} + + + +@Override +public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { + switch (ruleIndex) { + : + return _sempred(()_localctx, predIndex);}; separator="\n"> + } + return true; +} + + +>> + +parser_ctor(p) ::= << +public (TokenStream input) { + super(input); + _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); +} +>> + +/* This generates a private method since the actionIndex is generated, making an + * overriding implementation impossible to maintain. + */ +RuleActionFunction(r, actions) ::= << +private void _action( _localctx, int actionIndex) { + switch (actionIndex) { + : + + break;}; separator="\n"> + } +} +>> + +/* This generates a private method since the predIndex is generated, making an + * overriding implementation impossible to maintain. + */ +RuleSempredFunction(r, actions) ::= << +private boolean _sempred( _localctx, int predIndex) { + switch (predIndex) { + : + return ;}; separator="\n"> + } + return true; +} +>> + +RuleFunction(currentRule,args,code,locals,ruleCtx,altLabelCtxs,namedActions,finallyAction,postamble,exceptions) ::= << + + +}; separator="\n"> + + }>public final () throws RecognitionException { + _localctx = new (_ctx, getState()}>); + enterRule(_localctx, , RULE_); + + + try { + + int _alt; + + + + + } + + + + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + + finally { + + exitRule(); + } + return _localctx; +} +>> + +LeftRecursiveRuleFunction(currentRule,args,code,locals,ruleCtx,altLabelCtxs, + namedActions,finallyAction,postamble) ::= +<< + + +}; separator="\n"> + + }>public final () throws RecognitionException { + return (0}>); +} + +private (int _p}>) throws RecognitionException { + ParserRuleContext _parentctx = _ctx; + int _parentState = getState(); + _localctx = new (_ctx, _parentState}>); + _prevctx = _localctx; + int _startState = ; + enterRecursionRule(_localctx, , RULE_, _p); + + + try { + + int _alt; + + + + + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + + unrollRecursionContexts(_parentctx); + } + return _localctx; +} +>> + +CodeBlockForOuterMostAlt(currentOuterMostAltCodeBlock, locals, preamble, ops) ::= << +_localctx = new Context(_localctx); +enterOuterAlt(_localctx, ); + +>> + +CodeBlockForAlt(currentAltCodeBlock, locals, preamble, ops) ::= << +{ + + + +} +>> + +LL1AltBlock(choice, preamble, alts, error) ::= << +setState(); +_errHandler.sync(this); + = _input.LT(1); + +switch (_input.LA(1)) { + + + break;}; separator="\n"> +default: + +} +>> + +LL1OptionalBlock(choice, alts, error) ::= << +setState(); +_errHandler.sync(this); +switch (_input.LA(1)) { + + + break;}; separator="\n"> +default: + break; +} +>> + +LL1OptionalBlockSingleAlt(choice, expr, alts, preamble, error, followExpr) ::= << +setState(); +_errHandler.sync(this); + +if () { + +} +) ) !> +>> + +LL1StarBlockSingleAlt(choice, loopExpr, alts, preamble, iteration) ::= << +setState(); +_errHandler.sync(this); + +while () { + + setState(); + _errHandler.sync(this); + +} +>> + +LL1PlusBlockSingleAlt(choice, loopExpr, alts, preamble, iteration) ::= << +setState(); +_errHandler.sync(this); + +do { + + setState(); + _errHandler.sync(this); + +} while ( ); +>> + +// LL(*) stuff + +AltBlock(choice, preamble, alts, error) ::= << +setState(); +_errHandler.sync(this); + = _input.LT(1); + +switch ( getInterpreter().adaptivePredict(_input,,_ctx) ) { +: + + break;}; separator="\n"> +} +>> + +OptionalBlock(choice, alts, error) ::= << +setState(); +_errHandler.sync(this); +switch ( getInterpreter().adaptivePredict(_input,,_ctx) ) { ++1: + + break;}; separator="\n"> +} +>> + +StarBlock(choice, alts, sync, iteration) ::= << +setState(); +_errHandler.sync(this); +_alt = getInterpreter().adaptivePredict(_input,,_ctx); +while ( _alt!= && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1+1 ) { + + + } + setState(); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,,_ctx); +} +>> + +PlusBlock(choice, alts, error) ::= << +setState(); +_errHandler.sync(this); +_alt = 1+1; +do { + switch (_alt) { + +1: + + break;}; separator="\n"> + default: + + } + setState(); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,,_ctx); +} while ( _alt!= && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); +>> + +Sync(s) ::= "sync();" + +ThrowNoViableAlt(t) ::= "throw new NoViableAltException(this);" + +TestSetInline(s) ::= << +}; separator=" || "> +>> + +// Java language spec 15.19 - shift operators mask operands rather than overflow to 0... need range test +testShiftInRange(shiftAmount) ::= << +(() & ~0x3f) == 0 +>> + +bitsetBitfieldComparison(s, bits) ::= <% +(})> && ((1L \<\< ) & L) != 0) +%> + +isZero ::= [ +"0":true, +default:false +] + +offsetShift(shiftAmount, offset) ::= <% +( - ) +%> + +bitsetInlineComparison(s, bits) ::= <% +==}; separator=" || "> +%> + +cases(tokens) ::= << +:}; separator="\n"> +>> + +InvokeRule(r, argExprsChunks) ::= << +setState(); + = }>(,); +>> + +MatchToken(m) ::= << +setState(); + = }>match(); +>> + +MatchSet(m, expr, capture) ::= "" + +MatchNotSet(m, expr, capture) ::= "" + +CommonSetStuff(m, expr, capture, invert) ::= << +setState(); + = }>_input.LT(1); + +if ( \<= 0 || !() ) { + = (Token)}>_errHandler.recoverInline(this); +} +else { + if ( _input.LA(1)==Token.EOF ) matchedEOF = true; + _errHandler.reportMatch(this); + consume(); +} +>> + +Wildcard(w) ::= << +setState(); + = }>matchWildcard(); +>> + +// ACTION STUFF + +Action(a, foo, chunks) ::= "" + +ArgAction(a, chunks) ::= "" + +SemPred(p, chunks, failChunks) ::= << +setState(); +if (!()) throw new FailedPredicateException(this, , , ); +>> + +ExceptionClause(e, catchArg, catchAction) ::= << +catch () { + +} +>> + +// lexer actions are not associated with model objects + +LexerSkipCommand() ::= "skip();" +LexerMoreCommand() ::= "more();" +LexerPopModeCommand() ::= "popMode();" + +LexerTypeCommand(arg, grammar) ::= "_type = ;" +LexerChannelCommand(arg, grammar) ::= "_channel = ;" +LexerModeCommand(arg, grammar) ::= "_mode = ;" +LexerPushModeCommand(arg, grammar) ::= "pushMode();" + +ActionText(t) ::= "" +ActionTemplate(t) ::= "" +ArgRef(a) ::= "_localctx." +LocalRef(a) ::= "_localctx." +RetValueRef(a) ::= "_localctx." +QRetValueRef(a) ::= ".." +/** How to translate $tokenLabel */ +TokenRef(t) ::= "." +LabelRef(t) ::= "." +ListLabelRef(t) ::= "." +SetAttr(s,rhsChunks) ::= ". = ;" + +TokenLabelType() ::= "" +InputSymbolType() ::= "" + +TokenPropertyRef_text(t) ::= "(.!=null?..getText():null)" +TokenPropertyRef_type(t) ::= "(.!=null?..getType():0)" +TokenPropertyRef_line(t) ::= "(.!=null?..getLine():0)" +TokenPropertyRef_pos(t) ::= "(.!=null?..getCharPositionInLine():0)" +TokenPropertyRef_channel(t) ::= "(.!=null?..getChannel():0)" +TokenPropertyRef_index(t) ::= "(.!=null?..getTokenIndex():0)" +TokenPropertyRef_int(t) ::= "(.!=null?Integer.valueOf(..getText()):0)" + +RulePropertyRef_start(r) ::= "(.!=null?(..start):null)" +RulePropertyRef_stop(r) ::= "(.!=null?(..stop):null)" +RulePropertyRef_text(r) ::= "(.!=null?_input.getText(..start,..stop):null)" +RulePropertyRef_ctx(r) ::= "." +RulePropertyRef_parser(r) ::= "this" + +ThisRulePropertyRef_start(r) ::= "_localctx.start" +ThisRulePropertyRef_stop(r) ::= "_localctx.stop" +ThisRulePropertyRef_text(r) ::= "_input.getText(_localctx.start, _input.LT(-1))" +ThisRulePropertyRef_ctx(r) ::= "_localctx" +ThisRulePropertyRef_parser(r) ::= "this" + +NonLocalAttrRef(s) ::= "((Context)getInvokingContext())." +SetNonLocalAttr(s, rhsChunks) ::= + "((Context)getInvokingContext()). = ;" + +AddToLabelList(a) ::= "..add();" + +TokenDecl(t) ::= " " +TokenTypeDecl(t) ::= "int ;" +TokenListDecl(t) ::= "List\ = new ArrayList\()" +RuleContextDecl(r) ::= " " +RuleContextListDecl(rdecl) ::= "List\<> = new ArrayList\<>()" + +ContextTokenGetterDecl(t) ::= + "public TerminalNode () { return getToken(., 0); }" +ContextTokenListGetterDecl(t) ::= + "public List\ () { return getTokens(.); }" +ContextTokenListIndexedGetterDecl(t) ::= << +public TerminalNode (int i) { + return getToken(., i); +} +>> +ContextRuleGetterDecl(r) ::= << +public () { + return getRuleContext(.class,0); +} +>> +ContextRuleListGetterDecl(r) ::= << +public List\<\> () { + return getRuleContexts(.class); +} +>> +ContextRuleListIndexedGetterDecl(r) ::= << +public (int i) { + return getRuleContext(.class,i); +} +>> + +LexerRuleContext() ::= "RuleContext" + +/** The rule context name is the rule followed by a suffix; e.g., + * r becomes rContext. + */ +RuleContextNameSuffix() ::= "Context" + +ImplicitTokenLabel(tokenName) ::= "" +ImplicitRuleLabel(ruleName) ::= "" +ImplicitSetLabel(id) ::= "_tset" +ListLabelName(label) ::= "