Skip to content

Commit 37ceda1

Browse files
authored
Merge pull request #15 from consoleau/migration-spring-boot-2.2
Update to Spring Boot 2
2 parents 89233c8 + ea0dd73 commit 37ceda1

File tree

11 files changed

+143
-98
lines changed

11 files changed

+143
-98
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
language: java
22
sudo: true
3-
jdk: oraclejdk8
3+
jdk: openjdk8
44
script: "./gradle/travisBuild.sh"
55
before_cache:
66
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ repositories {
1515
}
1616
1717
dependencies {
18-
compile("au.com.console:kotlin-jpa-specification-dsl:0.1.0")
18+
compile("au.com.console:kotlin-jpa-specification-dsl:2.0.0")
1919
}
2020
```
2121

@@ -73,23 +73,23 @@ For more complex and dynamic queries it's good practice to create functions that
7373
and to allow for their composition in complex dynamic queries.
7474

7575
```kotlin
76-
fun hasName(name: String?): Specifications<TvShow>? = name?.let {
76+
fun hasName(name: String?): Specification<TvShow>? = name?.let {
7777
TvShow::name.equal(it)
7878
}
7979

80-
fun availableOnNetflix(available: Boolean?): Specifications<TvShow>? = available?.let {
80+
fun availableOnNetflix(available: Boolean?): Specification<TvShow>? = available?.let {
8181
TvShow::availableOnNetflix.equal(it)
8282
}
8383

84-
fun hasReleaseDateIn(releaseDates: List<String>?): Specifications<TvShow>? = releaseDates?.let {
84+
fun hasReleaseDateIn(releaseDates: List<String>?): Specification<TvShow>? = releaseDates?.let {
8585
TvShow::releaseDate.`in`(releaseDates)
8686
}
8787

88-
fun hasKeywordIn(keywords: List<String>?): Specifications<TvShow>? = keywords?.let {
88+
fun hasKeywordIn(keywords: List<String>?): Specification<TvShow>? = keywords?.let {
8989
or(keywords.map(::hasKeyword))
9090
}
9191

92-
fun hasKeyword(keyword: String?): Specifications<TvShow>? = keyword?.let {
92+
fun hasKeyword(keyword: String?): Specification<TvShow>? = keyword?.let {
9393
TvShow::synopsis.like("%$keyword%")
9494
}
9595
```
@@ -131,7 +131,7 @@ Or they can be combined with a service-layer query DTO and mapping extension fun
131131
* A single TvShowQuery is equivalent to an AND of all supplied criteria.
132132
* Note: any criteria that is null will be ignored (not included in the query).
133133
*/
134-
fun TvShowQuery.toSpecification(): Specifications<TvShow> = and(
134+
fun TvShowQuery.toSpecification(): Specification<TvShow> = and(
135135
hasName(name),
136136
availableOnNetflix(availableOnNetflix),
137137
hasKeywordIn(keywords),
@@ -155,7 +155,7 @@ This DSL builds on [Spring Data's Specifications abstraction](http://docs.spring
155155
The code `TvShow::releaseDate.equal("2010")` is a call to the Kotlin extension function:
156156

157157
```kotlin
158-
fun <T, R> KProperty1<T, R?>.equal(x: R): Specifications<T> = spec { equal(it, x) }
158+
fun <T, R> KProperty1<T, R?>.equal(x: R): Specification<T> = spec { equal(it, x) }
159159
```
160160

161161
This is a bit dense, but makes sense when it's broken down:
@@ -164,12 +164,12 @@ This is a bit dense, but makes sense when it's broken down:
164164
- `R`: The property type, for TvShow::releaseDate it is String
165165
- `KProperty1<T,R?>`: Kotlin reflection API representation of the property `TvShow::releaseDate`. The 1 refers to a property with 1 receiver, and `R?` is declared as nullable for the method to work on nullable properties as well as non-null properties.
166166
- `x`: The value to test against
167-
- `Specifications<T>`: The Spring data specifications result
167+
- `Specification<T>`: The Spring data specifications result
168168

169169
This is implemented using a private helper function `spec` that captures the common use case of taking an Entity property, and using a `CriteriaBuilder` to create a `Predicate`:
170170

171171
```kotlin
172-
private fun <T, R> KProperty1<T, R?>.spec(makePredicate: CriteriaBuilder.(path: Path<R>) -> Predicate): Specifications<T> =
172+
private fun <T, R> KProperty1<T, R?>.spec(makePredicate: CriteriaBuilder.(path: Path<R>) -> Predicate): Specification<T> =
173173
this.let { property -> where { root -> makePredicate(root.get(property)) } }
174174
```
175175

build.gradle

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
12
plugins {
2-
id 'nebula.kotlin' version '1.2.21'
3-
id 'nebula.project' version '3.4.0'
4-
id 'nebula.release' version '6.3.0'
5-
id 'nebula.maven-publish' version '7.0.1'
6-
id 'nebula.nebula-bintray' version '3.5.2'
3+
id 'nebula.project' version '7.0.5'
4+
id 'nebula.release' version '14.0.2'
5+
id 'nebula.kotlin' version '1.3.61'
6+
id 'nebula.maven-publish' version '14.1.1'
7+
id 'com.jfrog.bintray' version '1.8.4'
78
}
89

910
group 'au.com.console'
@@ -16,24 +17,30 @@ sourceCompatibility = JavaVersion.VERSION_1_8
1617
targetCompatibility = JavaVersion.VERSION_1_8
1718

1819
dependencies {
19-
compile 'org.springframework.data:spring-data-jpa:1.10.2.RELEASE'
20-
compileOnly 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
21-
testCompile 'org.springframework.boot:spring-boot-starter-data-jpa:1.3.5.RELEASE'
22-
testCompile 'com.h2database:h2:1.4.191'
23-
testCompile 'org.springframework.boot:spring-boot-starter-test:1.3.5.RELEASE'
24-
testCompile 'junit:junit:4.12'
20+
api 'org.jetbrains.kotlin:kotlin-reflect'
21+
api 'org.springframework.data:spring-data-jpa:2.2.4.RELEASE'
22+
compileOnly 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final'
23+
testApi 'org.springframework.boot:spring-boot-starter-data-jpa:2.2.2.RELEASE'
24+
testApi 'com.h2database:h2:1.4.191'
25+
testApi 'org.springframework.boot:spring-boot-starter-test:2.2.4.RELEASE'
26+
testApi 'org.junit.jupiter:junit-jupiter-api:5.5.2'
27+
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
28+
}
29+
30+
test {
31+
useJUnitPlatform()
32+
testLogging {
33+
events("passed", "skipped", "failed")
34+
setExceptionFormat("full")
35+
}
2536
}
2637

2738
tasks.bintrayUpload.dependsOn tasks.check
28-
tasks.artifactoryPublish.dependsOn tasks.check
2939

3040
gradle.taskGraph.whenReady { graph ->
3141
tasks.bintrayUpload.onlyIf {
3242
graph.hasTask(':final') || graph.hasTask(':candidate')
3343
}
34-
tasks.artifactoryPublish.onlyIf {
35-
graph.hasTask(':snapshot') || graph.hasTask(':devSnapshot')
36-
}
3744
}
3845

3946
bintray {
@@ -52,8 +59,4 @@ bintray {
5259
issueTrackerUrl = "https://github.com/consoleau/${project.name}/issues"
5360
vcsUrl = "https://github.com/consoleau/${project.name}.git"
5461
}
55-
}
56-
57-
task wrapper(type: Wrapper) {
58-
gradleVersion = "4.5.1"
59-
}
62+
}

gradle/travisBuild.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# This script will build the project.
33
# Based on https://github.com/nebula-plugins/nebula-kotlin-plugin/blob/master/gradle/buildViaTravis.sh
44

5-
SWITCHES="-x artifactoryPublish --info --stacktrace"
5+
SWITCHES="--info --stacktrace"
66

77
GRADLE_VERSION=$(./gradlew -version | grep Gradle | cut -d ' ' -f 2)
88

gradle/wrapper/gradle-wrapper.jar

2.73 KB
Binary file not shown.
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
#Tue Feb 20 13:21:37 AEST 2018
21
distributionBase=GRADLE_USER_HOME
32
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip

gradlew

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
#!/usr/bin/env bash
1+
#!/usr/bin/env sh
2+
3+
#
4+
# Copyright 2015 the original author or authors.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# https://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
218

319
##############################################################################
420
##
@@ -28,16 +44,16 @@ APP_NAME="Gradle"
2844
APP_BASE_NAME=`basename "$0"`
2945

3046
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31-
DEFAULT_JVM_OPTS=""
47+
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
3248

3349
# Use the maximum available, or set MAX_FD != -1 to use that value.
3450
MAX_FD="maximum"
3551

36-
warn ( ) {
52+
warn () {
3753
echo "$*"
3854
}
3955

40-
die ( ) {
56+
die () {
4157
echo
4258
echo "$*"
4359
echo
@@ -109,8 +125,8 @@ if $darwin; then
109125
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110126
fi
111127

112-
# For Cygwin, switch paths to Windows format before running java
113-
if $cygwin ; then
128+
# For Cygwin or MSYS, switch paths to Windows format before running java
129+
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
114130
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115131
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116132
JAVACMD=`cygpath --unix "$JAVACMD"`
@@ -154,16 +170,19 @@ if $cygwin ; then
154170
esac
155171
fi
156172

157-
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158-
function splitJvmOpts() {
159-
JVM_OPTS=("$@")
173+
# Escape application args
174+
save () {
175+
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176+
echo " "
160177
}
161-
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162-
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
178+
APP_ARGS=$(save "$@")
179+
180+
# Collect all arguments for the java command, following the shell quoting and substitution rules
181+
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
163182

164183
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
165-
if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
184+
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
166185
cd "$(dirname "$0")"
167186
fi
168187

169-
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
188+
exec "$JAVACMD" "$@"

gradlew.bat

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
@rem
2+
@rem Copyright 2015 the original author or authors.
3+
@rem
4+
@rem Licensed under the Apache License, Version 2.0 (the "License");
5+
@rem you may not use this file except in compliance with the License.
6+
@rem You may obtain a copy of the License at
7+
@rem
8+
@rem https://www.apache.org/licenses/LICENSE-2.0
9+
@rem
10+
@rem Unless required by applicable law or agreed to in writing, software
11+
@rem distributed under the License is distributed on an "AS IS" BASIS,
12+
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
@rem See the License for the specific language governing permissions and
14+
@rem limitations under the License.
15+
@rem
16+
117
@if "%DEBUG%" == "" @echo off
218
@rem ##########################################################################
319
@rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
1430
set APP_HOME=%DIRNAME%
1531

1632
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17-
set DEFAULT_JVM_OPTS=
33+
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
1834

1935
@rem Find java.exe
2036
if defined JAVA_HOME goto findJavaFromJavaHome
Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package au.com.console.jpaspecificationdsl
22

33
import org.springframework.data.jpa.domain.Specification
4-
import org.springframework.data.jpa.domain.Specifications
54
import javax.persistence.criteria.*
65
import kotlin.reflect.KProperty1
76

@@ -11,25 +10,27 @@ fun <Z, T, R> From<Z, T>.join(prop: KProperty1<T, R?>): Join<T, R> = this.join<T
1110
// Helper to enable get by Property
1211
fun <R> Path<*>.get(prop: KProperty1<*, R?>): Path<R> = this.get<R>(prop.name)
1312

14-
// Version of Specifications.where that makes the CriteriaBuilder implicit
15-
fun <T> where(makePredicate: CriteriaBuilder.(Root<T>) -> Predicate): Specifications<T> =
16-
Specifications.where<T> { root, _, criteriaBuilder -> criteriaBuilder.makePredicate(root) }
13+
// Version of Specification.where that makes the CriteriaBuilder implicit
14+
fun <T> where(makePredicate: CriteriaBuilder.(Root<T>) -> Predicate): Specification<T> =
15+
Specification { root, _, criteriaBuilder -> criteriaBuilder.makePredicate(root) }
1716

18-
// helper function for defining Specifications that take a Path to a property and send it to a CriteriaBuilder
19-
private fun <T, R> KProperty1<T, R?>.spec(makePredicate: CriteriaBuilder.(path: Path<R>) -> Predicate): Specifications<T> =
20-
this.let { property -> where { root -> makePredicate(root.get(property)) } }
17+
// helper function for defining Specification that take a Path to a property and send it to a CriteriaBuilder
18+
private fun <T, R> KProperty1<T, R?>.spec(makePredicate: CriteriaBuilder.(path: Path<R>) -> Predicate): Specification<T> =
19+
let { property -> where { root -> makePredicate(root.get(property)) } }
2120

2221
// Equality
23-
fun <T, R> KProperty1<T, R?>.equal(x: R): Specifications<T> = spec { equal(it, x) }
24-
fun <T, R> KProperty1<T, R?>.notEqual(x: R): Specifications<T> = spec { notEqual(it, x) }
22+
fun <T, R> KProperty1<T, R?>.equal(x: R): Specification<T> = spec { equal(it, x) }
23+
24+
fun <T, R> KProperty1<T, R?>.notEqual(x: R): Specification<T> = spec { notEqual(it, x) }
2525

2626
// Ignores empty collection otherwise an empty 'in' predicate will be generated which will never match any results
27-
fun <T, R: Any> KProperty1<T, R?>.`in`(values: Collection<R>): Specifications<T> = if (values.isNotEmpty()) spec { path ->
27+
fun <T, R : Any> KProperty1<T, R?>.`in`(values: Collection<R>): Specification<T> = if (values.isNotEmpty()) spec { path ->
2828
`in`(path).apply { values.forEach { this.value(it) } }
29-
} else Specifications.where<T>(null)
29+
} else Specification.where(null)!!
3030

3131
// Comparison
3232
fun <T> KProperty1<T, Number?>.le(x: Number) = spec { le(it, x) }
33+
3334
fun <T> KProperty1<T, Number?>.lt(x: Number) = spec { lt(it, x) }
3435
fun <T> KProperty1<T, Number?>.ge(x: Number) = spec { ge(it, x) }
3536
fun <T> KProperty1<T, Number?>.gt(x: Number) = spec { gt(it, x) }
@@ -41,49 +42,58 @@ fun <T, R : Comparable<R>> KProperty1<T, R?>.between(x: R, y: R) = spec { betwee
4142

4243
// True/False
4344
fun <T> KProperty1<T, Boolean?>.isTrue() = spec { isTrue(it) }
45+
4446
fun <T> KProperty1<T, Boolean?>.isFalse() = spec { isFalse(it) }
4547

4648
// Null / NotNull
4749
fun <T, R> KProperty1<T, R?>.isNull() = spec { isNull(it) }
50+
4851
fun <T, R> KProperty1<T, R?>.isNotNull() = spec { isNotNull(it) }
4952

5053
// Collections
5154
fun <T, R : Collection<*>> KProperty1<T, R?>.isEmpty() = spec { isEmpty(it) }
55+
5256
fun <T, R : Collection<*>> KProperty1<T, R?>.isNotEmpty() = spec { isNotEmpty(it) }
5357
fun <T, E, R : Collection<E>> KProperty1<T, R?>.isMember(elem: E) = spec { isMember(elem, it) }
5458
fun <T, E, R : Collection<E>> KProperty1<T, R?>.isNotMember(elem: E) = spec { isNotMember(elem, it) }
5559

5660
// Strings
57-
fun <T> KProperty1<T, String?>.like(x: String): Specifications<T> = spec { like(it, x) }
58-
fun <T> KProperty1<T, String?>.like(x: String, escapeChar: Char): Specifications<T> = spec { like(it, x, escapeChar) }
59-
fun <T> KProperty1<T, String?>.notLike(x: String): Specifications<T> = spec { notLike(it, x) }
60-
fun <T> KProperty1<T, String?>.notLike(x: String, escapeChar: Char): Specifications<T> = spec { notLike(it, x, escapeChar) }
61+
fun <T> KProperty1<T, String?>.like(x: String): Specification<T> = spec { like(it, x) }
62+
63+
fun <T> KProperty1<T, String?>.like(x: String, escapeChar: Char): Specification<T> = spec { like(it, x, escapeChar) }
64+
fun <T> KProperty1<T, String?>.notLike(x: String): Specification<T> = spec { notLike(it, x) }
65+
fun <T> KProperty1<T, String?>.notLike(x: String, escapeChar: Char): Specification<T> = spec { notLike(it, x, escapeChar) }
6166

6267
// And
63-
infix fun <T> Specifications<T>.and(other: Specification<T>): Specifications<T> = this.and(other)
64-
inline fun <reified T> and(vararg specs: Specifications<T>?): Specifications<T> {
68+
infix fun <T> Specification<T>.and(other: Specification<T>): Specification<T> = this.and(other)!!
69+
70+
inline fun <reified T> and(vararg specs: Specification<T>?): Specification<T> {
6571
return and(specs.toList())
6672
}
67-
inline fun <reified T> and(specs: Iterable<Specifications<T>?>): Specifications<T> {
68-
return combineSpecifications(specs, Specifications<T>::and)
73+
74+
inline fun <reified T> and(specs: Iterable<Specification<T>?>): Specification<T> {
75+
return combineSpecification(specs, Specification<T>::and)
6976
}
7077

7178
// Or
72-
infix fun <T> Specifications<T>.or(other: Specification<T>) : Specifications<T> = this.or(other)
73-
inline fun <reified T> or(vararg specs: Specifications<T>?): Specifications<T> {
79+
infix fun <T> Specification<T>.or(other: Specification<T>): Specification<T> = this.or(other)!!
80+
81+
inline fun <reified T> or(vararg specs: Specification<T>?): Specification<T> {
7482
return or(specs.toList())
7583
}
76-
inline fun <reified T> or(specs: Iterable<Specifications<T>?>): Specifications<T> {
77-
return combineSpecifications(specs, Specifications<T>::or)
84+
85+
inline fun <reified T> or(specs: Iterable<Specification<T>?>): Specification<T> {
86+
return combineSpecification(specs, Specification<T>::or)
7887
}
7988

8089
// Not
81-
operator fun <T> Specifications<T>.not(): Specifications<T> = Specifications.not(this)
90+
operator fun <T> Specification<T>.not(): Specification<T> = Specification.not(this)
8291

83-
// Combines Specifications with an operation
84-
inline fun <reified T> combineSpecifications(specs: Iterable<Specification<T>?>, operation: Specifications<T>.(Specification<T>) -> Specifications<T>): Specifications<T> {
92+
// Combines Specification with an operation
93+
inline fun <reified T> combineSpecification(specs: Iterable<Specification<T>?>,
94+
operation: Specification<T>.(Specification<T>) -> Specification<T>): Specification<T> {
8595
return specs.filterNotNull().fold(emptySpecification()) { existing, new -> existing.operation(new) }
8696
}
8797

8898
// Empty Specification
89-
inline fun <reified T> emptySpecification(): Specifications<T> = Specifications.where<T>(null)
99+
inline fun <reified T> emptySpecification(): Specification<T> = Specification.where(null)!!

0 commit comments

Comments
 (0)