├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── .mvn
└── wrapper
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── .sdkmanrc
├── LICENSE
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── spring-boot-flyway-tutorial
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sivalabs
│ │ │ └── bookmarks
│ │ │ ├── Application.java
│ │ │ └── domain
│ │ │ ├── Bookmark.java
│ │ │ └── BookmarkRepository.java
│ └── resources
│ │ ├── application.properties
│ │ └── db
│ │ └── migration
│ │ ├── V1__create_tables.sql
│ │ └── V2__create_bookmarks_indexes.sql
│ └── test
│ ├── java
│ └── com
│ │ └── sivalabs
│ │ └── bookmarks
│ │ ├── TestApplication.java
│ │ └── domain
│ │ └── BookmarkRepositoryTest.java
│ └── resources
│ └── test-data.sql
├── spring-boot-helloworld
├── .gitignore
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── README.md
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── mvnw
├── mvnw.cmd
├── pom.xml
├── settings.gradle
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sivalabs
│ │ │ └── helloworld
│ │ │ ├── ApplicationProperties.java
│ │ │ ├── GreetingResponse.java
│ │ │ ├── GreetingService.java
│ │ │ ├── HelloWorldController.java
│ │ │ └── SpringBootHelloWorldApplication.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── com
│ └── sivalabs
│ └── helloworld
│ ├── GreetingServiceTest.java
│ ├── HelloWorldControllerTest.java
│ └── SpringBootHelloWorldApplicationTests.java
├── spring-boot-jdbc-tutorial
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sivalabs
│ │ │ └── bookmarks
│ │ │ ├── Application.java
│ │ │ └── domain
│ │ │ ├── Bookmark.java
│ │ │ └── BookmarkRepository.java
│ └── resources
│ │ ├── application.properties
│ │ ├── data.sql
│ │ └── schema.sql
│ └── test
│ ├── java
│ └── com
│ │ └── sivalabs
│ │ └── bookmarks
│ │ └── domain
│ │ └── BookmarkRepositoryTest.java
│ └── resources
│ └── test-data.sql
├── spring-boot-jdbcclient-tutorial
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sivalabs
│ │ │ └── bookmarks
│ │ │ ├── Application.java
│ │ │ └── domain
│ │ │ ├── Bookmark.java
│ │ │ └── BookmarkRepository.java
│ └── resources
│ │ ├── application.properties
│ │ └── db
│ │ └── migration
│ │ └── V1__create_tables.sql
│ └── test
│ ├── java
│ └── com
│ │ └── sivalabs
│ │ └── bookmarks
│ │ └── domain
│ │ └── BookmarkRepositoryTest.java
│ └── resources
│ └── test-data.sql
└── spring-boot-rest-api-tutorial
├── .gitignore
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── sivalabs
│ │ └── bookmarks
│ │ ├── Application.java
│ │ ├── api
│ │ ├── GlobalExceptionHandler.java
│ │ ├── controllers
│ │ │ └── BookmarkController.java
│ │ └── models
│ │ │ ├── CreateBookmarkRequest.java
│ │ │ └── UpdateBookmarkRequest.java
│ │ └── domain
│ │ ├── Bookmark.java
│ │ ├── BookmarkDTO.java
│ │ ├── BookmarkNotFoundException.java
│ │ ├── BookmarkRepository.java
│ │ ├── BookmarkService.java
│ │ ├── CreateBookmarkCommand.java
│ │ ├── FindBookmarksQuery.java
│ │ ├── PagedResult.java
│ │ └── UpdateBookmarkCommand.java
└── resources
│ ├── application.properties
│ └── db
│ └── migration
│ └── V1__init.sql
└── test
├── java
└── com
│ └── sivalabs
│ └── bookmarks
│ ├── ApplicationTests.java
│ ├── ContainersConfig.java
│ ├── TestApplication.java
│ └── api
│ └── controllers
│ └── BookmarkControllerTests.java
└── resources
├── logback-test.xml
└── test-data.sql
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 |
8 | jobs:
9 | build:
10 | name: Run Tests
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 |
15 | - name: Set up JDK 17
16 | uses: actions/setup-java@v3
17 | with:
18 | java-version: 17
19 | distribution: 'temurin'
20 | cache: 'maven'
21 |
22 | - name: Build with Maven
23 | run: ./mvnw verify
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 | .gradle
7 | !gradle/wrapper/gradle-wrapper.jar
8 |
9 |
10 | ### STS ###
11 | .apt_generated
12 | .classpath
13 | .factorypath
14 | .project
15 | .settings
16 | .springBeans
17 | .sts4-cache
18 |
19 | ### IntelliJ IDEA ###
20 | .idea
21 | *.iws
22 | *.iml
23 | *.ipr
24 |
25 | ### NetBeans ###
26 | /nbproject/private/
27 | /nbbuild/
28 | /dist/
29 | /nbdist/
30 | /.nb-gradle/
31 | build/
32 | !**/src/main/**/build/
33 | !**/src/test/**/build/
34 |
35 | ### VS Code ###
36 | .vscode/
37 |
--------------------------------------------------------------------------------
/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2007-present the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import java.net.*;
18 | import java.io.*;
19 | import java.nio.channels.*;
20 | import java.util.Properties;
21 |
22 | public class MavenWrapperDownloader {
23 |
24 | private static final String WRAPPER_VERSION = "0.5.6";
25 | /**
26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
27 | */
28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
30 |
31 | /**
32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
33 | * use instead of the default one.
34 | */
35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
36 | ".mvn/wrapper/maven-wrapper.properties";
37 |
38 | /**
39 | * Path where the maven-wrapper.jar will be saved to.
40 | */
41 | private static final String MAVEN_WRAPPER_JAR_PATH =
42 | ".mvn/wrapper/maven-wrapper.jar";
43 |
44 | /**
45 | * Name of the property which should be used to override the default download url for the wrapper.
46 | */
47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
48 |
49 | public static void main(String args[]) {
50 | System.out.println("- Downloader started");
51 | File baseDirectory = new File(args[0]);
52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
53 |
54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom
55 | // wrapperUrl parameter.
56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
57 | String url = DEFAULT_DOWNLOAD_URL;
58 | if (mavenWrapperPropertyFile.exists()) {
59 | FileInputStream mavenWrapperPropertyFileInputStream = null;
60 | try {
61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
62 | Properties mavenWrapperProperties = new Properties();
63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
65 | } catch (IOException e) {
66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
67 | } finally {
68 | try {
69 | if (mavenWrapperPropertyFileInputStream != null) {
70 | mavenWrapperPropertyFileInputStream.close();
71 | }
72 | } catch (IOException e) {
73 | // Ignore ...
74 | }
75 | }
76 | }
77 | System.out.println("- Downloading from: " + url);
78 |
79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
80 | if (!outputFile.getParentFile().exists()) {
81 | if (!outputFile.getParentFile().mkdirs()) {
82 | System.out.println(
83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
84 | }
85 | }
86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
87 | try {
88 | downloadFileFromURL(url, outputFile);
89 | System.out.println("Done");
90 | System.exit(0);
91 | } catch (Throwable e) {
92 | System.out.println("- Error downloading");
93 | e.printStackTrace();
94 | System.exit(1);
95 | }
96 | }
97 |
98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception {
99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
100 | String username = System.getenv("MVNW_USERNAME");
101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
102 | Authenticator.setDefault(new Authenticator() {
103 | @Override
104 | protected PasswordAuthentication getPasswordAuthentication() {
105 | return new PasswordAuthentication(username, password);
106 | }
107 | });
108 | }
109 | URL website = new URL(urlString);
110 | ReadableByteChannel rbc;
111 | rbc = Channels.newChannel(website.openStream());
112 | FileOutputStream fos = new FileOutputStream(destination);
113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
114 | fos.close();
115 | rbc.close();
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-tutorials-blog-series/4e04cc2e628e0728234b949f7b341bad829e6be9/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/.sdkmanrc:
--------------------------------------------------------------------------------
1 | java=17.0.4-tem
2 | gradle=8.0.2
3 | maven=3.9.0
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 K. Siva Prasad Reddy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # spring-boot-tutorials-blog-series
2 | Code for [Spring Boot Tutorials Blog](https://www.sivalabs.in/getting-started-with-spring-boot/) Series
3 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # https://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Mingw, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | fi
118 |
119 | if [ -z "$JAVA_HOME" ]; then
120 | javaExecutable="`which javac`"
121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
122 | # readlink(1) is not available as standard on Solaris 10.
123 | readLink=`which readlink`
124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
125 | if $darwin ; then
126 | javaHome="`dirname \"$javaExecutable\"`"
127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
128 | else
129 | javaExecutable="`readlink -f \"$javaExecutable\"`"
130 | fi
131 | javaHome="`dirname \"$javaExecutable\"`"
132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
133 | JAVA_HOME="$javaHome"
134 | export JAVA_HOME
135 | fi
136 | fi
137 | fi
138 |
139 | if [ -z "$JAVACMD" ] ; then
140 | if [ -n "$JAVA_HOME" ] ; then
141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
142 | # IBM's JDK on AIX uses strange locations for the executables
143 | JAVACMD="$JAVA_HOME/jre/sh/java"
144 | else
145 | JAVACMD="$JAVA_HOME/bin/java"
146 | fi
147 | else
148 | JAVACMD="`which java`"
149 | fi
150 | fi
151 |
152 | if [ ! -x "$JAVACMD" ] ; then
153 | echo "Error: JAVA_HOME is not defined correctly." >&2
154 | echo " We cannot execute $JAVACMD" >&2
155 | exit 1
156 | fi
157 |
158 | if [ -z "$JAVA_HOME" ] ; then
159 | echo "Warning: JAVA_HOME environment variable is not set."
160 | fi
161 |
162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
163 |
164 | # traverses directory structure from process work directory to filesystem root
165 | # first directory with .mvn subdirectory is considered project base directory
166 | find_maven_basedir() {
167 |
168 | if [ -z "$1" ]
169 | then
170 | echo "Path not specified to find_maven_basedir"
171 | return 1
172 | fi
173 |
174 | basedir="$1"
175 | wdir="$1"
176 | while [ "$wdir" != '/' ] ; do
177 | if [ -d "$wdir"/.mvn ] ; then
178 | basedir=$wdir
179 | break
180 | fi
181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
182 | if [ -d "${wdir}" ]; then
183 | wdir=`cd "$wdir/.."; pwd`
184 | fi
185 | # end of workaround
186 | done
187 | echo "${basedir}"
188 | }
189 |
190 | # concatenates all lines of a file
191 | concat_lines() {
192 | if [ -f "$1" ]; then
193 | echo "$(tr -s '\n' ' ' < "$1")"
194 | fi
195 | }
196 |
197 | BASE_DIR=`find_maven_basedir "$(pwd)"`
198 | if [ -z "$BASE_DIR" ]; then
199 | exit 1;
200 | fi
201 |
202 | ##########################################################################################
203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
204 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
205 | ##########################################################################################
206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
207 | if [ "$MVNW_VERBOSE" = true ]; then
208 | echo "Found .mvn/wrapper/maven-wrapper.jar"
209 | fi
210 | else
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
213 | fi
214 | if [ -n "$MVNW_REPOURL" ]; then
215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
216 | else
217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
218 | fi
219 | while IFS="=" read key value; do
220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
221 | esac
222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
223 | if [ "$MVNW_VERBOSE" = true ]; then
224 | echo "Downloading from: $jarUrl"
225 | fi
226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
227 | if $cygwin; then
228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
229 | fi
230 |
231 | if command -v wget > /dev/null; then
232 | if [ "$MVNW_VERBOSE" = true ]; then
233 | echo "Found wget ... using wget"
234 | fi
235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
236 | wget "$jarUrl" -O "$wrapperJarPath"
237 | else
238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
239 | fi
240 | elif command -v curl > /dev/null; then
241 | if [ "$MVNW_VERBOSE" = true ]; then
242 | echo "Found curl ... using curl"
243 | fi
244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
245 | curl -o "$wrapperJarPath" "$jarUrl" -f
246 | else
247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
248 | fi
249 |
250 | else
251 | if [ "$MVNW_VERBOSE" = true ]; then
252 | echo "Falling back to using Java to download"
253 | fi
254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
255 | # For Cygwin, switch paths to Windows format before running javac
256 | if $cygwin; then
257 | javaClass=`cygpath --path --windows "$javaClass"`
258 | fi
259 | if [ -e "$javaClass" ]; then
260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
261 | if [ "$MVNW_VERBOSE" = true ]; then
262 | echo " - Compiling MavenWrapperDownloader.java ..."
263 | fi
264 | # Compiling the Java class
265 | ("$JAVA_HOME/bin/javac" "$javaClass")
266 | fi
267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
268 | # Running the downloader
269 | if [ "$MVNW_VERBOSE" = true ]; then
270 | echo " - Running MavenWrapperDownloader.java ..."
271 | fi
272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
273 | fi
274 | fi
275 | fi
276 | fi
277 | ##########################################################################################
278 | # End of extension
279 | ##########################################################################################
280 |
281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
282 | if [ "$MVNW_VERBOSE" = true ]; then
283 | echo $MAVEN_PROJECTBASEDIR
284 | fi
285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
286 |
287 | # For Cygwin, switch paths to Windows format before running java
288 | if $cygwin; then
289 | [ -n "$M2_HOME" ] &&
290 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
291 | [ -n "$JAVA_HOME" ] &&
292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
293 | [ -n "$CLASSPATH" ] &&
294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
295 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
297 | fi
298 |
299 | # Provide a "standardized" way to retrieve the CLI args that will
300 | # work with both Windows and non-Windows executions.
301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
302 | export MAVEN_CMD_LINE_ARGS
303 |
304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
305 |
306 | exec "$JAVACMD" \
307 | $MAVEN_OPTS \
308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
311 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM https://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
124 |
125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
162 | if ERRORLEVEL 1 goto error
163 | goto end
164 |
165 | :error
166 | set ERROR_CODE=1
167 |
168 | :end
169 | @endlocal & set ERROR_CODE=%ERROR_CODE%
170 |
171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
175 | :skipRcPost
176 |
177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
179 |
180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
181 |
182 | exit /B %ERROR_CODE%
183 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | 4.0.0
7 | com.sivalabs
8 | spring-boot-tutorials-blog-series
9 | 1.0.0
10 | pom
11 | spring-boot-tutorials-blog-series
12 |
13 |
14 | spring-boot-helloworld
15 | spring-boot-jdbc-tutorial
16 | spring-boot-flyway-tutorial
17 | spring-boot-rest-api-tutorial
18 | spring-boot-jdbcclient-tutorial
19 |
20 |
21 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 | .gradle
7 | build/
8 | !gradle/wrapper/gradle-wrapper.jar
9 | !**/src/main/**/build/
10 | !**/src/test/**/build/
11 |
12 | ### STS ###
13 | .apt_generated
14 | .classpath
15 | .factorypath
16 | .project
17 | .settings
18 | .springBeans
19 | .sts4-cache
20 | bin/
21 | !**/src/main/**/bin/
22 | !**/src/test/**/bin/
23 |
24 | ### IntelliJ IDEA ###
25 | .idea
26 | *.iws
27 | *.iml
28 | *.ipr
29 | out/
30 | !**/src/main/**/out/
31 | !**/src/test/**/out/
32 |
33 | ### NetBeans ###
34 | /nbproject/private/
35 | /nbbuild/
36 | /dist/
37 | /nbdist/
38 | /.nb-gradle/
39 |
40 | ### VS Code ###
41 | .vscode/
42 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | 4.0.0
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.1.2
11 |
12 |
13 | com.sivalabs
14 | spring-boot-flyway-tutorial
15 | 0.0.1
16 | spring-boot-flyway-tutorial
17 |
18 |
19 | 17
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-web
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-data-jpa
30 |
31 |
32 | org.postgresql
33 | postgresql
34 | runtime
35 |
36 |
37 | org.flywaydb
38 | flyway-core
39 |
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-test
44 | test
45 |
46 |
47 | org.springframework.boot
48 | spring-boot-testcontainers
49 | test
50 |
51 |
52 | org.testcontainers
53 | junit-jupiter
54 | test
55 |
56 |
57 | org.testcontainers
58 | postgresql
59 | test
60 |
61 |
62 |
63 |
64 |
65 |
66 | org.springframework.boot
67 | spring-boot-maven-plugin
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/main/java/com/sivalabs/bookmarks/Application.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/main/java/com/sivalabs/bookmarks/domain/Bookmark.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.GeneratedValue;
5 | import jakarta.persistence.GenerationType;
6 | import jakarta.persistence.Id;
7 | import jakarta.persistence.Table;
8 |
9 | import java.time.Instant;
10 |
11 | @Entity
12 | @Table(name = "bookmarks")
13 | public class Bookmark {
14 | @Id
15 | @GeneratedValue(strategy = GenerationType.IDENTITY)
16 | private Long id;
17 | private String title;
18 | private String url;
19 | private Instant createdAt;
20 |
21 | public Bookmark() {
22 | }
23 |
24 | public Bookmark(
25 | Long id,
26 | String title,
27 | String url,
28 | Instant createdAt) {
29 | this.id = id;
30 | this.title = title;
31 | this.url = url;
32 | this.createdAt = createdAt;
33 | }
34 |
35 | public Long getId() {
36 | return id;
37 | }
38 |
39 | public void setId(Long id) {
40 | this.id = id;
41 | }
42 |
43 | public String getTitle() {
44 | return title;
45 | }
46 |
47 | public void setTitle(String title) {
48 | this.title = title;
49 | }
50 |
51 | public String getUrl() {
52 | return url;
53 | }
54 |
55 | public void setUrl(String url) {
56 | this.url = url;
57 | }
58 |
59 | public Instant getCreatedAt() {
60 | return createdAt;
61 | }
62 |
63 | public void setCreatedAt(Instant createdAt) {
64 | this.createdAt = createdAt;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/main/java/com/sivalabs/bookmarks/domain/BookmarkRepository.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 |
5 | public interface BookmarkRepository extends JpaRepository {
6 | }
7 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #spring.flyway.enabled=false
2 | #spring.flyway.baseline-on-migrate=true
3 | #spring.flyway.table=db_migrations
4 | #spring.flyway.clean-disabled=false
5 | #spring.flyway.clean-on-validation-error=true
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/main/resources/db/migration/V1__create_tables.sql:
--------------------------------------------------------------------------------
1 | create table bookmarks
2 | (
3 | id bigserial not null,
4 | title varchar not null,
5 | url varchar not null,
6 | created_at timestamp,
7 | primary key (id)
8 | );
9 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/main/resources/db/migration/V2__create_bookmarks_indexes.sql:
--------------------------------------------------------------------------------
1 | CREATE INDEX idx_bookmarks_title ON bookmarks(title);
2 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/test/java/com/sivalabs/bookmarks/TestApplication.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.test.context.TestConfiguration;
5 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
6 | import org.springframework.context.annotation.Bean;
7 | import org.testcontainers.containers.PostgreSQLContainer;
8 | import org.testcontainers.utility.DockerImageName;
9 |
10 | @TestConfiguration(proxyBeanMethods = false)
11 | public class TestApplication {
12 |
13 | @Bean
14 | @ServiceConnection
15 | PostgreSQLContainer> postgresContainer() {
16 | return new PostgreSQLContainer<>(DockerImageName.parse("postgres:15-alpine"));
17 | }
18 |
19 | public static void main(String[] args) {
20 | SpringApplication
21 | .from(Application::main)
22 | .with(TestApplication.class)
23 | .run(args);
24 | }
25 | }
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/test/java/com/sivalabs/bookmarks/domain/BookmarkRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
6 | import org.springframework.test.context.jdbc.Sql;
7 |
8 | import java.time.Instant;
9 | import java.util.List;
10 | import java.util.Optional;
11 |
12 | import static org.assertj.core.api.Assertions.assertThat;
13 |
14 | @DataJpaTest(properties = {
15 | "spring.test.database.replace=none",
16 | "spring.datasource.url=jdbc:tc:postgresql:15.2-alpine:///db"
17 | })
18 | @Sql("/test-data.sql")
19 | class BookmarkRepositoryTest {
20 |
21 | @Autowired
22 | BookmarkRepository bookmarkRepository;
23 |
24 | @Test
25 | void shouldFindAllBookmarks() {
26 | List bookmarks = bookmarkRepository.findAll();
27 | assertThat(bookmarks).isNotEmpty();
28 | assertThat(bookmarks).hasSize(15);
29 | }
30 |
31 | @Test
32 | void shouldCreateBookmark() {
33 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
34 | Bookmark savedBookmark = bookmarkRepository.save(bookmark);
35 | assertThat(savedBookmark).isNotNull();
36 | }
37 |
38 | @Test
39 | void shouldGetBookmarkById() {
40 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
41 | Bookmark savedBookmark = bookmarkRepository.save(bookmark);
42 |
43 | Optional bookmarkOptional = bookmarkRepository.findById(savedBookmark.getId());
44 | assertThat(bookmarkOptional).isPresent();
45 | }
46 |
47 | @Test
48 | void shouldEmptyWhenBookmarkNotFound() {
49 | Optional bookmarkOptional = bookmarkRepository.findById(9999L);
50 | assertThat(bookmarkOptional).isEmpty();
51 | }
52 |
53 | @Test
54 | void shouldUpdateBookmark() {
55 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
56 | Bookmark savedBookmark = bookmarkRepository.save(bookmark);
57 |
58 | savedBookmark.setTitle("My Updated Title");
59 | savedBookmark.setUrl("https://www.sivalabs.in");
60 | bookmarkRepository.save(savedBookmark);
61 |
62 | Bookmark updatedBookmark = bookmarkRepository.findById(savedBookmark.getId()).orElseThrow();
63 | assertThat(updatedBookmark.getId()).isEqualTo(savedBookmark.getId());
64 | assertThat(updatedBookmark.getTitle()).isEqualTo("My Updated Title");
65 | assertThat(updatedBookmark.getUrl()).isEqualTo("https://www.sivalabs.in");
66 | }
67 |
68 | @Test
69 | void shouldDeleteBookmark() {
70 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
71 | Bookmark savedBookmark = bookmarkRepository.save(bookmark);
72 |
73 | bookmarkRepository.deleteById(savedBookmark.getId());
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/spring-boot-flyway-tutorial/src/test/resources/test-data.sql:
--------------------------------------------------------------------------------
1 | truncate table bookmarks;
2 | ALTER SEQUENCE bookmarks_id_seq RESTART WITH 1;
3 |
4 | insert into bookmarks(title, url, created_at) values
5 | ('How (not) to ask for Technical Help?','https://sivalabs.in/how-to-not-to-ask-for-technical-help', CURRENT_TIMESTAMP),
6 | ('Announcing My SpringBoot Tips Video Series on YouTube','https://sivalabs.in/announcing-my-springboot-tips-video-series', CURRENT_TIMESTAMP),
7 | ('Kubernetes - Exposing Services to outside of Cluster using Ingress','https://sivalabs.in/kubernetes-ingress', CURRENT_TIMESTAMP),
8 | ('Kubernetes - Blue/Green Deployments','https://sivalabs.in/kubernetes-blue-green-deployments', CURRENT_TIMESTAMP),
9 | ('Kubernetes - Releasing a new version of the application using Deployment Rolling Updates','https://sivalabs.in/kubernetes-deployment-rolling-updates', CURRENT_TIMESTAMP),
10 | ('Getting Started with Kubernetes','https://sivalabs.in/getting-started-with-kubernetes', CURRENT_TIMESTAMP),
11 | ('Get Super Productive with Intellij File Templates','https://sivalabs.in/get-super-productive-with-intellij-file-templates', CURRENT_TIMESTAMP),
12 | ('Few Things I learned in the HardWay in 15 years of my career','https://sivalabs.in/few-things-i-learned-the-hardway-in-15-years-of-my-career', CURRENT_TIMESTAMP),
13 | ('All the resources you ever need as a Java & Spring application developer','https://sivalabs.in/all-the-resources-you-ever-need-as-a-java-spring-application-developer', CURRENT_TIMESTAMP),
14 | ('GoLang from a Java developer perspective','https://sivalabs.in/golang-from-a-java-developer-perspective', CURRENT_TIMESTAMP),
15 | ('Imposing Code Structure Guidelines using ArchUnit','https://sivalabs.in/impose-architecture-guidelines-using-archunit', CURRENT_TIMESTAMP),
16 | ('SpringBoot Integration Testing using TestContainers Starter','https://sivalabs.in/spring-boot-integration-testing-using-testcontainers-starter', CURRENT_TIMESTAMP),
17 | ('Creating Yeoman based SpringBoot Generator','https://sivalabs.in/creating-yeoman-based-springboot-generator', CURRENT_TIMESTAMP),
18 | ('Testing REST APIs using Postman and Newman','https://sivalabs.in/testing-rest-apis-with-postman-newman', CURRENT_TIMESTAMP),
19 | ('Testing SpringBoot Applications','https://sivalabs.in/spring-boot-testing', CURRENT_TIMESTAMP)
20 | ;
--------------------------------------------------------------------------------
/spring-boot-helloworld/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 | .gradle
7 | !gradle/wrapper/gradle-wrapper.jar
8 |
9 |
10 | ### STS ###
11 | .apt_generated
12 | .classpath
13 | .factorypath
14 | .project
15 | .settings
16 | .springBeans
17 | .sts4-cache
18 |
19 | ### IntelliJ IDEA ###
20 | .idea
21 | *.iws
22 | *.iml
23 | *.ipr
24 |
25 | ### NetBeans ###
26 | /nbproject/private/
27 | /nbbuild/
28 | /dist/
29 | /nbdist/
30 | /.nb-gradle/
31 | build/
32 | !**/src/main/**/build/
33 | !**/src/test/**/build/
34 |
35 | ### VS Code ###
36 | .vscode/
37 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2007-present the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import java.net.*;
18 | import java.io.*;
19 | import java.nio.channels.*;
20 | import java.util.Properties;
21 |
22 | public class MavenWrapperDownloader {
23 |
24 | private static final String WRAPPER_VERSION = "0.5.6";
25 | /**
26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
27 | */
28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
30 |
31 | /**
32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
33 | * use instead of the default one.
34 | */
35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
36 | ".mvn/wrapper/maven-wrapper.properties";
37 |
38 | /**
39 | * Path where the maven-wrapper.jar will be saved to.
40 | */
41 | private static final String MAVEN_WRAPPER_JAR_PATH =
42 | ".mvn/wrapper/maven-wrapper.jar";
43 |
44 | /**
45 | * Name of the property which should be used to override the default download url for the wrapper.
46 | */
47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
48 |
49 | public static void main(String args[]) {
50 | System.out.println("- Downloader started");
51 | File baseDirectory = new File(args[0]);
52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
53 |
54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom
55 | // wrapperUrl parameter.
56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
57 | String url = DEFAULT_DOWNLOAD_URL;
58 | if (mavenWrapperPropertyFile.exists()) {
59 | FileInputStream mavenWrapperPropertyFileInputStream = null;
60 | try {
61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
62 | Properties mavenWrapperProperties = new Properties();
63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
65 | } catch (IOException e) {
66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
67 | } finally {
68 | try {
69 | if (mavenWrapperPropertyFileInputStream != null) {
70 | mavenWrapperPropertyFileInputStream.close();
71 | }
72 | } catch (IOException e) {
73 | // Ignore ...
74 | }
75 | }
76 | }
77 | System.out.println("- Downloading from: " + url);
78 |
79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
80 | if (!outputFile.getParentFile().exists()) {
81 | if (!outputFile.getParentFile().mkdirs()) {
82 | System.out.println(
83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
84 | }
85 | }
86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
87 | try {
88 | downloadFileFromURL(url, outputFile);
89 | System.out.println("Done");
90 | System.exit(0);
91 | } catch (Throwable e) {
92 | System.out.println("- Error downloading");
93 | e.printStackTrace();
94 | System.exit(1);
95 | }
96 | }
97 |
98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception {
99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
100 | String username = System.getenv("MVNW_USERNAME");
101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
102 | Authenticator.setDefault(new Authenticator() {
103 | @Override
104 | protected PasswordAuthentication getPasswordAuthentication() {
105 | return new PasswordAuthentication(username, password);
106 | }
107 | });
108 | }
109 | URL website = new URL(urlString);
110 | ReadableByteChannel rbc;
111 | rbc = Channels.newChannel(website.openStream());
112 | FileOutputStream fos = new FileOutputStream(destination);
113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
114 | fos.close();
115 | rbc.close();
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-tutorials-blog-series/4e04cc2e628e0728234b949f7b341bad829e6be9/spring-boot-helloworld/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/spring-boot-helloworld/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/README.md:
--------------------------------------------------------------------------------
1 | # SpringBoot HelloWorld
2 |
3 | How to run?
4 |
5 | 1. Using Maven: `./mvnw spring-boot:run`
6 |
7 | 2. Using Gradle: `./gradlew bootRun`
8 |
9 | 3. Run app as a jar:
10 | ```
11 | // Maven
12 | java -jar target/spring-boot-helloworld-0.0.1-SNAPSHOT.jar`
13 |
14 | // Gradle
15 | java -jar build/libs/spring-boot-helloworld-0.0.1-SNAPSHOT.jar`
16 | ```
17 | 4. Override properties:
18 |
19 | ```shell
20 | $ java -jar target/spring-boot-helloworld-0.0.1-SNAPSHOT.jar --app.greeting=Hola
21 | $ java -jar -Dapp.greeting=Hola target/spring-boot-helloworld-0.0.1-SNAPSHOT.jar
22 | $
23 | $ export APP_GREETING=Bonjour
24 | $ java -jar target/spring-boot-helloworld-0.0.1-SNAPSHOT.jar
25 | ```
26 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.1.2'
3 | id 'io.spring.dependency-management' version '1.1.0'
4 | id 'java'
5 | id 'com.google.cloud.tools.jib' version '3.3.1'
6 | }
7 |
8 | group = 'com.sivalabs'
9 | version = '0.0.1-SNAPSHOT'
10 | sourceCompatibility = '17'
11 |
12 | configurations {
13 | compileOnly {
14 | extendsFrom annotationProcessor
15 | }
16 | }
17 |
18 | repositories {
19 | mavenCentral()
20 | }
21 |
22 | dependencies {
23 | implementation 'org.springframework.boot:spring-boot-starter-actuator'
24 | implementation 'org.springframework.boot:spring-boot-starter-web'
25 | compileOnly 'org.projectlombok:lombok'
26 | developmentOnly 'org.springframework.boot:spring-boot-devtools'
27 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
28 | annotationProcessor 'org.projectlombok:lombok'
29 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
30 | }
31 |
32 | tasks.named('test') {
33 | useJUnitPlatform()
34 | }
35 |
36 | tasks.named("bootBuildImage") {
37 | imageName = "sivaprasadreddy/${project.name}"
38 | pullPolicy = "IF_NOT_PRESENT"
39 | }
40 |
41 | jib {
42 | from {
43 | image = 'eclipse-temurin:17.0.6_10-jre-focal'
44 | }
45 | to {
46 | image = "sivaprasadreddy/${project.name}"
47 | tags = ["latest", "${project.version}"]
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-tutorials-blog-series/4e04cc2e628e0728234b949f7b341bad829e6be9/spring-boot-helloworld/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/spring-boot-helloworld/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original 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 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 | # Collect all arguments for the java command;
201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
202 | # shell script including quotes and variable substitutions, so put them in
203 | # double quotes to make sure that they get re-expanded; and
204 | # * put everything else in single quotes, so that it's not re-expanded.
205 |
206 | set -- \
207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
208 | -classpath "$CLASSPATH" \
209 | org.gradle.wrapper.GradleWrapperMain \
210 | "$@"
211 |
212 | # Stop when "xargs" is not available.
213 | if ! command -v xargs >/dev/null 2>&1
214 | then
215 | die "xargs is not available"
216 | fi
217 |
218 | # Use "xargs" to parse quoted args.
219 | #
220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
221 | #
222 | # In Bash we could simply go:
223 | #
224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
225 | # set -- "${ARGS[@]}" "$@"
226 | #
227 | # but POSIX shell has neither arrays nor command substitution, so instead we
228 | # post-process each arg (as a line of input to sed) to backslash-escape any
229 | # character that might be a shell metacharacter, then use eval to reverse
230 | # that process (while maintaining the separation between arguments), and wrap
231 | # the whole thing up as a single "set" statement.
232 | #
233 | # This will of course break if any of these variables contains a newline or
234 | # an unmatched quote.
235 | #
236 |
237 | eval "set -- $(
238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
239 | xargs -n1 |
240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
241 | tr '\n' ' '
242 | )" '"$@"'
243 |
244 | exec "$JAVACMD" "$@"
245 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/gradlew.bat:
--------------------------------------------------------------------------------
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 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # https://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Mingw, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | fi
118 |
119 | if [ -z "$JAVA_HOME" ]; then
120 | javaExecutable="`which javac`"
121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
122 | # readlink(1) is not available as standard on Solaris 10.
123 | readLink=`which readlink`
124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
125 | if $darwin ; then
126 | javaHome="`dirname \"$javaExecutable\"`"
127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
128 | else
129 | javaExecutable="`readlink -f \"$javaExecutable\"`"
130 | fi
131 | javaHome="`dirname \"$javaExecutable\"`"
132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
133 | JAVA_HOME="$javaHome"
134 | export JAVA_HOME
135 | fi
136 | fi
137 | fi
138 |
139 | if [ -z "$JAVACMD" ] ; then
140 | if [ -n "$JAVA_HOME" ] ; then
141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
142 | # IBM's JDK on AIX uses strange locations for the executables
143 | JAVACMD="$JAVA_HOME/jre/sh/java"
144 | else
145 | JAVACMD="$JAVA_HOME/bin/java"
146 | fi
147 | else
148 | JAVACMD="`which java`"
149 | fi
150 | fi
151 |
152 | if [ ! -x "$JAVACMD" ] ; then
153 | echo "Error: JAVA_HOME is not defined correctly." >&2
154 | echo " We cannot execute $JAVACMD" >&2
155 | exit 1
156 | fi
157 |
158 | if [ -z "$JAVA_HOME" ] ; then
159 | echo "Warning: JAVA_HOME environment variable is not set."
160 | fi
161 |
162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
163 |
164 | # traverses directory structure from process work directory to filesystem root
165 | # first directory with .mvn subdirectory is considered project base directory
166 | find_maven_basedir() {
167 |
168 | if [ -z "$1" ]
169 | then
170 | echo "Path not specified to find_maven_basedir"
171 | return 1
172 | fi
173 |
174 | basedir="$1"
175 | wdir="$1"
176 | while [ "$wdir" != '/' ] ; do
177 | if [ -d "$wdir"/.mvn ] ; then
178 | basedir=$wdir
179 | break
180 | fi
181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
182 | if [ -d "${wdir}" ]; then
183 | wdir=`cd "$wdir/.."; pwd`
184 | fi
185 | # end of workaround
186 | done
187 | echo "${basedir}"
188 | }
189 |
190 | # concatenates all lines of a file
191 | concat_lines() {
192 | if [ -f "$1" ]; then
193 | echo "$(tr -s '\n' ' ' < "$1")"
194 | fi
195 | }
196 |
197 | BASE_DIR=`find_maven_basedir "$(pwd)"`
198 | if [ -z "$BASE_DIR" ]; then
199 | exit 1;
200 | fi
201 |
202 | ##########################################################################################
203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
204 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
205 | ##########################################################################################
206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
207 | if [ "$MVNW_VERBOSE" = true ]; then
208 | echo "Found .mvn/wrapper/maven-wrapper.jar"
209 | fi
210 | else
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
213 | fi
214 | if [ -n "$MVNW_REPOURL" ]; then
215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
216 | else
217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
218 | fi
219 | while IFS="=" read key value; do
220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
221 | esac
222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
223 | if [ "$MVNW_VERBOSE" = true ]; then
224 | echo "Downloading from: $jarUrl"
225 | fi
226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
227 | if $cygwin; then
228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
229 | fi
230 |
231 | if command -v wget > /dev/null; then
232 | if [ "$MVNW_VERBOSE" = true ]; then
233 | echo "Found wget ... using wget"
234 | fi
235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
236 | wget "$jarUrl" -O "$wrapperJarPath"
237 | else
238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
239 | fi
240 | elif command -v curl > /dev/null; then
241 | if [ "$MVNW_VERBOSE" = true ]; then
242 | echo "Found curl ... using curl"
243 | fi
244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
245 | curl -o "$wrapperJarPath" "$jarUrl" -f
246 | else
247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
248 | fi
249 |
250 | else
251 | if [ "$MVNW_VERBOSE" = true ]; then
252 | echo "Falling back to using Java to download"
253 | fi
254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
255 | # For Cygwin, switch paths to Windows format before running javac
256 | if $cygwin; then
257 | javaClass=`cygpath --path --windows "$javaClass"`
258 | fi
259 | if [ -e "$javaClass" ]; then
260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
261 | if [ "$MVNW_VERBOSE" = true ]; then
262 | echo " - Compiling MavenWrapperDownloader.java ..."
263 | fi
264 | # Compiling the Java class
265 | ("$JAVA_HOME/bin/javac" "$javaClass")
266 | fi
267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
268 | # Running the downloader
269 | if [ "$MVNW_VERBOSE" = true ]; then
270 | echo " - Running MavenWrapperDownloader.java ..."
271 | fi
272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
273 | fi
274 | fi
275 | fi
276 | fi
277 | ##########################################################################################
278 | # End of extension
279 | ##########################################################################################
280 |
281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
282 | if [ "$MVNW_VERBOSE" = true ]; then
283 | echo $MAVEN_PROJECTBASEDIR
284 | fi
285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
286 |
287 | # For Cygwin, switch paths to Windows format before running java
288 | if $cygwin; then
289 | [ -n "$M2_HOME" ] &&
290 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
291 | [ -n "$JAVA_HOME" ] &&
292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
293 | [ -n "$CLASSPATH" ] &&
294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
295 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
297 | fi
298 |
299 | # Provide a "standardized" way to retrieve the CLI args that will
300 | # work with both Windows and non-Windows executions.
301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
302 | export MAVEN_CMD_LINE_ARGS
303 |
304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
305 |
306 | exec "$JAVACMD" \
307 | $MAVEN_OPTS \
308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
311 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM https://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
124 |
125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
162 | if ERRORLEVEL 1 goto error
163 | goto end
164 |
165 | :error
166 | set ERROR_CODE=1
167 |
168 | :end
169 | @endlocal & set ERROR_CODE=%ERROR_CODE%
170 |
171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
175 | :skipRcPost
176 |
177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
179 |
180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
181 |
182 | exit /B %ERROR_CODE%
183 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | 4.0.0
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.1.2
11 |
12 |
13 | com.sivalabs
14 | spring-boot-helloworld
15 | 0.0.1-SNAPSHOT
16 | spring-boot-helloworld
17 | Spring Boot HelloWorld
18 |
19 | 17
20 | sivaprasadreddy/${project.artifactId}
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-actuator
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-web
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-validation
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-devtools
38 | runtime
39 | true
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-configuration-processor
44 | true
45 |
46 |
47 | org.projectlombok
48 | lombok
49 | true
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-starter-test
54 | test
55 |
56 |
57 |
58 |
59 |
60 |
61 | org.springframework.boot
62 | spring-boot-maven-plugin
63 |
64 | ${dockerImageName}
65 | IF_NOT_PRESENT
66 |
67 |
68 | org.projectlombok
69 | lombok
70 |
71 |
72 |
73 |
74 |
75 | com.google.cloud.tools
76 | jib-maven-plugin
77 | 3.3.1
78 |
79 |
80 | eclipse-temurin:17.0.6_10-jre-focal
81 |
82 |
83 | ${dockerImageName}
84 |
85 | latest
86 | ${project.version}
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'spring-boot-helloworld'
2 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/main/java/com/sivalabs/helloworld/ApplicationProperties.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.helloworld;
2 |
3 | import lombok.Getter;
4 | import lombok.Setter;
5 | import org.springframework.boot.context.properties.ConfigurationProperties;
6 |
7 | @ConfigurationProperties(prefix = "app")
8 | @Setter
9 | @Getter
10 | public class ApplicationProperties {
11 | private String greeting = "Hello";
12 | private String defaultName = "World";
13 | }
14 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/main/java/com/sivalabs/helloworld/GreetingResponse.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.helloworld;
2 |
3 | record GreetingResponse(String greeting){}
4 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/main/java/com/sivalabs/helloworld/GreetingService.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.helloworld;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.stereotype.Service;
5 |
6 | @Service
7 | @RequiredArgsConstructor
8 | public class GreetingService {
9 | private final ApplicationProperties properties;
10 |
11 | public String sayHello(String name) {
12 | String s = name == null ? properties.getDefaultName() : name;
13 | return String.format("%s %s", properties.getGreeting(), s);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/main/java/com/sivalabs/helloworld/HelloWorldController.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.helloworld;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.RequestParam;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | @RestController
10 | @RequiredArgsConstructor
11 | @Slf4j
12 | public class HelloWorldController {
13 | private final GreetingService greetingService;
14 |
15 | @GetMapping("/api/hello")
16 | public GreetingResponse sayHello(@RequestParam(name = "name", required = false) String name) {
17 | log.info("Say Hello to Name: {}", name);
18 | String greeting = greetingService.sayHello(name);
19 | return new GreetingResponse(greeting);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/main/java/com/sivalabs/helloworld/SpringBootHelloWorldApplication.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.helloworld;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
6 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
7 |
8 | @SpringBootApplication
9 | @EnableConfigurationProperties({ApplicationProperties.class})
10 | //@ConfigurationPropertiesScan
11 | public class SpringBootHelloWorldApplication {
12 |
13 | public static void main(String[] args) {
14 | SpringApplication.run(SpringBootHelloWorldApplication.class, args);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.greeting=Hello
2 | app.default-name=World
3 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/test/java/com/sivalabs/helloworld/GreetingServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.helloworld;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.BeforeEach;
5 | import org.junit.jupiter.api.Test;
6 | import org.junit.jupiter.api.extension.ExtendWith;
7 | import org.mockito.InjectMocks;
8 | import org.mockito.Mock;
9 | import org.mockito.junit.jupiter.MockitoExtension;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 | import static org.mockito.BDDMockito.given;
13 |
14 | @ExtendWith(MockitoExtension.class)
15 | class GreetingServiceTest {
16 |
17 | @Mock
18 | private ApplicationProperties properties;
19 |
20 | @InjectMocks
21 | private GreetingService greetingService;
22 |
23 | @BeforeEach
24 | void setUp() {
25 | given(properties.getGreeting()).willReturn("Hello");
26 | }
27 |
28 | @Test
29 | void shouldGreetWithDefaultNameWhenNameIsNotProvided() {
30 | given(properties.getDefaultName()).willReturn("World");
31 |
32 | String greeting = greetingService.sayHello(null);
33 |
34 | Assertions.assertEquals( "Hello World", greeting); //JUnit 5 based assertion
35 | assertThat(greeting).isEqualTo("Hello World"); //Assertj based assertion
36 | }
37 |
38 | @Test
39 | void shouldGreetWithGivenName() {
40 | String greeting = greetingService.sayHello("John");
41 |
42 | assertThat(greeting).isEqualTo("Hello John");
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/test/java/com/sivalabs/helloworld/HelloWorldControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.helloworld;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
6 | import org.springframework.boot.test.mock.mockito.MockBean;
7 | import org.springframework.test.web.servlet.MockMvc;
8 |
9 | import static org.hamcrest.CoreMatchers.is;
10 | import static org.mockito.BDDMockito.given;
11 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
12 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
13 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
14 |
15 | @WebMvcTest(controllers = HelloWorldController.class)
16 | class HelloWorldControllerTest {
17 | @Autowired
18 | private MockMvc mockMvc;
19 |
20 | @MockBean
21 | private GreetingService greetingService;
22 |
23 | @Test
24 | void shouldReturnGreetingSuccessfully() throws Exception {
25 | given(greetingService.sayHello("Siva")).willReturn("Hello Siva");
26 |
27 | mockMvc.perform(get("/api/hello?name={name}", "Siva"))
28 | .andExpect(status().isOk())
29 | .andExpect(jsonPath("$.greeting", is("Hello Siva")));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/spring-boot-helloworld/src/test/java/com/sivalabs/helloworld/SpringBootHelloWorldApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.helloworld;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
6 | import org.springframework.boot.test.context.SpringBootTest;
7 | import org.springframework.test.web.servlet.MockMvc;
8 |
9 | import static org.hamcrest.CoreMatchers.is;
10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
12 |
13 | @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
14 | @AutoConfigureMockMvc
15 | class SpringBootHelloWorldApplicationTests {
16 |
17 | @Autowired
18 | private MockMvc mockMvc;
19 |
20 | @Test
21 | void shouldReturnGreetingSuccessfully() throws Exception {
22 | mockMvc.perform(get("/api/hello?name={name}", "Siva"))
23 | .andExpect(status().isOk())
24 | .andExpect(jsonPath("$.greeting", is("Hello Siva")));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 | .gradle
7 | build/
8 | !gradle/wrapper/gradle-wrapper.jar
9 | !**/src/main/**/build/
10 | !**/src/test/**/build/
11 |
12 | ### STS ###
13 | .apt_generated
14 | .classpath
15 | .factorypath
16 | .project
17 | .settings
18 | .springBeans
19 | .sts4-cache
20 | bin/
21 | !**/src/main/**/bin/
22 | !**/src/test/**/bin/
23 |
24 | ### IntelliJ IDEA ###
25 | .idea
26 | *.iws
27 | *.iml
28 | *.ipr
29 | out/
30 | !**/src/main/**/out/
31 | !**/src/test/**/out/
32 |
33 | ### NetBeans ###
34 | /nbproject/private/
35 | /nbbuild/
36 | /dist/
37 | /nbdist/
38 | /.nb-gradle/
39 |
40 | ### VS Code ###
41 | .vscode/
42 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | 4.0.0
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.1.2
11 |
12 |
13 | com.sivalabs
14 | spring-boot-jdbc-tutorial
15 | 0.0.1
16 | spring-boot-jdbc-tutorial
17 |
18 |
19 | 17
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-jdbc
26 |
27 |
28 | org.postgresql
29 | postgresql
30 | runtime
31 |
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-test
36 | test
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-testcontainers
41 | test
42 |
43 |
44 | org.testcontainers
45 | junit-jupiter
46 | test
47 |
48 |
49 | org.testcontainers
50 | postgresql
51 | test
52 |
53 |
54 |
55 |
56 |
57 |
58 | org.springframework.boot
59 | spring-boot-maven-plugin
60 |
61 |
62 |
63 |
64 |
65 | spring-milestones
66 | Spring Milestones
67 | https://repo.spring.io/milestone
68 |
69 | false
70 |
71 |
72 |
73 | spring-snapshots
74 | Spring Snapshots
75 | https://repo.spring.io/snapshot
76 |
77 | false
78 |
79 |
80 |
81 |
82 |
83 | spring-milestones
84 | Spring Milestones
85 | https://repo.spring.io/milestone
86 |
87 | false
88 |
89 |
90 |
91 | spring-snapshots
92 | Spring Snapshots
93 | https://repo.spring.io/snapshot
94 |
95 | false
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/src/main/java/com/sivalabs/bookmarks/Application.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/src/main/java/com/sivalabs/bookmarks/domain/Bookmark.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import java.time.Instant;
4 |
5 | public record Bookmark(
6 | Long id,
7 | String title,
8 | String url,
9 | Instant createdAt) {}
10 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/src/main/java/com/sivalabs/bookmarks/domain/BookmarkRepository.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import org.springframework.dao.EmptyResultDataAccessException;
4 | import org.springframework.jdbc.core.JdbcTemplate;
5 | import org.springframework.jdbc.core.RowMapper;
6 | import org.springframework.jdbc.support.GeneratedKeyHolder;
7 | import org.springframework.jdbc.support.KeyHolder;
8 | import org.springframework.stereotype.Repository;
9 |
10 | import java.sql.PreparedStatement;
11 | import java.sql.ResultSet;
12 | import java.sql.SQLException;
13 | import java.sql.Timestamp;
14 | import java.util.List;
15 | import java.util.Optional;
16 |
17 | import static com.sivalabs.bookmarks.domain.BookmarkRepository.BookmarkRowMapper.INSTANCE;
18 |
19 | @Repository
20 | public class BookmarkRepository {
21 | private final JdbcTemplate jdbcTemplate;
22 |
23 | public BookmarkRepository(JdbcTemplate jdbcTemplate) {
24 | this.jdbcTemplate = jdbcTemplate;
25 | }
26 |
27 | public List findAll() {
28 | String sql = "select id, title, url, created_at from bookmarks";
29 | return jdbcTemplate.query(sql, INSTANCE);
30 | }
31 |
32 | public Optional findById(Long id) {
33 | String sql = "select id, title, url, created_at from bookmarks where id = ?";
34 | try {
35 | Bookmark bookmark = jdbcTemplate.queryForObject(sql, INSTANCE, id);
36 | return Optional.ofNullable(bookmark);
37 | } catch (EmptyResultDataAccessException e) {
38 | return Optional.empty();
39 | }
40 | }
41 |
42 | public Long save(Bookmark bookmark) {
43 | KeyHolder keyHolder = new GeneratedKeyHolder();
44 |
45 | jdbcTemplate.update(connection -> {
46 | String sql = "insert into bookmarks(title, url, created_at) values(?,?,?)";
47 | PreparedStatement ps = connection.prepareStatement(sql, new String[] { "id" });
48 | ps.setString(1, bookmark.title());
49 | ps.setString(2, bookmark.url());
50 | ps.setTimestamp(3, Timestamp.from(bookmark.createdAt()));
51 | return ps;
52 | }, keyHolder);
53 |
54 | return (long) keyHolder.getKey();
55 | }
56 |
57 | public void update(Bookmark bookmark) {
58 | String sql = "update bookmarks set title = ?, url = ? where id = ?";
59 | int count = jdbcTemplate.update(sql, bookmark.title(), bookmark.url(), bookmark.id());
60 | if (count == 0) {
61 | throw new RuntimeException("Bookmark not found");
62 | }
63 | }
64 |
65 | public void delete(Long id) {
66 | String sql = "delete from bookmarks where id = ?";
67 | int count = jdbcTemplate.update(sql, id);
68 | if (count == 0) {
69 | throw new RuntimeException("Bookmark not found");
70 | }
71 | }
72 |
73 | static class BookmarkRowMapper implements RowMapper {
74 | public static final BookmarkRowMapper INSTANCE = new BookmarkRowMapper();
75 | private BookmarkRowMapper(){}
76 |
77 | @Override
78 | public Bookmark mapRow(ResultSet rs, int rowNum) throws SQLException {
79 | return new Bookmark(
80 | rs.getLong("id"),
81 | rs.getString("title"),
82 | rs.getString("url"),
83 | rs.getTimestamp("created_at").toInstant()
84 | );
85 | }
86 | }
87 | }
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.sql.init.mode=always
2 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/src/main/resources/data.sql:
--------------------------------------------------------------------------------
1 | truncate table bookmarks;
2 | ALTER SEQUENCE bookmarks_id_seq RESTART WITH 1;
3 |
4 | insert into bookmarks(title, url, created_at) values
5 | ('How (not) to ask for Technical Help?','https://sivalabs.in/how-to-not-to-ask-for-technical-help', CURRENT_TIMESTAMP),
6 | ('Announcing My SpringBoot Tips Video Series on YouTube','https://sivalabs.in/announcing-my-springboot-tips-video-series', CURRENT_TIMESTAMP),
7 | ('Kubernetes - Exposing Services to outside of Cluster using Ingress','https://sivalabs.in/kubernetes-ingress', CURRENT_TIMESTAMP),
8 | ('Kubernetes - Blue/Green Deployments','https://sivalabs.in/kubernetes-blue-green-deployments', CURRENT_TIMESTAMP),
9 | ('Kubernetes - Releasing a new version of the application using Deployment Rolling Updates','https://sivalabs.in/kubernetes-deployment-rolling-updates', CURRENT_TIMESTAMP),
10 | ('Getting Started with Kubernetes','https://sivalabs.in/getting-started-with-kubernetes', CURRENT_TIMESTAMP),
11 | ('Get Super Productive with Intellij File Templates','https://sivalabs.in/get-super-productive-with-intellij-file-templates', CURRENT_TIMESTAMP),
12 | ('Few Things I learned in the HardWay in 15 years of my career','https://sivalabs.in/few-things-i-learned-the-hardway-in-15-years-of-my-career', CURRENT_TIMESTAMP),
13 | ('All the resources you ever need as a Java & Spring application developer','https://sivalabs.in/all-the-resources-you-ever-need-as-a-java-spring-application-developer', CURRENT_TIMESTAMP),
14 | ('GoLang from a Java developer perspective','https://sivalabs.in/golang-from-a-java-developer-perspective', CURRENT_TIMESTAMP),
15 | ('Imposing Code Structure Guidelines using ArchUnit','https://sivalabs.in/impose-architecture-guidelines-using-archunit', CURRENT_TIMESTAMP),
16 | ('SpringBoot Integration Testing using TestContainers Starter','https://sivalabs.in/spring-boot-integration-testing-using-testcontainers-starter', CURRENT_TIMESTAMP),
17 | ('Creating Yeoman based SpringBoot Generator','https://sivalabs.in/creating-yeoman-based-springboot-generator', CURRENT_TIMESTAMP),
18 | ('Testing REST APIs using Postman and Newman','https://sivalabs.in/testing-rest-apis-with-postman-newman', CURRENT_TIMESTAMP),
19 | ('Testing SpringBoot Applications','https://sivalabs.in/spring-boot-testing', CURRENT_TIMESTAMP)
20 | ;
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/src/main/resources/schema.sql:
--------------------------------------------------------------------------------
1 | create table if not exists bookmarks
2 | (
3 | id bigserial not null,
4 | title varchar not null,
5 | url varchar not null,
6 | created_at timestamp,
7 | primary key (id)
8 | );
9 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/src/test/java/com/sivalabs/bookmarks/domain/BookmarkRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import org.junit.jupiter.api.BeforeEach;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
7 | import org.springframework.jdbc.core.JdbcTemplate;
8 | import org.springframework.test.context.jdbc.Sql;
9 |
10 | import java.time.Instant;
11 | import java.util.List;
12 | import java.util.Optional;
13 |
14 | import static org.assertj.core.api.Assertions.assertThat;
15 |
16 | @JdbcTest(properties = {
17 | "spring.test.database.replace=none",
18 | "spring.datasource.url=jdbc:tc:postgresql:15.2-alpine:///db"
19 | })
20 | @Sql("/test-data.sql")
21 | class BookmarkRepositoryTest {
22 |
23 | @Autowired
24 | JdbcTemplate jdbcTemplate;
25 |
26 | BookmarkRepository bookmarkRepository;
27 |
28 | @BeforeEach
29 | void setUp() {
30 | bookmarkRepository = new BookmarkRepository(jdbcTemplate);
31 | }
32 |
33 | @Test
34 | void shouldFindAllBookmarks() {
35 | List bookmarks = bookmarkRepository.findAll();
36 | assertThat(bookmarks).isNotEmpty();
37 | assertThat(bookmarks).hasSize(15);
38 | }
39 |
40 | @Test
41 | void shouldCreateBookmark() {
42 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
43 | Long id = bookmarkRepository.save(bookmark);
44 | assertThat(id).isNotNull();
45 | }
46 |
47 | @Test
48 | void shouldGetBookmarkById() {
49 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
50 | Long id = bookmarkRepository.save(bookmark);
51 |
52 | Optional bookmarkOptional = bookmarkRepository.findById(id);
53 | assertThat(bookmarkOptional).isPresent();
54 | }
55 |
56 | @Test
57 | void shouldEmptyWhenBookmarkNotFound() {
58 | Optional bookmarkOptional = bookmarkRepository.findById(9999L);
59 | assertThat(bookmarkOptional).isEmpty();
60 | }
61 |
62 | @Test
63 | void shouldUpdateBookmark() {
64 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
65 | Long id = bookmarkRepository.save(bookmark);
66 |
67 | Bookmark updatedBookmark = new Bookmark(id, "My Updated Title", "https://www.sivalabs.in", bookmark.createdAt());
68 | bookmarkRepository.update(updatedBookmark);
69 |
70 | updatedBookmark = bookmarkRepository.findById(id).orElseThrow();
71 | assertThat(updatedBookmark.id()).isEqualTo(id);
72 | assertThat(updatedBookmark.title()).isEqualTo("My Updated Title");
73 | assertThat(updatedBookmark.url()).isEqualTo("https://www.sivalabs.in");
74 | }
75 |
76 | @Test
77 | void shouldDeleteBookmark() {
78 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
79 | Long id = bookmarkRepository.save(bookmark);
80 |
81 | bookmarkRepository.delete(id);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/spring-boot-jdbc-tutorial/src/test/resources/test-data.sql:
--------------------------------------------------------------------------------
1 | truncate table bookmarks;
2 | ALTER SEQUENCE bookmarks_id_seq RESTART WITH 1;
3 |
4 | insert into bookmarks(title, url, created_at) values
5 | ('How (not) to ask for Technical Help?','https://sivalabs.in/how-to-not-to-ask-for-technical-help', CURRENT_TIMESTAMP),
6 | ('Announcing My SpringBoot Tips Video Series on YouTube','https://sivalabs.in/announcing-my-springboot-tips-video-series', CURRENT_TIMESTAMP),
7 | ('Kubernetes - Exposing Services to outside of Cluster using Ingress','https://sivalabs.in/kubernetes-ingress', CURRENT_TIMESTAMP),
8 | ('Kubernetes - Blue/Green Deployments','https://sivalabs.in/kubernetes-blue-green-deployments', CURRENT_TIMESTAMP),
9 | ('Kubernetes - Releasing a new version of the application using Deployment Rolling Updates','https://sivalabs.in/kubernetes-deployment-rolling-updates', CURRENT_TIMESTAMP),
10 | ('Getting Started with Kubernetes','https://sivalabs.in/getting-started-with-kubernetes', CURRENT_TIMESTAMP),
11 | ('Get Super Productive with Intellij File Templates','https://sivalabs.in/get-super-productive-with-intellij-file-templates', CURRENT_TIMESTAMP),
12 | ('Few Things I learned in the HardWay in 15 years of my career','https://sivalabs.in/few-things-i-learned-the-hardway-in-15-years-of-my-career', CURRENT_TIMESTAMP),
13 | ('All the resources you ever need as a Java & Spring application developer','https://sivalabs.in/all-the-resources-you-ever-need-as-a-java-spring-application-developer', CURRENT_TIMESTAMP),
14 | ('GoLang from a Java developer perspective','https://sivalabs.in/golang-from-a-java-developer-perspective', CURRENT_TIMESTAMP),
15 | ('Imposing Code Structure Guidelines using ArchUnit','https://sivalabs.in/impose-architecture-guidelines-using-archunit', CURRENT_TIMESTAMP),
16 | ('SpringBoot Integration Testing using TestContainers Starter','https://sivalabs.in/spring-boot-integration-testing-using-testcontainers-starter', CURRENT_TIMESTAMP),
17 | ('Creating Yeoman based SpringBoot Generator','https://sivalabs.in/creating-yeoman-based-springboot-generator', CURRENT_TIMESTAMP),
18 | ('Testing REST APIs using Postman and Newman','https://sivalabs.in/testing-rest-apis-with-postman-newman', CURRENT_TIMESTAMP),
19 | ('Testing SpringBoot Applications','https://sivalabs.in/spring-boot-testing', CURRENT_TIMESTAMP)
20 | ;
--------------------------------------------------------------------------------
/spring-boot-jdbcclient-tutorial/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | 4.0.0
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.2.0-M2
11 |
12 |
13 | com.sivalabs
14 | spring-boot-jdbcclient-tutorial
15 | 0.0.1
16 | spring-boot-jdbcclient-tutorial
17 |
18 |
19 | 17
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-jdbc
26 |
27 |
28 | org.flywaydb
29 | flyway-core
30 |
31 |
32 | org.postgresql
33 | postgresql
34 | runtime
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-test
40 | test
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-testcontainers
45 | test
46 |
47 |
48 | org.testcontainers
49 | junit-jupiter
50 | test
51 |
52 |
53 | org.testcontainers
54 | postgresql
55 | test
56 |
57 |
58 |
59 |
60 |
61 |
62 | org.springframework.boot
63 | spring-boot-maven-plugin
64 |
65 |
66 |
67 |
68 |
69 | spring-milestones
70 | Spring Milestones
71 | https://repo.spring.io/milestone
72 |
73 | false
74 |
75 |
76 |
77 | spring-snapshots
78 | Spring Snapshots
79 | https://repo.spring.io/snapshot
80 |
81 | false
82 |
83 |
84 |
85 |
86 |
87 | spring-milestones
88 | Spring Milestones
89 | https://repo.spring.io/milestone
90 |
91 | false
92 |
93 |
94 |
95 | spring-snapshots
96 | Spring Snapshots
97 | https://repo.spring.io/snapshot
98 |
99 | false
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/spring-boot-jdbcclient-tutorial/src/main/java/com/sivalabs/bookmarks/Application.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/spring-boot-jdbcclient-tutorial/src/main/java/com/sivalabs/bookmarks/domain/Bookmark.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import java.time.Instant;
4 |
5 | public record Bookmark(
6 | Long id,
7 | String title,
8 | String url,
9 | Instant createdAt) {}
10 |
--------------------------------------------------------------------------------
/spring-boot-jdbcclient-tutorial/src/main/java/com/sivalabs/bookmarks/domain/BookmarkRepository.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import org.springframework.jdbc.core.RowMapper;
4 | import org.springframework.jdbc.core.simple.JdbcClient;
5 | import org.springframework.jdbc.support.GeneratedKeyHolder;
6 | import org.springframework.jdbc.support.KeyHolder;
7 | import org.springframework.stereotype.Repository;
8 |
9 | import java.sql.ResultSet;
10 | import java.sql.SQLException;
11 | import java.sql.Timestamp;
12 | import java.util.List;
13 | import java.util.Optional;
14 |
15 | import static com.sivalabs.bookmarks.domain.BookmarkRepository.BookmarkRowMapper.INSTANCE;
16 |
17 | @Repository
18 | public class BookmarkRepository {
19 | private final JdbcClient jdbcClient;
20 |
21 | public BookmarkRepository(JdbcClient jdbcClient) {
22 | this.jdbcClient = jdbcClient;
23 | }
24 |
25 | public List findAll() {
26 | String sql = "select id, title, url, created_at from bookmarks";
27 | return jdbcClient.sql(sql).query(INSTANCE).list();
28 | }
29 |
30 | public Optional findById(Long id) {
31 | String sql = "select id, title, url, created_at from bookmarks where id = :id";
32 | return jdbcClient.sql(sql).param("id", id).query(INSTANCE).optional();
33 | }
34 |
35 | public Long save(Bookmark bookmark) {
36 | String sql = "insert into bookmarks(title, url, created_at) values(:title,:url,:createdAt) returning id";
37 | KeyHolder keyHolder = new GeneratedKeyHolder();
38 | jdbcClient.sql(sql)
39 | .param("title", bookmark.title())
40 | .param("url", bookmark.url())
41 | .param("createdAt", Timestamp.from(bookmark.createdAt()))
42 | .update(keyHolder);
43 | return keyHolder.getKeyAs(Long.class);
44 | }
45 |
46 | public void update(Bookmark bookmark) {
47 | String sql = "update bookmarks set title = ?, url = ? where id = ?";
48 | int count = jdbcClient.sql(sql)
49 | .param(1, bookmark.title())
50 | .param(2, bookmark.url())
51 | .param(3, bookmark.id())
52 | .update();
53 | if (count == 0) {
54 | throw new RuntimeException("Bookmark not found");
55 | }
56 | }
57 |
58 | public void delete(Long id) {
59 | String sql = "delete from bookmarks where id = ?";
60 | int count = jdbcClient.sql(sql).param(1, id).update();
61 | if (count == 0) {
62 | throw new RuntimeException("Bookmark not found");
63 | }
64 | }
65 |
66 | static class BookmarkRowMapper implements RowMapper {
67 | public static final BookmarkRowMapper INSTANCE = new BookmarkRowMapper();
68 | private BookmarkRowMapper(){}
69 |
70 | @Override
71 | public Bookmark mapRow(ResultSet rs, int rowNum) throws SQLException {
72 | return new Bookmark(
73 | rs.getLong("id"),
74 | rs.getString("title"),
75 | rs.getString("url"),
76 | rs.getTimestamp("created_at").toInstant()
77 | );
78 | }
79 | }
80 | }
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/spring-boot-jdbcclient-tutorial/src/main/resources/application.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-tutorials-blog-series/4e04cc2e628e0728234b949f7b341bad829e6be9/spring-boot-jdbcclient-tutorial/src/main/resources/application.properties
--------------------------------------------------------------------------------
/spring-boot-jdbcclient-tutorial/src/main/resources/db/migration/V1__create_tables.sql:
--------------------------------------------------------------------------------
1 | create table bookmarks
2 | (
3 | id bigserial primary key,
4 | title varchar not null,
5 | url varchar not null,
6 | created_at timestamp
7 | );
8 |
--------------------------------------------------------------------------------
/spring-boot-jdbcclient-tutorial/src/test/java/com/sivalabs/bookmarks/domain/BookmarkRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import org.junit.jupiter.api.BeforeEach;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
7 | import org.springframework.boot.autoconfigure.jdbc.JdbcClientAutoConfiguration;
8 | import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
9 | import org.springframework.jdbc.core.simple.JdbcClient;
10 | import org.springframework.test.context.jdbc.Sql;
11 |
12 | import java.time.Instant;
13 | import java.util.List;
14 | import java.util.Optional;
15 |
16 | import static org.assertj.core.api.Assertions.assertThat;
17 |
18 | @JdbcTest(properties = {
19 | "spring.test.database.replace=none",
20 | "spring.datasource.url=jdbc:tc:postgresql:15.4-alpine:///db"
21 | })
22 | @ImportAutoConfiguration(JdbcClientAutoConfiguration.class)
23 | @Sql("/test-data.sql")
24 | class BookmarkRepositoryTest {
25 |
26 | @Autowired
27 | JdbcClient jdbcClient;
28 |
29 | BookmarkRepository bookmarkRepository;
30 |
31 | @BeforeEach
32 | void setUp() {
33 | bookmarkRepository = new BookmarkRepository(jdbcClient);
34 | }
35 |
36 | @Test
37 | void shouldFindAllBookmarks() {
38 | List bookmarks = bookmarkRepository.findAll();
39 | assertThat(bookmarks).isNotEmpty();
40 | assertThat(bookmarks).hasSize(15);
41 | }
42 |
43 | @Test
44 | void shouldCreateBookmark() {
45 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
46 | Long id = bookmarkRepository.save(bookmark);
47 | assertThat(id).isNotNull();
48 | }
49 |
50 | @Test
51 | void shouldGetBookmarkById() {
52 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
53 | Long id = bookmarkRepository.save(bookmark);
54 |
55 | Optional bookmarkOptional = bookmarkRepository.findById(id);
56 | assertThat(bookmarkOptional).isPresent();
57 | assertThat(bookmarkOptional.get().id()).isEqualTo(id);
58 | assertThat(bookmarkOptional.get().title()).isEqualTo(bookmark.title());
59 | assertThat(bookmarkOptional.get().url()).isEqualTo(bookmark.url());
60 | }
61 |
62 | @Test
63 | void shouldEmptyWhenBookmarkNotFound() {
64 | Optional bookmarkOptional = bookmarkRepository.findById(9999L);
65 | assertThat(bookmarkOptional).isEmpty();
66 | }
67 |
68 | @Test
69 | void shouldUpdateBookmark() {
70 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
71 | Long id = bookmarkRepository.save(bookmark);
72 |
73 | Bookmark changedBookmark = new Bookmark(id, "My Updated Title", "https://www.sivalabs.in", bookmark.createdAt());
74 | bookmarkRepository.update(changedBookmark);
75 |
76 | Bookmark updatedBookmark = bookmarkRepository.findById(id).orElseThrow();
77 | assertThat(updatedBookmark.id()).isEqualTo(changedBookmark.id());
78 | assertThat(updatedBookmark.title()).isEqualTo(changedBookmark.title());
79 | assertThat(updatedBookmark.url()).isEqualTo(changedBookmark.url());
80 | }
81 |
82 | @Test
83 | void shouldDeleteBookmark() {
84 | Bookmark bookmark = new Bookmark(null, "My Title", "https://sivalabs.in", Instant.now());
85 | Long id = bookmarkRepository.save(bookmark);
86 |
87 | bookmarkRepository.delete(id);
88 |
89 | Optional optionalBookmark = bookmarkRepository.findById(id);
90 | assertThat(optionalBookmark).isEmpty();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/spring-boot-jdbcclient-tutorial/src/test/resources/test-data.sql:
--------------------------------------------------------------------------------
1 | truncate table bookmarks;
2 | ALTER SEQUENCE bookmarks_id_seq RESTART WITH 1;
3 |
4 | insert into bookmarks(title, url, created_at) values
5 | ('How (not) to ask for Technical Help?','https://sivalabs.in/how-to-not-to-ask-for-technical-help', CURRENT_TIMESTAMP),
6 | ('Announcing My SpringBoot Tips Video Series on YouTube','https://sivalabs.in/announcing-my-springboot-tips-video-series', CURRENT_TIMESTAMP),
7 | ('Kubernetes - Exposing Services to outside of Cluster using Ingress','https://sivalabs.in/kubernetes-ingress', CURRENT_TIMESTAMP),
8 | ('Kubernetes - Blue/Green Deployments','https://sivalabs.in/kubernetes-blue-green-deployments', CURRENT_TIMESTAMP),
9 | ('Kubernetes - Releasing a new version of the application using Deployment Rolling Updates','https://sivalabs.in/kubernetes-deployment-rolling-updates', CURRENT_TIMESTAMP),
10 | ('Getting Started with Kubernetes','https://sivalabs.in/getting-started-with-kubernetes', CURRENT_TIMESTAMP),
11 | ('Get Super Productive with Intellij File Templates','https://sivalabs.in/get-super-productive-with-intellij-file-templates', CURRENT_TIMESTAMP),
12 | ('Few Things I learned in the HardWay in 15 years of my career','https://sivalabs.in/few-things-i-learned-the-hardway-in-15-years-of-my-career', CURRENT_TIMESTAMP),
13 | ('All the resources you ever need as a Java & Spring application developer','https://sivalabs.in/all-the-resources-you-ever-need-as-a-java-spring-application-developer', CURRENT_TIMESTAMP),
14 | ('GoLang from a Java developer perspective','https://sivalabs.in/golang-from-a-java-developer-perspective', CURRENT_TIMESTAMP),
15 | ('Imposing Code Structure Guidelines using ArchUnit','https://sivalabs.in/impose-architecture-guidelines-using-archunit', CURRENT_TIMESTAMP),
16 | ('SpringBoot Integration Testing using TestContainers Starter','https://sivalabs.in/spring-boot-integration-testing-using-testcontainers-starter', CURRENT_TIMESTAMP),
17 | ('Creating Yeoman based SpringBoot Generator','https://sivalabs.in/creating-yeoman-based-springboot-generator', CURRENT_TIMESTAMP),
18 | ('Testing REST APIs using Postman and Newman','https://sivalabs.in/testing-rest-apis-with-postman-newman', CURRENT_TIMESTAMP),
19 | ('Testing SpringBoot Applications','https://sivalabs.in/spring-boot-testing', CURRENT_TIMESTAMP)
20 | ;
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.1.2
9 |
10 |
11 | com.sivalabs
12 | spring-boot-rest-api-tutorial
13 | 0.0.1-SNAPSHOT
14 | spring-boot-rest-api-tutorial
15 | spring-boot-rest-api-tutorial
16 |
17 | 17
18 | 4.2.0
19 |
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-starter-data-jpa
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-validation
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-web
32 |
33 |
34 | io.github.wimdeblauwe
35 | error-handling-spring-boot-starter
36 | ${error-handling-spring-boot-starter.version}
37 |
38 |
39 | org.flywaydb
40 | flyway-core
41 |
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-devtools
46 | runtime
47 | true
48 |
49 |
50 | org.postgresql
51 | postgresql
52 | runtime
53 |
54 |
55 | org.springframework.boot
56 | spring-boot-starter-test
57 | test
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-testcontainers
62 | test
63 |
64 |
65 | org.testcontainers
66 | junit-jupiter
67 | test
68 |
69 |
70 | org.testcontainers
71 | postgresql
72 | test
73 |
74 |
75 | io.rest-assured
76 | rest-assured
77 | test
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | org.springframework.boot
86 | spring-boot-maven-plugin
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/Application.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/api/GlobalExceptionHandler.java:
--------------------------------------------------------------------------------
1 | //Commented as we are using "error-handling-spring-boot-starter" library.
2 | /*
3 | package com.sivalabs.bookmarks.api;
4 |
5 | import com.sivalabs.bookmarks.domain.BookmarkNotFoundException;
6 | import org.springframework.http.HttpStatus;
7 | import org.springframework.http.ProblemDetail;
8 | import org.springframework.web.bind.annotation.ExceptionHandler;
9 | import org.springframework.web.bind.annotation.RestControllerAdvice;
10 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
11 |
12 | import java.net.URI;
13 | import java.time.Instant;
14 |
15 | @RestControllerAdvice
16 | public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
17 |
18 | @ExceptionHandler(BookmarkNotFoundException.class)
19 | ProblemDetail handleBookmarkNotFoundException(BookmarkNotFoundException e) {
20 | ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage());
21 | problemDetail.setTitle("Bookmark Not Found");
22 | problemDetail.setType(URI.create("https://api.bookmarks.com/errors/not-found"));
23 | problemDetail.setProperty("errorCategory", "Generic");
24 | problemDetail.setProperty("timestamp", Instant.now());
25 | return problemDetail;
26 | }
27 | }
28 | */
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/api/controllers/BookmarkController.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.api.controllers;
2 |
3 | import com.sivalabs.bookmarks.api.models.CreateBookmarkRequest;
4 | import com.sivalabs.bookmarks.api.models.UpdateBookmarkRequest;
5 | import com.sivalabs.bookmarks.domain.CreateBookmarkCommand;
6 | import com.sivalabs.bookmarks.domain.FindBookmarksQuery;
7 | import com.sivalabs.bookmarks.domain.BookmarkDTO;
8 | import com.sivalabs.bookmarks.domain.UpdateBookmarkCommand;
9 | import com.sivalabs.bookmarks.domain.PagedResult;
10 | import com.sivalabs.bookmarks.domain.BookmarkService;
11 | import org.springframework.http.ResponseEntity;
12 | import org.springframework.validation.annotation.Validated;
13 | import org.springframework.web.bind.annotation.DeleteMapping;
14 | import org.springframework.web.bind.annotation.GetMapping;
15 | import org.springframework.web.bind.annotation.PathVariable;
16 | import org.springframework.web.bind.annotation.PostMapping;
17 | import org.springframework.web.bind.annotation.PutMapping;
18 | import org.springframework.web.bind.annotation.RequestBody;
19 | import org.springframework.web.bind.annotation.RequestMapping;
20 | import org.springframework.web.bind.annotation.RequestParam;
21 | import org.springframework.web.bind.annotation.RestController;
22 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
23 |
24 | import java.net.URI;
25 |
26 | @RestController
27 | @RequestMapping("/api/bookmarks")
28 | class BookmarkController {
29 | private final BookmarkService bookmarkService;
30 |
31 | BookmarkController(BookmarkService bookmarkService) {
32 | this.bookmarkService = bookmarkService;
33 | }
34 |
35 | @GetMapping
36 | PagedResult findBookmarks(
37 | @RequestParam(name = "page", defaultValue = "1") Integer pageNo,
38 | @RequestParam(name = "size", defaultValue = "10") Integer pageSize) {
39 | FindBookmarksQuery query = new FindBookmarksQuery(pageNo, pageSize);
40 | return bookmarkService.findBookmarks(query);
41 | }
42 |
43 | @PostMapping
44 | ResponseEntity create(@RequestBody @Validated CreateBookmarkRequest request) {
45 | CreateBookmarkCommand cmd = new CreateBookmarkCommand(
46 | request.title(),
47 | request.url()
48 | );
49 | BookmarkDTO bookmark = bookmarkService.create(cmd);
50 | URI location = ServletUriComponentsBuilder
51 | .fromCurrentRequest()
52 | .path("/api/bookmarks/{id}")
53 | .buildAndExpand(bookmark.id()).toUri();
54 | return ResponseEntity.created(location).body(bookmark);
55 | }
56 |
57 | @PutMapping("/{id}")
58 | void update(@PathVariable(name = "id") Long id,
59 | @RequestBody @Validated UpdateBookmarkRequest request) {
60 | UpdateBookmarkCommand cmd = new UpdateBookmarkCommand(id, request.title(), request.url());
61 | bookmarkService.update(cmd);
62 | }
63 |
64 | @GetMapping("/{id}")
65 | ResponseEntity findById(@PathVariable(name = "id") Long id) {
66 | return bookmarkService.findById(id)
67 | .map(ResponseEntity::ok)
68 | .orElseGet(() -> ResponseEntity.notFound().build());
69 | }
70 |
71 | @DeleteMapping("/{id}")
72 | void delete(@PathVariable(name = "id") Long id) {
73 | bookmarkService.delete(id);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/api/models/CreateBookmarkRequest.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.api.models;
2 |
3 | import jakarta.validation.constraints.NotEmpty;
4 |
5 | public record CreateBookmarkRequest(
6 | @NotEmpty(message = "Title is required")
7 | String title,
8 | @NotEmpty(message = "URL is required")
9 | String url) {
10 | }
11 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/api/models/UpdateBookmarkRequest.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.api.models;
2 |
3 | import jakarta.validation.constraints.NotEmpty;
4 |
5 | public record UpdateBookmarkRequest(
6 | @NotEmpty(message = "Title is required")
7 | String title,
8 | @NotEmpty(message = "URL is required")
9 | String url) {
10 | }
11 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/Bookmark.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import jakarta.persistence.Column;
4 | import jakarta.persistence.Entity;
5 | import jakarta.persistence.GeneratedValue;
6 | import jakarta.persistence.GenerationType;
7 | import jakarta.persistence.Id;
8 | import jakarta.persistence.Table;
9 | import org.springframework.data.annotation.CreatedDate;
10 | import org.springframework.data.annotation.LastModifiedDate;
11 |
12 | import java.time.Instant;
13 |
14 | @Entity
15 | @Table(name = "bookmarks")
16 | class Bookmark {
17 | @Id
18 | @GeneratedValue(strategy = GenerationType.IDENTITY)
19 | private Long id;
20 | @Column(nullable = false)
21 | private String title;
22 | @Column(nullable = false)
23 | private String url;
24 | @Column(name = "created_at", nullable = false, updatable = false)
25 | @CreatedDate
26 | private Instant createdAt;
27 | @Column(name = "updated_at", insertable = false)
28 | @LastModifiedDate
29 | private Instant updatedAt;
30 |
31 | public Bookmark() {}
32 |
33 | public Bookmark(String title, String url) {
34 | this.title = title;
35 | this.url = url;
36 | this.createdAt = Instant.now();
37 | }
38 |
39 | public Bookmark(Long id, String title, String url, Instant createdAt, Instant updatedAt) {
40 | this.id = id;
41 | this.title = title;
42 | this.url = url;
43 | this.createdAt = createdAt;
44 | this.updatedAt = updatedAt;
45 | }
46 |
47 | public Long getId() {
48 | return id;
49 | }
50 |
51 | public void setId(Long id) {
52 | this.id = id;
53 | }
54 |
55 | public String getTitle() {
56 | return title;
57 | }
58 |
59 | public void setTitle(String title) {
60 | this.title = title;
61 | }
62 |
63 | public String getUrl() {
64 | return url;
65 | }
66 |
67 | public void setUrl(String url) {
68 | this.url = url;
69 | }
70 |
71 | public Instant getCreatedAt() {
72 | return createdAt;
73 | }
74 |
75 | public void setCreatedAt(Instant createdAt) {
76 | this.createdAt = createdAt;
77 | }
78 |
79 | public Instant getUpdatedAt() {
80 | return updatedAt;
81 | }
82 |
83 | public void setUpdatedAt(Instant updatedAt) {
84 | this.updatedAt = updatedAt;
85 | }
86 | }
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/BookmarkDTO.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import java.time.Instant;
4 |
5 | public record BookmarkDTO(
6 | Long id,
7 | String title,
8 | String url,
9 | Instant createdAt
10 | ) {
11 | static BookmarkDTO from(Bookmark bookmark) {
12 | return new BookmarkDTO(bookmark.getId(),
13 | bookmark.getTitle(),
14 | bookmark.getUrl(),
15 | bookmark.getCreatedAt()
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/BookmarkNotFoundException.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | public class BookmarkNotFoundException extends RuntimeException {
4 | public BookmarkNotFoundException(Long id) {
5 | super(String.format("Bookmark with id=%d not found", id));
6 | }
7 |
8 | public static BookmarkNotFoundException of(Long id) {
9 | return new BookmarkNotFoundException(id);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/BookmarkRepository.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import org.springframework.data.domain.Page;
4 | import org.springframework.data.domain.Pageable;
5 | import org.springframework.data.jpa.repository.JpaRepository;
6 | import org.springframework.data.jpa.repository.Query;
7 |
8 | import java.util.Optional;
9 |
10 | interface BookmarkRepository extends JpaRepository {
11 |
12 | @Query("""
13 | SELECT
14 | new com.sivalabs.bookmarks.domain.BookmarkDTO(b.id, b.title, b.url, b.createdAt)
15 | FROM Bookmark b
16 | """)
17 | Page findBookmarks(Pageable pageable);
18 |
19 | @Query("""
20 | SELECT
21 | new com.sivalabs.bookmarks.domain.BookmarkDTO(b.id, b.title, b.url, b.createdAt)
22 | FROM Bookmark b
23 | WHERE b.id = ?1
24 | """)
25 | Optional findBookmarkById(Long id);
26 | }
27 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/BookmarkService.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import org.springframework.data.domain.Page;
4 | import org.springframework.data.domain.PageRequest;
5 | import org.springframework.data.domain.Pageable;
6 | import org.springframework.data.domain.Sort;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.transaction.annotation.Transactional;
9 |
10 | import java.time.Instant;
11 | import java.util.Optional;
12 |
13 | @Service
14 | @Transactional(readOnly = true)
15 | public class BookmarkService {
16 | private final BookmarkRepository repo;
17 |
18 | BookmarkService(BookmarkRepository repo) {
19 | this.repo = repo;
20 | }
21 |
22 | public PagedResult findBookmarks(FindBookmarksQuery query) {
23 | Sort sort = Sort.by(Sort.Direction.DESC, "createdAt");
24 | int pageNo = query.pageNo() > 0 ? query.pageNo() - 1 : 0;
25 | Pageable pageable = PageRequest.of(pageNo, query.pageSize(), sort);
26 | Page page = repo.findBookmarks(pageable);
27 | return new PagedResult<>(
28 | page.getContent(),
29 | page.getTotalElements(),
30 | page.getNumber() + 1,
31 | page.getTotalPages(),
32 | page.isFirst(),
33 | page.isLast(),
34 | page.hasNext(),
35 | page.hasPrevious()
36 | );
37 | }
38 |
39 | public Optional findById(Long id) {
40 | return repo.findBookmarkById(id);
41 | }
42 |
43 | @Transactional
44 | public BookmarkDTO create(CreateBookmarkCommand cmd) {
45 | Bookmark bookmark = new Bookmark();
46 | bookmark.setTitle(cmd.title());
47 | bookmark.setUrl(cmd.url());
48 | bookmark.setCreatedAt(Instant.now());
49 | return BookmarkDTO.from(repo.save(bookmark));
50 | }
51 |
52 | @Transactional
53 | public void update(UpdateBookmarkCommand cmd) {
54 | Bookmark bookmark = repo.findById(cmd.id())
55 | .orElseThrow(()-> BookmarkNotFoundException.of(cmd.id()));
56 | bookmark.setTitle(cmd.title());
57 | bookmark.setUrl(cmd.url());
58 | bookmark.setUpdatedAt(Instant.now());
59 | repo.save(bookmark);
60 | }
61 |
62 | @Transactional
63 | public void delete(Long postId) {
64 | Bookmark entity = repo.findById(postId)
65 | .orElseThrow(()-> BookmarkNotFoundException.of(postId));
66 | repo.delete(entity);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/CreateBookmarkCommand.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | public record CreateBookmarkCommand(String title, String url) {}
4 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/FindBookmarksQuery.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | public record FindBookmarksQuery(int pageNo, int pageSize) {
4 | }
5 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/PagedResult.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import java.util.List;
5 |
6 | public record PagedResult(
7 | List data,
8 | long totalElements,
9 | int pageNumber,
10 | int totalPages,
11 | @JsonProperty("isFirst") boolean isFirst,
12 | @JsonProperty("isLast") boolean isLast,
13 | @JsonProperty("hasNext") boolean hasNext,
14 | @JsonProperty("hasPrevious") boolean hasPrevious) {}
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/java/com/sivalabs/bookmarks/domain/UpdateBookmarkCommand.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.domain;
2 |
3 | public record UpdateBookmarkCommand(
4 | Long id,
5 | String title,
6 | String url) {
7 | }
8 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.jpa.show-sql=true
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/main/resources/db/migration/V1__init.sql:
--------------------------------------------------------------------------------
1 | create table bookmarks
2 | (
3 | id bigserial primary key,
4 | title varchar not null,
5 | url varchar not null,
6 | created_at timestamp,
7 | updated_at timestamp
8 | );
9 |
10 | INSERT INTO bookmarks(title, url, created_at) VALUES
11 | ('How (not) to ask for Technical Help?','https://sivalabs.in/how-to-not-to-ask-for-technical-help', CURRENT_TIMESTAMP),
12 | ('Announcing My SpringBoot Tips Video Series on YouTube','https://sivalabs.in/announcing-my-springboot-tips-video-series', CURRENT_TIMESTAMP),
13 | ('Kubernetes - Exposing Services to outside of Cluster using Ingress','https://sivalabs.in/kubernetes-ingress', CURRENT_TIMESTAMP),
14 | ('Kubernetes - Blue/Green Deployments','https://sivalabs.in/kubernetes-blue-green-deployments', CURRENT_TIMESTAMP),
15 | ('Kubernetes - Releasing a new version of the application using Deployment Rolling Updates','https://sivalabs.in/kubernetes-deployment-rolling-updates', CURRENT_TIMESTAMP),
16 | ('Getting Started with Kubernetes','https://sivalabs.in/getting-started-with-kubernetes', CURRENT_TIMESTAMP),
17 | ('Get Super Productive with Intellij File Templates','https://sivalabs.in/get-super-productive-with-intellij-file-templates', CURRENT_TIMESTAMP),
18 | ('Few Things I learned in the HardWay in 15 years of my career','https://sivalabs.in/few-things-i-learned-the-hardway-in-15-years-of-my-career', CURRENT_TIMESTAMP),
19 | ('All the resources you ever need as a Java & Spring application developer','https://sivalabs.in/all-the-resources-you-ever-need-as-a-java-spring-application-developer', CURRENT_TIMESTAMP),
20 | ('GoLang from a Java developer perspective','https://sivalabs.in/golang-from-a-java-developer-perspective', CURRENT_TIMESTAMP),
21 | ('Imposing Code Structure Guidelines using ArchUnit','https://sivalabs.in/impose-architecture-guidelines-using-archunit', CURRENT_TIMESTAMP),
22 | ('SpringBoot Integration Testing using TestContainers Starter','https://sivalabs.in/spring-boot-integration-testing-using-testcontainers-starter', CURRENT_TIMESTAMP),
23 | ('Creating Yeoman based SpringBoot Generator','https://sivalabs.in/creating-yeoman-based-springboot-generator', CURRENT_TIMESTAMP),
24 | ('Testing REST APIs using Postman and Newman','https://sivalabs.in/testing-rest-apis-with-postman-newman', CURRENT_TIMESTAMP),
25 | ('Testing SpringBoot Applications','https://sivalabs.in/spring-boot-testing', CURRENT_TIMESTAMP)
26 | ;
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/test/java/com/sivalabs/bookmarks/ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 | import org.springframework.context.annotation.Import;
6 |
7 | @SpringBootTest
8 | @Import(ContainersConfig.class)
9 | class ApplicationTests {
10 |
11 | @Test
12 | void contextLoads() {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/test/java/com/sivalabs/bookmarks/ContainersConfig.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks;
2 |
3 | import org.springframework.boot.test.context.TestConfiguration;
4 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
5 | import org.springframework.context.annotation.Bean;
6 | import org.testcontainers.containers.PostgreSQLContainer;
7 | import org.testcontainers.utility.DockerImageName;
8 |
9 | @TestConfiguration(proxyBeanMethods = false)
10 | public class ContainersConfig {
11 |
12 | @Bean
13 | @ServiceConnection
14 | PostgreSQLContainer> postgresContainer() {
15 | return new PostgreSQLContainer<>(DockerImageName.parse("postgres:15.4-alpine"));
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/test/java/com/sivalabs/bookmarks/TestApplication.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks;
2 |
3 | import org.springframework.boot.SpringApplication;
4 |
5 | public class TestApplication {
6 |
7 | public static void main(String[] args) {
8 | SpringApplication
9 | .from(Application::main)
10 | .with(ContainersConfig.class)
11 | .run(args);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/test/java/com/sivalabs/bookmarks/api/controllers/BookmarkControllerTests.java:
--------------------------------------------------------------------------------
1 | package com.sivalabs.bookmarks.api.controllers;
2 |
3 | import com.sivalabs.bookmarks.domain.BookmarkDTO;
4 | import com.sivalabs.bookmarks.domain.BookmarkService;
5 | import com.sivalabs.bookmarks.domain.CreateBookmarkCommand;
6 | import io.restassured.RestAssured;
7 | import io.restassured.http.ContentType;
8 | import org.junit.jupiter.api.BeforeEach;
9 | import org.junit.jupiter.api.Test;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.boot.test.context.SpringBootTest;
12 | import org.springframework.boot.test.web.server.LocalServerPort;
13 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
14 | import org.springframework.test.context.jdbc.Sql;
15 | import org.testcontainers.containers.PostgreSQLContainer;
16 | import org.testcontainers.junit.jupiter.Container;
17 | import org.testcontainers.junit.jupiter.Testcontainers;
18 | import org.testcontainers.utility.DockerImageName;
19 |
20 | import java.util.Optional;
21 |
22 | import static io.restassured.RestAssured.given;
23 | import static org.assertj.core.api.Assertions.assertThat;
24 | import static org.hamcrest.Matchers.equalTo;
25 | import static org.hamcrest.Matchers.matchesRegex;
26 | import static org.hamcrest.Matchers.notNullValue;
27 | import static org.hamcrest.Matchers.nullValue;
28 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
29 |
30 | @SpringBootTest(webEnvironment = RANDOM_PORT)
31 | @Testcontainers
32 | class BookmarkControllerTests {
33 |
34 | @Container
35 | @ServiceConnection
36 | static PostgreSQLContainer> postgres = new PostgreSQLContainer<>(DockerImageName.parse("postgres:15.4-alpine"));
37 |
38 | @LocalServerPort
39 | private Integer port;
40 |
41 | @Autowired
42 | private BookmarkService bookmarkService;
43 |
44 | @BeforeEach
45 | void setUp() {
46 | RestAssured.port = port;
47 | }
48 |
49 | @Test
50 | @Sql("/test-data.sql")
51 | void shouldGetBookmarksByPage() {
52 | given().contentType(ContentType.JSON)
53 | .when()
54 | .get("/api/bookmarks?page=1&size=10")
55 | .then()
56 | .statusCode(200)
57 | .body("data.size()", equalTo(10))
58 | .body("totalElements", equalTo(15))
59 | .body("pageNumber", equalTo(1))
60 | .body("totalPages", equalTo(2))
61 | .body("isFirst", equalTo(true))
62 | .body("isLast", equalTo(false))
63 | .body("hasNext", equalTo(true))
64 | .body("hasPrevious", equalTo(false));
65 | }
66 |
67 | @Test
68 | void shouldCreateBookmarkSuccessfully() {
69 | given().contentType(ContentType.JSON)
70 | .body(
71 | """
72 | {
73 | "title": "SivaLabs blog",
74 | "url": "https://sivalabs.in"
75 | }
76 | """)
77 | .when()
78 | .post("/api/bookmarks")
79 | .then()
80 | .statusCode(201)
81 | .header("Location", matchesRegex(".*/api/bookmarks/[0-9]+$"))
82 | .body("id", notNullValue())
83 | .body("title", equalTo("SivaLabs blog"))
84 | .body("url", equalTo("https://sivalabs.in"))
85 | .body("createdAt", notNullValue())
86 | .body("updatedAt", nullValue());
87 | }
88 |
89 | @Test
90 | void shouldUpdateBookmarkSuccessfully() {
91 | CreateBookmarkCommand cmd = new CreateBookmarkCommand("SivaLabs blog", "https://sivalabs.in");
92 | BookmarkDTO bookmark = bookmarkService.create(cmd);
93 |
94 | given().contentType(ContentType.JSON)
95 | .body(
96 | """
97 | {
98 | "title": "SivaLabs - Tech Blog",
99 | "url": "https://www.sivalabs.in"
100 | }
101 | """)
102 | .when()
103 | .put("/api/bookmarks/{id}", bookmark.id())
104 | .then()
105 | .statusCode(200);
106 | }
107 |
108 |
109 | @Test
110 | void shouldGetBookmarkByIdSuccessfully() {
111 | CreateBookmarkCommand cmd = new CreateBookmarkCommand("SivaLabs blog", "https://sivalabs.in");
112 | BookmarkDTO bookmark = bookmarkService.create(cmd);
113 |
114 | given().contentType(ContentType.JSON)
115 | .when()
116 | .get("/api/bookmarks/{id}", bookmark.id())
117 | .then()
118 | .statusCode(200)
119 | .body("id", equalTo(bookmark.id().intValue()))
120 | .body("title", equalTo("SivaLabs blog"))
121 | .body("url", equalTo("https://sivalabs.in"))
122 | .body("createdAt", notNullValue())
123 | .body("updatedAt", nullValue());
124 | }
125 |
126 | @Test
127 | void shouldGet404WhenBookmarkNotExists() {
128 | Long nonExistingId = 99999L;
129 | given().contentType(ContentType.JSON)
130 | .when()
131 | .get("/api/bookmarks/{id}", nonExistingId)
132 | .then()
133 | .statusCode(404);
134 | }
135 |
136 | @Test
137 | void shouldDeleteBookmarkByIdSuccessfully() {
138 | CreateBookmarkCommand cmd = new CreateBookmarkCommand("SivaLabs blog", "https://sivalabs.in");
139 | BookmarkDTO bookmark = bookmarkService.create(cmd);
140 |
141 | given().contentType(ContentType.JSON)
142 | .when()
143 | .delete("/api/bookmarks/{id}", bookmark.id())
144 | .then()
145 | .statusCode(200);
146 |
147 | Optional optionalBookmark = bookmarkService.findById(bookmark.id());
148 | assertThat(optionalBookmark).isEmpty();
149 | }
150 | }
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spring-boot-rest-api-tutorial/src/test/resources/test-data.sql:
--------------------------------------------------------------------------------
1 | TRUNCATE TABLE bookmarks;
2 | ALTER SEQUENCE bookmarks_id_seq RESTART WITH 1;
3 |
4 | INSERT INTO bookmarks(title, url, created_at) VALUES
5 | ('How (not) to ask for Technical Help?','https://sivalabs.in/how-to-not-to-ask-for-technical-help', CURRENT_TIMESTAMP),
6 | ('Announcing My SpringBoot Tips Video Series on YouTube','https://sivalabs.in/announcing-my-springboot-tips-video-series', CURRENT_TIMESTAMP),
7 | ('Kubernetes - Exposing Services to outside of Cluster using Ingress','https://sivalabs.in/kubernetes-ingress', CURRENT_TIMESTAMP),
8 | ('Kubernetes - Blue/Green Deployments','https://sivalabs.in/kubernetes-blue-green-deployments', CURRENT_TIMESTAMP),
9 | ('Kubernetes - Releasing a new version of the application using Deployment Rolling Updates','https://sivalabs.in/kubernetes-deployment-rolling-updates', CURRENT_TIMESTAMP),
10 | ('Getting Started with Kubernetes','https://sivalabs.in/getting-started-with-kubernetes', CURRENT_TIMESTAMP),
11 | ('Get Super Productive with Intellij File Templates','https://sivalabs.in/get-super-productive-with-intellij-file-templates', CURRENT_TIMESTAMP),
12 | ('Few Things I learned in the HardWay in 15 years of my career','https://sivalabs.in/few-things-i-learned-the-hardway-in-15-years-of-my-career', CURRENT_TIMESTAMP),
13 | ('All the resources you ever need as a Java & Spring application developer','https://sivalabs.in/all-the-resources-you-ever-need-as-a-java-spring-application-developer', CURRENT_TIMESTAMP),
14 | ('GoLang from a Java developer perspective','https://sivalabs.in/golang-from-a-java-developer-perspective', CURRENT_TIMESTAMP),
15 | ('Imposing Code Structure Guidelines using ArchUnit','https://sivalabs.in/impose-architecture-guidelines-using-archunit', CURRENT_TIMESTAMP),
16 | ('SpringBoot Integration Testing using TestContainers Starter','https://sivalabs.in/spring-boot-integration-testing-using-testcontainers-starter', CURRENT_TIMESTAMP),
17 | ('Creating Yeoman based SpringBoot Generator','https://sivalabs.in/creating-yeoman-based-springboot-generator', CURRENT_TIMESTAMP),
18 | ('Testing REST APIs using Postman and Newman','https://sivalabs.in/testing-rest-apis-with-postman-newman', CURRENT_TIMESTAMP),
19 | ('Testing SpringBoot Applications','https://sivalabs.in/spring-boot-testing', CURRENT_TIMESTAMP)
20 | ;
--------------------------------------------------------------------------------