diff --git a/algorithms-modules/algorithms-miscellaneous-7/pom.xml b/algorithms-modules/algorithms-miscellaneous-7/pom.xml index 3703ea5a16..c520eb6c25 100644 --- a/algorithms-modules/algorithms-miscellaneous-7/pom.xml +++ b/algorithms-modules/algorithms-miscellaneous-7/pom.xml @@ -13,4 +13,22 @@ 1.0.0-SNAPSHOT + + 1.35 + + + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + \ No newline at end of file diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/perfectnumber/PerfectNumber.java b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/perfectnumber/PerfectNumber.java new file mode 100644 index 0000000000..59b7e51a37 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/perfectnumber/PerfectNumber.java @@ -0,0 +1,50 @@ +package com.baeldung.algorithms.perfectnumber; + +import java.util.stream.IntStream; + +class PerfectNumber { + + public static boolean isPerfectBruteForce(int number) { + int sum = 0; + for (int i = 1; i <= number / 2; i++) { + if (number % i == 0) { + sum += i; + } + } + return sum == number; + } + + public static boolean isPerfectStream(int number) { + int sum = IntStream.rangeClosed(2, (int) Math.sqrt(number)) + .filter(test -> number % test == 0) + .reduce(1, (s, test) -> s + test + (number / test)); + return sum == number; + } + + public static boolean isPerfectEuclidEuler(int number) { + int p = 2; + int perfectNumber = (int) (Math.pow(2, p - 1) * (Math.pow(2, p) - 1)); + while (perfectNumber <= number) { + if (perfectNumber == number) { + return true; + } + p++; + perfectNumber = (int) (Math.pow(2, p - 1) * (Math.pow(2, p) - 1)); + } + return false; + } + + public static boolean isPerfectEuclidEulerUsingShift(int number) { + int p = 2; + int perfectNumber = (2 << (p - 1)) * ((2 << p) - 1); + while (perfectNumber <= number) { + if (perfectNumber == number) { + return true; + } + p++; + perfectNumber = (2 << (p - 1)) * ((2 << p) - 1); + } + return false; + } + +} \ No newline at end of file diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/perfectnumber/PerfectNumberBenchmark.java b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/perfectnumber/PerfectNumberBenchmark.java new file mode 100644 index 0000000000..9890c2887f --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/perfectnumber/PerfectNumberBenchmark.java @@ -0,0 +1,41 @@ +package com.baeldung.algorithms.perfectnumber; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@State(Scope.Benchmark) +public class PerfectNumberBenchmark { + + @Benchmark + public boolean bruteForceBenchmark() { + return PerfectNumber.isPerfectBruteForce(33550336); + } + + @Benchmark + public boolean streamBenchmark() { + return PerfectNumber.isPerfectStream(33550336); + } + + @Benchmark + public boolean euclidEulerBenchmark() { + return PerfectNumber.isPerfectEuclidEuler(33550336); + } + + @Benchmark + public boolean euclidEulerUsingShiftBenchmark() { + return PerfectNumber.isPerfectEuclidEulerUsingShift(33550336); + } + + public static void main(String[] args) throws Exception { + Options options = new OptionsBuilder() + .include(PerfectNumberBenchmark.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(options).run(); + } +} \ No newline at end of file diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/perfectnumber/PerfectNumberUnitTest.java b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/perfectnumber/PerfectNumberUnitTest.java new file mode 100644 index 0000000000..e03e7c8653 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/perfectnumber/PerfectNumberUnitTest.java @@ -0,0 +1,68 @@ +package com.baeldung.algorithms.perfectnumber; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PerfectNumberUnitTest { + @Test + void givenPerfectNumber_whenCheckingIsPerfectBruteForce_thenReturnTrue() { + assertTrue(PerfectNumber.isPerfectBruteForce(6)); + } + + @Test + void givenNonPerfectNumber_whenCheckingIsPerfectBruteForce_thenReturnFalse() { + assertFalse(PerfectNumber.isPerfectBruteForce(10)); + } + + @Test + void givenNegativeNumber_whenCheckingIsPerfectBruteForce_thenReturnFalse() { + assertFalse(PerfectNumber.isPerfectBruteForce(-28)); + } + + @Test + void givenPerfectNumber_whenCheckingIsPerfectStream_thenReturnTrue() { + assertTrue(PerfectNumber.isPerfectStream(28)); + } + + @Test + void givenNonPerfectNumber_whenCheckingIsPerfectStream_thenReturnFalse() { + assertFalse(PerfectNumber.isPerfectStream(10)); + } + + @Test + void givenNegativeNumber_whenCheckingIsPerfectStream_thenReturnFalse() { + assertFalse(PerfectNumber.isPerfectStream(-6)); + } + + @Test + void givenPerfectNumber_whenCheckingIsPerfectEuclidEuler_thenReturnTrue() { + assertTrue(PerfectNumber.isPerfectEuclidEuler(28)); + } + + @Test + void givenNonPerfectNumber_whenCheckingIsPerfectEuclidEuler_thenReturnFalse() { + assertFalse(PerfectNumber.isPerfectEuclidEuler(10)); + } + + @Test + void givenNegativeNumber_whenCheckingIsPerfectEuclidEuler_thenReturnFalse() { + assertFalse(PerfectNumber.isPerfectEuclidEuler(-6)); + } + + @Test + void givenPerfectNumber_whenCheckingIsPerfectEuclidEulerUsingShift_thenReturnTrue() { + assertTrue(PerfectNumber.isPerfectEuclidEulerUsingShift(28)); + } + + @Test + void givenNonPerfectNumber_whenCheckingIsPerfectEuclidEulerUsingShift_thenReturnFalse() { + assertFalse(PerfectNumber.isPerfectEuclidEulerUsingShift(10)); + } + + @Test + void givenNegativeNumber_whenCheckingIsPerfectEuclidEulerUsingShift_thenReturnFalse() { + assertFalse(PerfectNumber.isPerfectEuclidEulerUsingShift(-6)); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-5/README.md b/core-java-modules/core-java-collections-5/README.md index 4f174b5163..2fc7f7c699 100644 --- a/core-java-modules/core-java-collections-5/README.md +++ b/core-java-modules/core-java-collections-5/README.md @@ -9,7 +9,6 @@ - [Skipping the First Iteration in Java](https://www.baeldung.com/java-skip-first-iteration) - [Remove Elements From a Queue Using Loop](https://www.baeldung.com/java-remove-elements-queue) - [Intro to Vector Class in Java](https://www.baeldung.com/java-vector-guide) -- [HashSet toArray() Method in Java](https://www.baeldung.com/java-hashset-toarray) - [Time Complexity of Java Collections Sort in Java](https://www.baeldung.com/java-time-complexity-collections-sort) - [Check if List Contains at Least One Enum](https://www.baeldung.com/java-list-check-enum-presence) - [Comparison of for Loops and Iterators](https://www.baeldung.com/java-for-loops-vs-iterators) diff --git a/core-java-modules/core-java-collections-5/pom.xml b/core-java-modules/core-java-collections-5/pom.xml index 939e479ba1..227b80ac4a 100644 --- a/core-java-modules/core-java-collections-5/pom.xml +++ b/core-java-modules/core-java-collections-5/pom.xml @@ -55,8 +55,8 @@ org.apache.maven.plugins maven-compiler-plugin - 9 - 9 + 16 + 16 diff --git a/core-java-modules/core-java-collections-6/README.md b/core-java-modules/core-java-collections-6/README.md new file mode 100644 index 0000000000..b5f1adbf48 --- /dev/null +++ b/core-java-modules/core-java-collections-6/README.md @@ -0,0 +1,5 @@ +========= + +## Core Java Collections Cookbooks and Examples + +### Relevant Articles: diff --git a/core-java-modules/core-java-collections-6/pom.xml b/core-java-modules/core-java-collections-6/pom.xml new file mode 100644 index 0000000000..3d38c3de35 --- /dev/null +++ b/core-java-modules/core-java-collections-6/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + core-java-collections-6 + jar + core-java-collections-6 + + + com.baeldung.core-java-modules + core-java-modules + 0.0.1-SNAPSHOT + + + + + org.junit.platform + junit-platform-runner + ${junit-platform.version} + test + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + org.junit.vintage + junit-vintage-engine + ${junit.version} + test + + + org.roaringbitmap + RoaringBitmap + ${roaringbitmap.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + + + + + + 5.9.2 + 0.9.38 + 1.36 + + + diff --git a/core-java-modules/core-java-collections-6/src/test/java/com/baeldung/listiteration/ListIterationUnitTest.java b/core-java-modules/core-java-collections-6/src/test/java/com/baeldung/listiteration/ListIterationUnitTest.java new file mode 100644 index 0000000000..2f1b2cc083 --- /dev/null +++ b/core-java-modules/core-java-collections-6/src/test/java/com/baeldung/listiteration/ListIterationUnitTest.java @@ -0,0 +1,71 @@ +package com.baeldung.listiteration; + +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +public class ListIterationUnitTest { + + List programmingLanguages = new ArrayList<>(List.of("Java", "Python", "C++")); + List numbers = new ArrayList<>(List.of(1, 2, 3)); + + @Test + public void givenStringList_whenAddElementWithListIterator_thenModifiedList() { + ListIterator listIterator = programmingLanguages.listIterator(); + while (listIterator.hasNext()) { + String language = listIterator.next(); + if (language.equals("Python")) { + listIterator.add("JavaScript"); + } + } + + assertIterableEquals(Arrays.asList("Java", "Python", "JavaScript", "C++"), programmingLanguages); + } + + @Test + public void givenNumericalList_whenMultiplyElementWithListIterator_thenModifiedList() { + ListIterator listIterator = numbers.listIterator(); + while (listIterator.hasNext()) { + int num = listIterator.next(); + if (num == 2) { + listIterator.add(num * 10); + } + } + assertIterableEquals(Arrays.asList(1, 2, 20, 3), numbers); + } + + @Test + public void givenStringList_whenAddElementWithEnhancedForLoopAndCopy_thenModifiedList() { + List copyOfWords = new ArrayList<>(programmingLanguages); + for (String word : copyOfWords) { + programmingLanguages.add(word.toUpperCase()); // Modified: Convert to uppercase + } + assertIterableEquals(Arrays.asList("Java", "Python", "C++", "JAVA", "PYTHON", "C++"), programmingLanguages); + } + + @Test + public void givenNumericalList_whenMultiplyElementWithEnhancedForLoopAndCopy_thenModifiedList() { + List copyOfNumbers = new ArrayList<>(numbers); + for (int num : copyOfNumbers) { + numbers.add(num * 2); + } + assertIterableEquals(Arrays.asList(1, 2, 3, 2, 4, 6), numbers); + } + + @Test + public void givenStringList_whenConvertToUpperCaseWithJava8Stream_thenModifiedList() { + programmingLanguages = programmingLanguages.stream().map(String::toUpperCase).collect(Collectors.toList()); + assertIterableEquals(Arrays.asList("JAVA", "PYTHON", "C++"), programmingLanguages); + } + + @Test + public void givenNumericalList_whenMultiplyByThreeWithJava8Stream_thenModifiedList() { + numbers = numbers.stream().map(num -> num * 3).collect(Collectors.toList()); + assertIterableEquals(Arrays.asList(3, 6, 9), numbers); + } + +} diff --git a/core-java-modules/core-java-collections-conversions-3/README.md b/core-java-modules/core-java-collections-conversions-3/README.md index 959e4e8160..96727e8d88 100644 --- a/core-java-modules/core-java-collections-conversions-3/README.md +++ b/core-java-modules/core-java-collections-conversions-3/README.md @@ -5,3 +5,4 @@ This module contains articles about conversions among Collection types in Java. ### Relevant Articles: - [Converting HashMap Values to an ArrayList in Java](https://www.baeldung.com/java-hashmap-arraylist) - [Joining a List in Java With Commas and “and”](https://www.baeldung.com/java-string-concatenation-natural-language) +- [HashSet toArray() Method in Java](https://www.baeldung.com/java-hashset-toarray) diff --git a/core-java-modules/core-java-collections-5/toarraymethod/ConvertingHashSetToArrayUnitTest.java b/core-java-modules/core-java-collections-conversions-3/src/test/java/com/baeldung/toarraymethod/ConvertingHashSetToArrayUnitTest.java similarity index 100% rename from core-java-modules/core-java-collections-5/toarraymethod/ConvertingHashSetToArrayUnitTest.java rename to core-java-modules/core-java-collections-conversions-3/src/test/java/com/baeldung/toarraymethod/ConvertingHashSetToArrayUnitTest.java diff --git a/core-java-modules/core-java-collections-list-2/src/test/java/com/baeldung/java/listInitialization/ListInitializationUnitTest.java b/core-java-modules/core-java-collections-list-2/src/test/java/com/baeldung/java/listInitialization/ListInitializationUnitTest.java deleted file mode 100644 index 25f39e9a13..0000000000 --- a/core-java-modules/core-java-collections-list-2/src/test/java/com/baeldung/java/listInitialization/ListInitializationUnitTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.baeldung.java.listInitialization; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.extern.java.Log; - -import org.junit.Assert; -import org.junit.Test; - -@Log -public class ListInitializationUnitTest { - - @Test - public void givenAnonymousInnerClass_thenInitialiseList() { - List cities = new ArrayList() { - { - add("New York"); - add("Rio"); - add("Tokyo"); - } - }; - - Assert.assertTrue(cities.contains("New York")); - } - - @Test - public void givenArraysAsList_thenInitialiseList() { - List list = Arrays.asList("foo", "bar"); - - Assert.assertTrue(list.contains("foo")); - } - - @Test(expected = UnsupportedOperationException.class) - public void givenArraysAsList_whenAdd_thenUnsupportedException() { - List list = Arrays.asList("foo", "bar"); - - list.add("baz"); - } - - @Test - public void givenArraysAsList_whenCreated_thenShareReference() { - String[] array = { "foo", "bar" }; - List list = Arrays.asList(array); - array[0] = "baz"; - Assert.assertEquals("baz", list.get(0)); - } - - @Test - public void givenStream_thenInitializeList() { - List list = Stream.of("foo", "bar") - .collect(Collectors.toList()); - - Assert.assertTrue(list.contains("foo")); - } -} diff --git a/core-java-modules/core-java-collections-list-2/src/test/java/com/baeldung/java/listinitialization/ListInitializationUnitTest.java b/core-java-modules/core-java-collections-list-2/src/test/java/com/baeldung/java/listinitialization/ListInitializationUnitTest.java new file mode 100644 index 0000000000..4b06621fef --- /dev/null +++ b/core-java-modules/core-java-collections-list-2/src/test/java/com/baeldung/java/listinitialization/ListInitializationUnitTest.java @@ -0,0 +1,91 @@ +package com.baeldung.java.listinitialization; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Test; + +import lombok.extern.java.Log; + +@Log +public class ListInitializationUnitTest { + + @Test + public void givenAnonymousInnerClass_thenInitialiseList() { + List cities = new ArrayList() { + { + add("New York"); + add("Rio"); + add("Tokyo"); + } + }; + + assertTrue(cities.contains("New York")); + } + + @Test + public void givenArraysAsList_thenInitialiseList() { + List list = Arrays.asList("foo", "bar"); + + assertTrue(list.contains("foo")); + } + + @Test(expected = UnsupportedOperationException.class) + public void givenArraysAsList_whenAdd_thenUnsupportedException() { + List list = Arrays.asList("foo", "bar"); + + list.add("baz"); + } + + @Test + public void givenArraysAsList_whenUsingArrayListConstructor_thenWeCanAddOrRemove() { + List list = new ArrayList<>(Arrays.asList("foo", "bar")); + + list.add("baz"); + assertEquals(List.of("foo", "bar","baz"), list); + + list.remove("baz"); + assertEquals(List.of("foo", "bar"), list); + } + + @Test + public void givenArraysAsList_whenCreated_thenShareReference() { + String[] array = { "foo", "bar" }; + List list = Arrays.asList(array); + array[0] = "baz"; + assertEquals("baz", list.get(0)); + } + + @Test + public void givenIntNumbers_whenRequiredLong_thenCastAutomatically() { + int intNum = 42; + long longNum = intNum; + + assertEquals(42L, longNum); + } + + @Test + public void givenArrayAsList_whenRequiredLongList_thenGetExpectedResult() { + List listOfLongFixedSize = Arrays.asList(1L, 2L, 3L); + List listOfLong = new ArrayList<>(Arrays.asList(1L, 2L, 3L)); + + List expected = List.of(1L, 2L, 3L); + + assertEquals(expected, listOfLongFixedSize); + assertEquals(expected, listOfLong); + } + + @Test + public void givenStream_thenInitializeList() { + List list = Stream.of("foo", "bar") + .collect(Collectors.toList()); + + assertTrue(list.contains("foo")); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-advanced-3/pom.xml b/core-java-modules/core-java-concurrency-advanced-3/pom.xml index cd3e9ed170..d09b0d55e8 100644 --- a/core-java-modules/core-java-concurrency-advanced-3/pom.xml +++ b/core-java-modules/core-java-concurrency-advanced-3/pom.xml @@ -86,7 +86,7 @@ 1.8 0.22.6 1.9.20.1 - 0.43 + 0.55.0 1.2.3 0.14.1 1.9.20.1 diff --git a/core-java-modules/core-java-lang-math-3/README.md b/core-java-modules/core-java-lang-math-3/README.md index d4eef0f1b9..473369beef 100644 --- a/core-java-modules/core-java-lang-math-3/README.md +++ b/core-java-modules/core-java-lang-math-3/README.md @@ -7,15 +7,11 @@ - [Evaluating a Math Expression in Java](https://www.baeldung.com/java-evaluate-math-expression-string) - [Swap Two Variables in Java](https://www.baeldung.com/java-swap-two-variables) - [Java Program to Find the Roots of a Quadratic Equation](https://www.baeldung.com/roots-quadratic-equation) -- [Create a BMI Calculator in Java](https://www.baeldung.com/java-body-mass-index-calculator) - [Java Program to Calculate the Standard Deviation](https://www.baeldung.com/java-calculate-standard-deviation) - [Java Program to Print Pascal’s Triangle](https://www.baeldung.com/java-pascal-triangle) - [Java Money and the Currency API](http://www.baeldung.com/java-money-and-currency) - [Clamp Function in Java](https://www.baeldung.com/java-clamp-function) - [Creating a Magic Square in Java](https://www.baeldung.com/java-magic-square) -- [Check if a Point Is Between Two Points Drawn on a Straight Line in Java](https://www.baeldung.com/java-check-point-straight-line) - [Validate if a String Is a Valid Geo Coordinate](https://www.baeldung.com/java-geo-coordinates-validation) -- [Rotate a Vertex Around a Certain Point in Java](https://www.baeldung.com/java-rotate-vertex-around-point) - [Calculating the Power of Any Number in Java Without Using Math pow() Method](https://www.baeldung.com/java-calculating-the-power-without-math-pow) -- [Solving Rod Cutting Problem in Java](https://www.baeldung.com/java-rod-cutting-problem) - More articles: [[<-- Prev]](/core-java-modules/core-java-lang-math-2) diff --git a/core-java-modules/core-java-lang-math-4/README.md b/core-java-modules/core-java-lang-math-4/README.md index 377c51848d..0efe87f7ff 100644 --- a/core-java-modules/core-java-lang-math-4/README.md +++ b/core-java-modules/core-java-lang-math-4/README.md @@ -2,3 +2,7 @@ ### Relevant articles: - [Calculate Percentiles in Java](https://www.baeldung.com/java-compute-percentiles) +- [Solving Rod Cutting Problem in Java](https://www.baeldung.com/java-rod-cutting-problem) +- [Rotate a Vertex Around a Certain Point in Java](https://www.baeldung.com/java-rotate-vertex-around-point) +- [Create a BMI Calculator in Java](https://www.baeldung.com/java-body-mass-index-calculator) +- [Check if a Point Is Between Two Points Drawn on a Straight Line in Java](https://www.baeldung.com/java-check-point-straight-line) diff --git a/core-java-modules/core-java-lang-math-4/pom.xml b/core-java-modules/core-java-lang-math-4/pom.xml index e818855d36..ccafb4a894 100644 --- a/core-java-modules/core-java-lang-math-4/pom.xml +++ b/core-java-modules/core-java-lang-math-4/pom.xml @@ -12,4 +12,8 @@ 0.0.1-SNAPSHOT + + 17 + + diff --git a/core-java-modules/core-java-lang-math-3/src/main/java/com/baeldung/math/bmicalculator/BMICalculator.java b/core-java-modules/core-java-lang-math-4/src/main/java/com/baeldung/math/bmicalculator/BMICalculator.java similarity index 100% rename from core-java-modules/core-java-lang-math-3/src/main/java/com/baeldung/math/bmicalculator/BMICalculator.java rename to core-java-modules/core-java-lang-math-4/src/main/java/com/baeldung/math/bmicalculator/BMICalculator.java diff --git a/core-java-modules/core-java-lang-math-3/src/main/java/com/baeldung/math/pointbetweentwopoints/PointLiesBetweenTwoPoints.java b/core-java-modules/core-java-lang-math-4/src/main/java/com/baeldung/math/pointbetweentwopoints/PointLiesBetweenTwoPoints.java similarity index 100% rename from core-java-modules/core-java-lang-math-3/src/main/java/com/baeldung/math/pointbetweentwopoints/PointLiesBetweenTwoPoints.java rename to core-java-modules/core-java-lang-math-4/src/main/java/com/baeldung/math/pointbetweentwopoints/PointLiesBetweenTwoPoints.java diff --git a/core-java-modules/core-java-lang-math-3/src/main/java/com/baeldung/math/rodcutting/RodCuttingProblem.java b/core-java-modules/core-java-lang-math-4/src/main/java/com/baeldung/math/rodcutting/RodCuttingProblem.java similarity index 100% rename from core-java-modules/core-java-lang-math-3/src/main/java/com/baeldung/math/rodcutting/RodCuttingProblem.java rename to core-java-modules/core-java-lang-math-4/src/main/java/com/baeldung/math/rodcutting/RodCuttingProblem.java diff --git a/core-java-modules/core-java-lang-math-3/src/main/java/com/baeldung/math/rotatevertex/VertexRotation.java b/core-java-modules/core-java-lang-math-4/src/main/java/com/baeldung/math/rotatevertex/VertexRotation.java similarity index 100% rename from core-java-modules/core-java-lang-math-3/src/main/java/com/baeldung/math/rotatevertex/VertexRotation.java rename to core-java-modules/core-java-lang-math-4/src/main/java/com/baeldung/math/rotatevertex/VertexRotation.java diff --git a/core-java-modules/core-java-lang-math-3/src/test/java/com/baeldung/math/bmicalculator/BMICalculatorUnitTest.java b/core-java-modules/core-java-lang-math-4/src/test/java/com/baeldung/math/bmicalculator/BMICalculatorUnitTest.java similarity index 100% rename from core-java-modules/core-java-lang-math-3/src/test/java/com/baeldung/math/bmicalculator/BMICalculatorUnitTest.java rename to core-java-modules/core-java-lang-math-4/src/test/java/com/baeldung/math/bmicalculator/BMICalculatorUnitTest.java diff --git a/core-java-modules/core-java-lang-math-3/src/test/java/com/baeldung/math/pointbetweentwopoints/PointLiesBetweenTwoPointsUnitTest.java b/core-java-modules/core-java-lang-math-4/src/test/java/com/baeldung/math/pointbetweentwopoints/PointLiesBetweenTwoPointsUnitTest.java similarity index 100% rename from core-java-modules/core-java-lang-math-3/src/test/java/com/baeldung/math/pointbetweentwopoints/PointLiesBetweenTwoPointsUnitTest.java rename to core-java-modules/core-java-lang-math-4/src/test/java/com/baeldung/math/pointbetweentwopoints/PointLiesBetweenTwoPointsUnitTest.java diff --git a/core-java-modules/core-java-lang-math-3/src/test/java/com/baeldung/math/rodcutting/RodCuttingProblemUnitTest.java b/core-java-modules/core-java-lang-math-4/src/test/java/com/baeldung/math/rodcutting/RodCuttingProblemUnitTest.java similarity index 100% rename from core-java-modules/core-java-lang-math-3/src/test/java/com/baeldung/math/rodcutting/RodCuttingProblemUnitTest.java rename to core-java-modules/core-java-lang-math-4/src/test/java/com/baeldung/math/rodcutting/RodCuttingProblemUnitTest.java diff --git a/core-java-modules/core-java-lang-math-3/src/test/java/com/baeldung/math/rotatevertex/VertexRotationUnitTest.java b/core-java-modules/core-java-lang-math-4/src/test/java/com/baeldung/math/rotatevertex/VertexRotationUnitTest.java similarity index 100% rename from core-java-modules/core-java-lang-math-3/src/test/java/com/baeldung/math/rotatevertex/VertexRotationUnitTest.java rename to core-java-modules/core-java-lang-math-4/src/test/java/com/baeldung/math/rotatevertex/VertexRotationUnitTest.java diff --git a/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/Example.java b/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/Example.java new file mode 100644 index 0000000000..0557e032ad --- /dev/null +++ b/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/Example.java @@ -0,0 +1,13 @@ +package com.baeldung.passclassasparameter; + +public class Example { + public static void processClass(Class clazz) { + System.out.println("Processing class: " + clazz.getName()); + } + + public static void main(String[] args) { + processClass(String.class); + processClass(Integer.class); + processClass(Double.class); + } +} diff --git a/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/GenericExample.java b/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/GenericExample.java new file mode 100644 index 0000000000..c72d57743c --- /dev/null +++ b/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/GenericExample.java @@ -0,0 +1,22 @@ +package com.baeldung.passclassasparameter; + +import java.util.ArrayList; +import java.util.List; + +public class GenericExample { + public static void printListElements(Class clazz, List list) { + System.out.println("Elements of " + clazz.getSimpleName() + " list:"); + for (T element : list) { + System.out.println(element); + } + } + + public static void main(String[] args) { + List stringList = new ArrayList<>(); + stringList.add("Java"); + stringList.add("is"); + stringList.add("awesome"); + + printListElements(String.class, stringList); + } +} diff --git a/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/ReflectionExample.java b/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/ReflectionExample.java new file mode 100644 index 0000000000..04c9dc3414 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-others/src/main/java/com/baeldung/passclassasparameter/ReflectionExample.java @@ -0,0 +1,21 @@ +package com.baeldung.passclassasparameter; + +import java.lang.reflect.Method; + +public class ReflectionExample { + public static void processClass(Class clazz, String methodName) throws Exception { + Method method = clazz.getMethod(methodName); + Object instance = clazz.getDeclaredConstructor().newInstance(); + method.invoke(instance); + } + + public static void main(String[] args) throws Exception { + processClass(ReflectionTarget.class, "sayHello"); + } +} + +class ReflectionTarget { + public void sayHello() { + System.out.println("Hello, Reflection!"); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/convertphonenumberinwordstonumber/UseHashMapToConvertPhoneNumberInWordsToNumber.java b/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/convertphonenumberinwordstonumber/UseHashMapToConvertPhoneNumberInWordsToNumber.java new file mode 100644 index 0000000000..c969e43f86 --- /dev/null +++ b/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/convertphonenumberinwordstonumber/UseHashMapToConvertPhoneNumberInWordsToNumber.java @@ -0,0 +1,43 @@ +import java.util.Map; + +public class UseHashMapToConvertPhoneNumberInWordsToNumber { + private static Map multipliers = Map.of("double",2, + "triple", 3, + "quadruple", 4); + private static Map digits = Map.of("zero","1", + "one", "1", + "two", "2", + "three", "3", + "four", "4", + "five", "5", + "six", "6", + "seven", "7", + "eight", "8", + "nine", "9"); + + + public static String convertPhoneNumberInWordsToNumber(String phoneNumberInWord) { + + StringBuilder output = new StringBuilder(); + Integer currentMultiplier = null; + String[] words = phoneNumberInWord.split(" "); + + for (String word : words) { + Integer multiplier = multipliers.get(word); + if (multiplier != null) { + if (currentMultiplier != null) { + throw new IllegalArgumentException("Cannot have consecutive multipliers, at: " + word); + } + currentMultiplier = multiplier; + } else { + String digit = digits.get(word); + if (digit == null) { + throw new IllegalArgumentException("Invalid word: " + word); + } + output.append(digit.repeat(currentMultiplier != null ? currentMultiplier : 1)); + currentMultiplier = null; + } + } + return output.toString(); + } +} diff --git a/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/convertphonenumberinwordstonumber/UseSwitchToConvertPhoneNumberInWordsToNumber.java b/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/convertphonenumberinwordstonumber/UseSwitchToConvertPhoneNumberInWordsToNumber.java new file mode 100644 index 0000000000..69f9ce9ab7 --- /dev/null +++ b/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/convertphonenumberinwordstonumber/UseSwitchToConvertPhoneNumberInWordsToNumber.java @@ -0,0 +1,64 @@ +public class UseSwitchToConvertPhoneNumberInWordsToNumber { + + public static String convertPhoneNumberInWordsToNumber(String phoneNumberInWord) { + + StringBuilder output = new StringBuilder(); + Integer currentMultiplier = null; + String[] words = phoneNumberInWord.split(" "); + + for (String word : words) { + Integer multiplier = getWordAsMultiplier(word); + if (multiplier != null) { + if (currentMultiplier != null) { + throw new IllegalArgumentException("Cannot have consecutive multipliers, at: " + word); + } + currentMultiplier = multiplier; + } else { + output.append(getWordAsDigit(word).repeat(currentMultiplier != null ? currentMultiplier : 1)); + currentMultiplier = null; + } + } + return output.toString(); + } + + public static Integer getWordAsMultiplier(String word) { + switch (word) { + case "double": + return 2; + case "triple": + return 3; + case "quadruple": + return 4; + default: + return null; + } + } + + public static String getWordAsDigit(String word) { + switch (word) { + case "zero": + return "0"; + case "one": + return "1"; + case "two": + return "2"; + case "three": + return "3"; + case "four": + return "4"; + case "five": + return "5"; + case "six": + return "6"; + case "seven": + return "7"; + case "eight": + return "8"; + case "nine": + return "9"; + default: + throw new IllegalArgumentException("Invalid word: " + word); + } + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/convertphonenumberinwordstonumber/UseHashMapToConvertPhoneNumberInWordsToNumberUnitTest.java b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/convertphonenumberinwordstonumber/UseHashMapToConvertPhoneNumberInWordsToNumberUnitTest.java new file mode 100644 index 0000000000..95a50dd16e --- /dev/null +++ b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/convertphonenumberinwordstonumber/UseHashMapToConvertPhoneNumberInWordsToNumberUnitTest.java @@ -0,0 +1,31 @@ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class UseHashMapToConvertPhoneNumberInWordsToNumberUnitTest { + + @Test + void givenStringWithWhiteSpaces_WhenConvertPhoneNumberInWordsToNumber_ThenEquivalentNumber() { + + assertEquals("5248888", + UseHashMapToConvertPhoneNumberInWordsToNumber + .convertPhoneNumberInWordsToNumber("five two four quadruple eight")); + } + + @Test + void givenStringEndingWithConseutiveMultipliers_WhenConvertPhoneNumberInWordsToNumber_ThenThrowException() { + + assertThrows(IllegalArgumentException.class, () -> { + UseHashMapToConvertPhoneNumberInWordsToNumber + .convertPhoneNumberInWordsToNumber("five eight three double triple"); + }); + } + + @Test + void givenStringWithInvalidWords_WhenConvertPhoneNumberInWordsToNumber_ThenThrowException() { + + assertThrows(IllegalArgumentException.class, () -> { + UseHashMapToConvertPhoneNumberInWordsToNumber + .convertPhoneNumberInWordsToNumber("five eight three two four penta null eight"); + }); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/convertphonenumberinwordstonumber/UseSwitchToConvertPhoneNumberInWordsToNumberUnitTest.java b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/convertphonenumberinwordstonumber/UseSwitchToConvertPhoneNumberInWordsToNumberUnitTest.java new file mode 100644 index 0000000000..093e96ecea --- /dev/null +++ b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/convertphonenumberinwordstonumber/UseSwitchToConvertPhoneNumberInWordsToNumberUnitTest.java @@ -0,0 +1,62 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class UseSwitchToConvertPhoneNumberInWordsToNumberUnitTest { + + @Test + void givenStringWithWhiteSpaces_WhenConvertPhoneNumberInWordsToNumber_ThenEquivalentNumber() { + + assertEquals("5248888", + UseSwitchToConvertPhoneNumberInWordsToNumber + .convertPhoneNumberInWordsToNumber("five two four quadruple eight")); + + } + + @Test + void givenStringEndingWithConseutiveMultipliers_WhenConvertPhoneNumberInWordsToNumber_ThenThrowException() { + + assertThrows(IllegalArgumentException.class, () -> { + UseSwitchToConvertPhoneNumberInWordsToNumber + .convertPhoneNumberInWordsToNumber("five eight three double triple"); + }); + } + + @Test + void givenStringWithInvalidWords_WhenConvertPhoneNumberInWordsToNumber_ThenThrowException() { + + assertThrows(IllegalArgumentException.class, () -> { + UseSwitchToConvertPhoneNumberInWordsToNumber + .convertPhoneNumberInWordsToNumber("five eight three two four penta null eight"); + }); + } + + + @Test + void givenString_WhenGetWordAsMultiplier_ThenEquivalentNumber() { + assertEquals(2, UseSwitchToConvertPhoneNumberInWordsToNumber + .getWordAsMultiplier("double")); + } + + @Test + void givenInvalidString_WhenGetWordAsMultiplier_ThenReturnNull() { + assertEquals(null, UseSwitchToConvertPhoneNumberInWordsToNumber + .getWordAsMultiplier("hexa")); + + } + + @Test + void givenString_WhenMapIndividualDigits_ThenEquivalentNumber() { + assertEquals("5", + UseSwitchToConvertPhoneNumberInWordsToNumber + .getWordAsDigit("five")); + } + + @Test + void givenInvalidString_WhenMapIndividualDigits_ThenThrowException() { + assertThrows(IllegalArgumentException.class, () -> { + UseSwitchToConvertPhoneNumberInWordsToNumber + .convertPhoneNumberInWordsToNumber("penta"); + }); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-string-operations-8/src/test/java/com/baeldung/symmetricsubstringlength/SymmetricSubstringMaxLengthUnitTest.java b/core-java-modules/core-java-string-operations-8/src/test/java/com/baeldung/symmetricsubstringlength/SymmetricSubstringMaxLengthUnitTest.java new file mode 100644 index 0000000000..2ae37862dd --- /dev/null +++ b/core-java-modules/core-java-string-operations-8/src/test/java/com/baeldung/symmetricsubstringlength/SymmetricSubstringMaxLengthUnitTest.java @@ -0,0 +1,77 @@ +package com.baeldung.symmetricsubstringlength; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SymmetricSubstringMaxLengthUnitTest { + String input = "<>>"; + int expected = 4; + + @Test + public void givenString_whenUsingSymmetricSubstringExpansion_thenFindLongestSymmetricSubstring() { + int start = 0; + int mid = 0; + int last_gt = 0; + int end = 0; + int best = 0; + + while (start < input.length()) { + int current = Math.min(mid - start, end - mid); + if (best < current) { + best = current; + } + + if (end - mid == current && end < input.length()) { + if (input.charAt(end) == '?') { + end++; + } else if (input.charAt(end) == '>') { + end++; + last_gt = end; + } else { + end++; + mid = end; + start = Math.max(start, last_gt); + } + } else if (mid < input.length() && input.charAt(mid) == '?') { + mid++; + } else if (start < mid) { + start++; + } else { + start = Math.max(start, last_gt); + mid++; + end = Math.max(mid, end); + } + } + int result = 2 * best; + + assertEquals(expected, result); + } + + @Test + public void givenString_whenUsingBruteForce_thenFindLongestSymmetricSubstring() { + int max = 0; + for (int i = 0; i < input.length(); i++) { + for (int j = i + 1; j <= input.length(); j++) { + String t = input.substring(i, j); + if (t.length() % 2 == 0) { + int k = 0, l = t.length() - 1; + boolean isSym = true; + while (k < l && isSym) { + if (!(t.charAt(k) == '<' || t.charAt(k) == '?') && (t.charAt(l) == '>' || t.charAt(l) == '?')) { + isSym = false; + } + k++; + l--; + } + if (isSym) { + max = Math.max(max, t.length()); + } + } + } + } + + assertEquals(expected, max); + } + +} diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index bd7aae6410..f6c5f8191a 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -89,6 +89,7 @@ core-java-collections-3 core-java-collections-4 core-java-collections-5 + core-java-collections-6 core-java-collections-conversions core-java-collections-set-2 core-java-collections-list diff --git a/libraries-3/pom.xml b/libraries-3/pom.xml index 33650b4f7f..d0589ac59b 100644 --- a/libraries-3/pom.xml +++ b/libraries-3/pom.xml @@ -168,7 +168,7 @@ - 0.22.6 + 0.26.0 1.9.20.1 0.14.1 1.9.20.1 diff --git a/libraries-apache-commons-2/pom.xml b/libraries-apache-commons-2/pom.xml index c555b83273..0f00bf5d84 100644 --- a/libraries-apache-commons-2/pom.xml +++ b/libraries-apache-commons-2/pom.xml @@ -44,15 +44,27 @@ ${mockftpserver.version} test + + org.tukaani + xz + ${xz.version} + + + com.github.luben + zstd-jni + ${zstd-jni.version} + - 1.23.0 + 1.26.1 1.10.13 2.9.0 1.10.0 3.6 2.7.1 + 1.9 + 1.5.5-11 \ No newline at end of file diff --git a/libraries-apache-commons-2/src/main/java/com/baeldung/commons/compress/CompressUtils.java b/libraries-apache-commons-2/src/main/java/com/baeldung/commons/compress/CompressUtils.java new file mode 100644 index 0000000000..796b3fd22b --- /dev/null +++ b/libraries-apache-commons-2/src/main/java/com/baeldung/commons/compress/CompressUtils.java @@ -0,0 +1,119 @@ +package com.baeldung.commons.compress; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.ArchiveStreamFactory; +import org.apache.commons.compress.archivers.examples.Archiver; +import org.apache.commons.compress.archivers.examples.Expander; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.compress.compressors.CompressorException; +import org.apache.commons.compress.compressors.CompressorInputStream; +import org.apache.commons.compress.compressors.CompressorOutputStream; +import org.apache.commons.compress.compressors.CompressorStreamFactory; +import org.apache.commons.compress.utils.FileNameUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; + +public class CompressUtils { + + private CompressUtils() { + } + + public static void archive(Path directory, Path destination) throws IOException, ArchiveException { + String format = FileNameUtils.getExtension(destination); + new Archiver().create(format, destination, directory); + } + + public static void archiveAndCompress(String directory, Path destination) throws IOException, ArchiveException, CompressorException { + archiveAndCompress(Paths.get(directory), destination); + } + + public static void archiveAndCompress(Path directory, Path destination) throws IOException, ArchiveException, CompressorException { + String compressionFormat = FileNameUtils.getExtension(destination); + String archiveFormat = FilenameUtils.getExtension(destination.getFileName() + .toString() + .replace("." + compressionFormat, "")); + + try (OutputStream archive = Files.newOutputStream(destination); + BufferedOutputStream archiveBuffer = new BufferedOutputStream(archive); + CompressorOutputStream compressor = new CompressorStreamFactory().createCompressorOutputStream(compressionFormat, archiveBuffer); + ArchiveOutputStream archiver = new ArchiveStreamFactory().createArchiveOutputStream(archiveFormat, compressor)) { + new Archiver().create(archiver, directory); + } + } + + public static void decompress(Path file, Path destination) throws IOException, ArchiveException, CompressorException { + decompress(Files.newInputStream(file), destination); + } + + public static void decompress(InputStream file, Path destination) throws IOException, ArchiveException, CompressorException { + try (InputStream in = file; + BufferedInputStream inputBuffer = new BufferedInputStream(in); + OutputStream out = Files.newOutputStream(destination); + CompressorInputStream decompressor = new CompressorStreamFactory().createCompressorInputStream(inputBuffer)) { + IOUtils.copy(decompressor, out); + } + } + + public static void extract(Path archive, Path destination) throws IOException, ArchiveException, CompressorException { + new Expander().expand(archive, destination); + } + + public static void compressFile(Path file, Path destination) throws IOException, CompressorException { + String format = FileNameUtils.getExtension(destination); + + try (OutputStream out = Files.newOutputStream(destination); + BufferedOutputStream buffer = new BufferedOutputStream(out); + CompressorOutputStream compressor = new CompressorStreamFactory().createCompressorOutputStream(format, buffer)) { + IOUtils.copy(Files.newInputStream(file), compressor); + } + } + + public static void zip(Path file, Path destination) throws IOException { + try (InputStream input = Files.newInputStream(file); + OutputStream output = Files.newOutputStream(destination); + ZipArchiveOutputStream archive = new ZipArchiveOutputStream(output)) { + archive.setLevel(Deflater.BEST_COMPRESSION); + archive.setMethod(ZipEntry.DEFLATED); + + archive.putArchiveEntry(new ZipArchiveEntry(file.getFileName() + .toString())); + IOUtils.copy(input, archive); + archive.closeArchiveEntry(); + } + } + + public static void extractOne(Path archivePath, String fileName, Path destinationDirectory) throws IOException, ArchiveException { + try (InputStream input = Files.newInputStream(archivePath); + BufferedInputStream buffer = new BufferedInputStream(input); + ArchiveInputStream archive = new ArchiveStreamFactory().createArchiveInputStream(buffer)) { + + ArchiveEntry entry; + while ((entry = archive.getNextEntry()) != null) { + if (entry.getName() + .equals(fileName)) { + Path outFile = destinationDirectory.resolve(fileName); + Files.createDirectories(outFile.getParent()); + try (OutputStream os = Files.newOutputStream(outFile)) { + IOUtils.copy(archive, os); + } + break; + } + } + } + } +} \ No newline at end of file diff --git a/libraries-apache-commons-2/src/main/java/com/baeldung/commons/convertunicode/UnicodeConverterUtil.java b/libraries-apache-commons-2/src/main/java/com/baeldung/commons/convertunicode/UnicodeConverterUtil.java index c788f6ee61..997c5b61ff 100644 --- a/libraries-apache-commons-2/src/main/java/com/baeldung/commons/convertunicode/UnicodeConverterUtil.java +++ b/libraries-apache-commons-2/src/main/java/com/baeldung/commons/convertunicode/UnicodeConverterUtil.java @@ -1,10 +1,10 @@ package com.baeldung.commons.convertunicode; -import org.apache.commons.text.StringEscapeUtils; - import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.text.StringEscapeUtils; + public class UnicodeConverterUtil { public static String decodeWithApacheCommons(String input) { @@ -15,7 +15,7 @@ public class UnicodeConverterUtil { Pattern pattern = Pattern.compile("\\\\u[0-9a-fA-F]{4}"); Matcher matcher = pattern.matcher(input); - StringBuilder decodedString = new StringBuilder(); + StringBuffer decodedString = new StringBuffer(); while (matcher.find()) { String unicodeSequence = matcher.group(); diff --git a/libraries-apache-commons-2/src/test/java/com/baeldung/commons/compress/CompressUtilsUnitTest.java b/libraries-apache-commons-2/src/test/java/com/baeldung/commons/compress/CompressUtilsUnitTest.java new file mode 100644 index 0000000000..9bdaf0dfd4 --- /dev/null +++ b/libraries-apache-commons-2/src/test/java/com/baeldung/commons/compress/CompressUtilsUnitTest.java @@ -0,0 +1,135 @@ +package com.baeldung.commons.compress; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.compressors.CompressorException; +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class CompressUtilsUnitTest { + + static Path TMP; + static String ZIP_FILE = "new.txt.zip"; + static String COMPRESSED_FILE = "new.txt.gz"; + static String DECOMPRESSED_FILE = "decompressed-file.txt"; + static String DECOMPRESSED_ARCHIVE = "decompressed-archive.tar"; + static String COMPRESSED_ARCHIVE = "archive.tar.gz"; + static String MODIFIED_ARCHIVE = "modified-archive.tar"; + static String EXTRACTED_DIR = "extracted"; + + @BeforeAll + static void setup() throws IOException { + TMP = Files.createTempDirectory("compress-test") + .toAbsolutePath(); + } + + @AfterAll + static void destroy() throws IOException { + FileUtils.deleteDirectory(TMP.toFile()); + } + + @Test + @Order(1) + void givenFile_whenCompressing_thenCompressed() throws IOException, CompressorException, URISyntaxException { + Path destination = TMP.resolve(COMPRESSED_FILE); + + CompressUtils.compressFile(TestResources.testFile(), destination); + + assertTrue(Files.isRegularFile(destination)); + } + + @Test + @Order(2) + void givenFile_whenZipping_thenZipFileCreated() throws IOException, URISyntaxException { + Path destination = TMP.resolve(ZIP_FILE); + + CompressUtils.zip(TestResources.testFile(), destination); + + assertTrue(Files.isRegularFile(destination)); + } + + @Test + @Order(3) + void givenCompressedArchive_whenDecompressing_thenArchiveAvailable() throws IOException, ArchiveException, CompressorException { + Path destination = TMP.resolve(DECOMPRESSED_ARCHIVE); + + CompressUtils.decompress(TestResources.compressedArchive(), destination); + + assertTrue(Files.isRegularFile(destination)); + } + + @Test + @Order(4) + void givenCompressedFile_whenDecompressing_thenFileAvailable() throws IOException, ArchiveException, CompressorException { + Path destination = TMP.resolve(DECOMPRESSED_FILE); + + CompressUtils.decompress(TMP.resolve(COMPRESSED_FILE), destination); + + assertTrue(Files.isRegularFile(destination)); + } + + @Test + @Order(5) + void givenDecompressedArchive_whenUnarchiving_thenFilesAvailable() throws IOException, ArchiveException, CompressorException { + Path destination = TMP.resolve(EXTRACTED_DIR); + + CompressUtils.extract(TMP.resolve(DECOMPRESSED_ARCHIVE), destination); + + assertTrue(Files.isDirectory(destination)); + } + + @Test + @Order(6) + void givenDirectory_whenArchivingAndCompressing_thenCompressedArchiveAvailable() throws IOException, ArchiveException, CompressorException { + Path destination = TMP.resolve(COMPRESSED_ARCHIVE); + + CompressUtils.archiveAndCompress(TMP.resolve(EXTRACTED_DIR), destination); + + assertTrue(Files.isRegularFile(destination)); + } + + @Test + @Order(7) + void givenExistingArchive_whenAddingSingleEntry_thenArchiveModified() throws IOException, ArchiveException, CompressorException, URISyntaxException { + Path archive = TMP.resolve(DECOMPRESSED_ARCHIVE); + Path newArchive = TMP.resolve(MODIFIED_ARCHIVE); + Path tmpDir = TMP.resolve(newArchive + "-tmpd"); + + Path newEntry = TestResources.testFile(); + + CompressUtils.extract(archive, tmpDir); + assertTrue(Files.isDirectory(tmpDir)); + + Files.copy(newEntry, tmpDir.resolve(newEntry.getFileName())); + CompressUtils.archive(tmpDir, newArchive); + assertTrue(Files.isRegularFile(newArchive)); + + FileUtils.deleteDirectory(tmpDir.toFile()); + Files.delete(archive); + Files.move(newArchive, archive); + assertTrue(Files.isRegularFile(archive)); + } + + @Test + @Order(8) + void givenExistingArchive_whenExtractingSingleEntry_thenFileExtracted() throws IOException, ArchiveException { + Path archive = TMP.resolve(DECOMPRESSED_ARCHIVE); + String targetFile = "sub/other.txt"; + + CompressUtils.extractOne(archive, targetFile, TMP); + + assertTrue(Files.isRegularFile(TMP.resolve(targetFile))); + } +} diff --git a/libraries-apache-commons-2/src/test/java/com/baeldung/commons/compress/TestResources.java b/libraries-apache-commons-2/src/test/java/com/baeldung/commons/compress/TestResources.java new file mode 100644 index 0000000000..6e1f4129cf --- /dev/null +++ b/libraries-apache-commons-2/src/test/java/com/baeldung/commons/compress/TestResources.java @@ -0,0 +1,25 @@ +package com.baeldung.commons.compress; + +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; + +public interface TestResources { + + String DIR = "/compress/"; + + static InputStream compressedArchive() { + return TestResources.class.getResourceAsStream(DIR + CompressUtilsUnitTest.COMPRESSED_ARCHIVE); + } + + static Path testFile() throws URISyntaxException { + URL resource = TestResources.class.getResource(DIR + "new.txt"); + if (resource == null) { + throw new IllegalArgumentException("file not found!"); + } else { + return Paths.get(resource.toURI()); + } + } +} \ No newline at end of file diff --git a/libraries-apache-commons-2/src/test/resources/compress/archive.tar.gz b/libraries-apache-commons-2/src/test/resources/compress/archive.tar.gz new file mode 100644 index 0000000000..cdb6fd3fd4 Binary files /dev/null and b/libraries-apache-commons-2/src/test/resources/compress/archive.tar.gz differ diff --git a/libraries-apache-commons-2/src/test/resources/compress/new.txt b/libraries-apache-commons-2/src/test/resources/compress/new.txt new file mode 100644 index 0000000000..f207b5eb40 --- /dev/null +++ b/libraries-apache-commons-2/src/test/resources/compress/new.txt @@ -0,0 +1,2 @@ +lorem ipsum +dolor sit amet diff --git a/libraries-data-3/pom.xml b/libraries-data-3/pom.xml index 35da822e7c..b32e5b5bf5 100644 --- a/libraries-data-3/pom.xml +++ b/libraries-data-3/pom.xml @@ -100,7 +100,7 @@ 1.2 0.7.0 3.24ea1 - 0.43 + 0.55.0 1.2.3.Final 2.1.2 8.2.0 diff --git a/libraries-data-3/src/main/java/com/baeldung/cactoos/CactoosCollectionUtils.java b/libraries-data-3/src/main/java/com/baeldung/cactoos/CactoosCollectionUtils.java index 717c63ae63..e3664e6b18 100644 --- a/libraries-data-3/src/main/java/com/baeldung/cactoos/CactoosCollectionUtils.java +++ b/libraries-data-3/src/main/java/com/baeldung/cactoos/CactoosCollectionUtils.java @@ -3,10 +3,13 @@ package com.baeldung.cactoos; import java.util.Collection; import java.util.List; -import org.cactoos.collection.Filtered; +import org.cactoos.func.FuncOf; +import org.cactoos.iterable.Filtered; import org.cactoos.iterable.IterableOf; +import org.cactoos.iterable.Mapped; import org.cactoos.list.ListOf; import org.cactoos.scalar.And; +import org.cactoos.scalar.True; import org.cactoos.text.FormattedText; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,13 +19,24 @@ public class CactoosCollectionUtils { final Logger LOGGER = LoggerFactory.getLogger(CactoosCollectionUtils.class); public void iterateCollection(List strings) throws Exception { - new And((String input) -> LOGGER.info(new FormattedText("%s\n", input).asString()), strings).value(); + new And( + new Mapped<>( + new FuncOf<>( + input -> System.out.printf("Item: %s\n", input), + new True() + ), + strings + ) + ).value(); } public Collection getFilteredList(List strings) { - Collection filteredStrings = new ListOf<>( - new Filtered<>(string -> string.length() == 5, new IterableOf<>(strings))); - return filteredStrings; + return new ListOf<>( + new Filtered<>( + s -> s.length() == 5, + strings + ) + ); } } diff --git a/libraries-data-3/src/main/java/com/baeldung/cactoos/CactoosStringUtils.java b/libraries-data-3/src/main/java/com/baeldung/cactoos/CactoosStringUtils.java index 3e2903ebf4..af82ad1ca4 100644 --- a/libraries-data-3/src/main/java/com/baeldung/cactoos/CactoosStringUtils.java +++ b/libraries-data-3/src/main/java/com/baeldung/cactoos/CactoosStringUtils.java @@ -10,17 +10,17 @@ import org.cactoos.text.Upper; public class CactoosStringUtils { - public String createString() throws IOException { + public String createString() throws Exception { String testString = new TextOf("Test String").asString(); return testString; } - public String createdFormattedString(String stringToFormat) throws IOException { + public String createdFormattedString(String stringToFormat) throws Exception { String formattedString = new FormattedText("Hello %s", stringToFormat).asString(); return formattedString; } - public String toLowerCase(String testString) throws IOException { + public String toLowerCase(String testString) throws Exception { String lowerCaseString = new Lowered(new TextOf(testString)).asString(); return lowerCaseString; } diff --git a/libraries-data-3/src/test/java/com/baeldung/cactoos/CactoosStringUtilsUnitTest.java b/libraries-data-3/src/test/java/com/baeldung/cactoos/CactoosStringUtilsUnitTest.java index 67dd6d91e4..00762e1ce6 100644 --- a/libraries-data-3/src/test/java/com/baeldung/cactoos/CactoosStringUtilsUnitTest.java +++ b/libraries-data-3/src/test/java/com/baeldung/cactoos/CactoosStringUtilsUnitTest.java @@ -9,7 +9,7 @@ import org.junit.Test; public class CactoosStringUtilsUnitTest { @Test - public void whenFormattedTextIsPassedWithArgs_thenFormattedStringIsReturned() throws IOException { + public void whenFormattedTextIsPassedWithArgs_thenFormattedStringIsReturned() throws Exception { CactoosStringUtils obj = new CactoosStringUtils(); diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/DefaultCatalog.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/DefaultCatalog.java new file mode 100644 index 0000000000..a730e9bfc6 --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/DefaultCatalog.java @@ -0,0 +1,56 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables; + + +import com.baeldung.jooq.jointables.public_.Public; + +import java.util.Arrays; +import java.util.List; + +import org.jooq.Constants; +import org.jooq.Schema; +import org.jooq.impl.CatalogImpl; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class DefaultCatalog extends CatalogImpl { + + private static final long serialVersionUID = 1L; + + /** + * The reference instance of DEFAULT_CATALOG + */ + public static final DefaultCatalog DEFAULT_CATALOG = new DefaultCatalog(); + + /** + * The schema public. + */ + public final Public PUBLIC = Public.PUBLIC; + + /** + * No further instances allowed + */ + private DefaultCatalog() { + super(""); + } + + @Override + public final List getSchemas() { + return Arrays.asList( + Public.PUBLIC + ); + } + + /** + * A reference to the 3.19 minor release of the code generator. If this + * doesn't compile, it's because the runtime library uses an older minor + * release, namely: 3.19. You can turn off the generation of this reference + * by specifying /configuration/generator/generate/jooqVersionReference + */ + private static final String REQUIRE_RUNTIME_JOOQ_VERSION = Constants.VERSION_3_19; +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/JoinTables.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/JoinTables.java new file mode 100644 index 0000000000..2c67ef2ad3 --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/JoinTables.java @@ -0,0 +1,75 @@ +package com.baeldung.jooq.jointables; + +import static org.jooq.impl.DSL.field; + +import org.jooq.DSLContext; +import org.jooq.Record; +import org.jooq.Result; +import org.jooq.SelectJoinStep; + +import com.baeldung.jooq.jointables.public_.Tables; + +public class JoinTables { + + public static Result usingJoinMethod(DSLContext context) { + SelectJoinStep query = context.select() + .from(Tables.BOOK) + .join(Tables.BOOKAUTHOR) + .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))); + return query.fetch(); + } + + public static Result usingMultipleJoinMethod(DSLContext context) { + SelectJoinStep query = context.select() + .from(Tables.BOOK) + .join(Tables.BOOKAUTHOR) + .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))) + .join(Tables.STORE) + .on(field(Tables.BOOK.STORE_ID).eq(field(Tables.STORE.ID))); + return query.fetch(); + } + + public static Result usingLeftOuterJoinMethod(DSLContext context) { + SelectJoinStep query = context.select() + .from(Tables.BOOK) + .leftOuterJoin(Tables.BOOKAUTHOR) + .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))); + return query.fetch(); + } + + public static Result usingRightOuterJoinMethod(DSLContext context) { + SelectJoinStep query = context.select() + .from(Tables.BOOK) + .rightOuterJoin(Tables.BOOKAUTHOR) + .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))); + return query.fetch(); + } + + public static Result usingFullOuterJoinMethod(DSLContext context) { + SelectJoinStep query = context.select() + .from(Tables.BOOK) + .fullOuterJoin(Tables.BOOKAUTHOR) + .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))); + return query.fetch(); + } + + public static Result usingNaturalJoinMethod(DSLContext context) { + SelectJoinStep query = context.select() + .from(Tables.BOOK) + .naturalJoin(Tables.BOOKAUTHOR); + return query.fetch(); + } + + public static Result usingCrossJoinMethod(DSLContext context) { + SelectJoinStep query = context.select() + .from(Tables.STORE) + .crossJoin(Tables.BOOK); + return query.fetch(); + } + + public static void printResult(Result result) { + for (Record record : result) { + System.out.println(record); + } + } +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Keys.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Keys.java new file mode 100644 index 0000000000..86c1939891 --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Keys.java @@ -0,0 +1,34 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_; + + +import com.baeldung.jooq.jointables.public_.tables.Book; +import com.baeldung.jooq.jointables.public_.tables.Bookauthor; +import com.baeldung.jooq.jointables.public_.tables.Store; +import com.baeldung.jooq.jointables.public_.tables.records.BookRecord; +import com.baeldung.jooq.jointables.public_.tables.records.BookauthorRecord; +import com.baeldung.jooq.jointables.public_.tables.records.StoreRecord; + +import org.jooq.TableField; +import org.jooq.UniqueKey; +import org.jooq.impl.DSL; +import org.jooq.impl.Internal; + + +/** + * A class modelling foreign key relationships and constraints of tables in + * public. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class Keys { + + // ------------------------------------------------------------------------- + // UNIQUE and PRIMARY KEY definitions + // ------------------------------------------------------------------------- + + public static final UniqueKey BOOK_PKEY = Internal.createUniqueKey(Book.BOOK, DSL.name("Book_pkey"), new TableField[] { Book.BOOK.ID }, true); + public static final UniqueKey AUTHOR_PKEY = Internal.createUniqueKey(Bookauthor.BOOKAUTHOR, DSL.name("Author_pkey"), new TableField[] { Bookauthor.BOOKAUTHOR.ID }, true); + public static final UniqueKey STORE_PKEY = Internal.createUniqueKey(Store.STORE, DSL.name("Store_pkey"), new TableField[] { Store.STORE.ID }, true); +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Public.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Public.java new file mode 100644 index 0000000000..1b2e8dda87 --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Public.java @@ -0,0 +1,69 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_; + + +import com.baeldung.jooq.jointables.DefaultCatalog; +import com.baeldung.jooq.jointables.public_.tables.Book; +import com.baeldung.jooq.jointables.public_.tables.Bookauthor; +import com.baeldung.jooq.jointables.public_.tables.Store; + +import java.util.Arrays; +import java.util.List; + +import org.jooq.Catalog; +import org.jooq.Table; +import org.jooq.impl.SchemaImpl; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class Public extends SchemaImpl { + + private static final long serialVersionUID = 1L; + + /** + * The reference instance of public + */ + public static final Public PUBLIC = new Public(); + + /** + * The table public.Book. + */ + public final Book BOOK = Book.BOOK; + + /** + * The table public.BookAuthor. + */ + public final Bookauthor BOOKAUTHOR = Bookauthor.BOOKAUTHOR; + + /** + * The table public.Store. + */ + public final Store STORE = Store.STORE; + + /** + * No further instances allowed + */ + private Public() { + super("public", null); + } + + + @Override + public Catalog getCatalog() { + return DefaultCatalog.DEFAULT_CATALOG; + } + + @Override + public final List> getTables() { + return Arrays.asList( + Book.BOOK, + Bookauthor.BOOKAUTHOR, + Store.STORE + ); + } +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Tables.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Tables.java new file mode 100644 index 0000000000..789c131a25 --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/Tables.java @@ -0,0 +1,32 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_; + + +import com.baeldung.jooq.jointables.public_.tables.Book; +import com.baeldung.jooq.jointables.public_.tables.Bookauthor; +import com.baeldung.jooq.jointables.public_.tables.Store; + + +/** + * Convenience access to all tables in public. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class Tables { + + /** + * The table public.Book. + */ + public static final Book BOOK = Book.BOOK; + + /** + * The table public.BookAuthor. + */ + public static final Bookauthor BOOKAUTHOR = Bookauthor.BOOKAUTHOR; + + /** + * The table public.Store. + */ + public static final Store STORE = Store.STORE; +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Book.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Book.java new file mode 100644 index 0000000000..4e21648cbd --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Book.java @@ -0,0 +1,238 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_.tables; + + +import com.baeldung.jooq.jointables.public_.Keys; +import com.baeldung.jooq.jointables.public_.Public; +import com.baeldung.jooq.jointables.public_.tables.records.BookRecord; + +import java.util.Collection; + +import org.jooq.Condition; +import org.jooq.Field; +import org.jooq.Name; +import org.jooq.PlainSQL; +import org.jooq.QueryPart; +import org.jooq.SQL; +import org.jooq.Schema; +import org.jooq.Select; +import org.jooq.Stringly; +import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.TableOptions; +import org.jooq.UniqueKey; +import org.jooq.impl.DSL; +import org.jooq.impl.SQLDataType; +import org.jooq.impl.TableImpl; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class Book extends TableImpl { + + private static final long serialVersionUID = 1L; + + /** + * The reference instance of public.Book + */ + public static final Book BOOK = new Book(); + + /** + * The class holding records for this type + */ + @Override + public Class getRecordType() { + return BookRecord.class; + } + + /** + * The column public.Book.id. + */ + public final TableField ID = createField(DSL.name("id"), SQLDataType.INTEGER.nullable(false), this, ""); + + /** + * The column public.Book.author_id. + */ + public final TableField AUTHOR_ID = createField(DSL.name("author_id"), SQLDataType.INTEGER, this, ""); + + /** + * The column public.Book.title. + */ + public final TableField TITLE = createField(DSL.name("title"), SQLDataType.VARCHAR, this, ""); + + /** + * The column public.Book.description. + */ + public final TableField DESCRIPTION = createField(DSL.name("description"), SQLDataType.VARCHAR, this, ""); + + /** + * The column public.Book.store_id. + */ + public final TableField STORE_ID = createField(DSL.name("store_id"), SQLDataType.INTEGER, this, ""); + + private Book(Name alias, Table aliased) { + this(alias, aliased, (Field[]) null, null); + } + + private Book(Name alias, Table aliased, Field[] parameters, Condition where) { + super(alias, null, aliased, parameters, DSL.comment(""), TableOptions.table(), where); + } + + /** + * Create an aliased public.Book table reference + */ + public Book(String alias) { + this(DSL.name(alias), BOOK); + } + + /** + * Create an aliased public.Book table reference + */ + public Book(Name alias) { + this(alias, BOOK); + } + + /** + * Create a public.Book table reference + */ + public Book() { + this(DSL.name("Book"), null); + } + + @Override + public Schema getSchema() { + return aliased() ? null : Public.PUBLIC; + } + + @Override + public UniqueKey getPrimaryKey() { + return Keys.BOOK_PKEY; + } + + @Override + public Book as(String alias) { + return new Book(DSL.name(alias), this); + } + + @Override + public Book as(Name alias) { + return new Book(alias, this); + } + + @Override + public Book as(Table alias) { + return new Book(alias.getQualifiedName(), this); + } + + /** + * Rename this table + */ + @Override + public Book rename(String name) { + return new Book(DSL.name(name), null); + } + + /** + * Rename this table + */ + @Override + public Book rename(Name name) { + return new Book(name, null); + } + + /** + * Rename this table + */ + @Override + public Book rename(Table name) { + return new Book(name.getQualifiedName(), null); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Book where(Condition condition) { + return new Book(getQualifiedName(), aliased() ? this : null, null, condition); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Book where(Collection conditions) { + return where(DSL.and(conditions)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Book where(Condition... conditions) { + return where(DSL.and(conditions)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Book where(Field condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Book where(SQL condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Book where(@Stringly.SQL String condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Book where(@Stringly.SQL String condition, Object... binds) { + return where(DSL.condition(condition, binds)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Book where(@Stringly.SQL String condition, QueryPart... parts) { + return where(DSL.condition(condition, parts)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Book whereExists(Select select) { + return where(DSL.exists(select)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Book whereNotExists(Select select) { + return where(DSL.notExists(select)); + } +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Bookauthor.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Bookauthor.java new file mode 100644 index 0000000000..9d828e9add --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Bookauthor.java @@ -0,0 +1,228 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_.tables; + + +import com.baeldung.jooq.jointables.public_.Keys; +import com.baeldung.jooq.jointables.public_.Public; +import com.baeldung.jooq.jointables.public_.tables.records.BookauthorRecord; + +import java.util.Collection; + +import org.jooq.Condition; +import org.jooq.Field; +import org.jooq.Name; +import org.jooq.PlainSQL; +import org.jooq.QueryPart; +import org.jooq.SQL; +import org.jooq.Schema; +import org.jooq.Select; +import org.jooq.Stringly; +import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.TableOptions; +import org.jooq.UniqueKey; +import org.jooq.impl.DSL; +import org.jooq.impl.SQLDataType; +import org.jooq.impl.TableImpl; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class Bookauthor extends TableImpl { + + private static final long serialVersionUID = 1L; + + /** + * The reference instance of public.BookAuthor + */ + public static final Bookauthor BOOKAUTHOR = new Bookauthor(); + + /** + * The class holding records for this type + */ + @Override + public Class getRecordType() { + return BookauthorRecord.class; + } + + /** + * The column public.BookAuthor.id. + */ + public final TableField ID = createField(DSL.name("id"), SQLDataType.INTEGER.nullable(false), this, ""); + + /** + * The column public.BookAuthor.name. + */ + public final TableField NAME = createField(DSL.name("name"), SQLDataType.VARCHAR.nullable(false), this, ""); + + /** + * The column public.BookAuthor.country. + */ + public final TableField COUNTRY = createField(DSL.name("country"), SQLDataType.VARCHAR, this, ""); + + private Bookauthor(Name alias, Table aliased) { + this(alias, aliased, (Field[]) null, null); + } + + private Bookauthor(Name alias, Table aliased, Field[] parameters, Condition where) { + super(alias, null, aliased, parameters, DSL.comment(""), TableOptions.table(), where); + } + + /** + * Create an aliased public.BookAuthor table reference + */ + public Bookauthor(String alias) { + this(DSL.name(alias), BOOKAUTHOR); + } + + /** + * Create an aliased public.BookAuthor table reference + */ + public Bookauthor(Name alias) { + this(alias, BOOKAUTHOR); + } + + /** + * Create a public.BookAuthor table reference + */ + public Bookauthor() { + this(DSL.name("BookAuthor"), null); + } + + @Override + public Schema getSchema() { + return aliased() ? null : Public.PUBLIC; + } + + @Override + public UniqueKey getPrimaryKey() { + return Keys.AUTHOR_PKEY; + } + + @Override + public Bookauthor as(String alias) { + return new Bookauthor(DSL.name(alias), this); + } + + @Override + public Bookauthor as(Name alias) { + return new Bookauthor(alias, this); + } + + @Override + public Bookauthor as(Table alias) { + return new Bookauthor(alias.getQualifiedName(), this); + } + + /** + * Rename this table + */ + @Override + public Bookauthor rename(String name) { + return new Bookauthor(DSL.name(name), null); + } + + /** + * Rename this table + */ + @Override + public Bookauthor rename(Name name) { + return new Bookauthor(name, null); + } + + /** + * Rename this table + */ + @Override + public Bookauthor rename(Table name) { + return new Bookauthor(name.getQualifiedName(), null); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Bookauthor where(Condition condition) { + return new Bookauthor(getQualifiedName(), aliased() ? this : null, null, condition); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Bookauthor where(Collection conditions) { + return where(DSL.and(conditions)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Bookauthor where(Condition... conditions) { + return where(DSL.and(conditions)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Bookauthor where(Field condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Bookauthor where(SQL condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Bookauthor where(@Stringly.SQL String condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Bookauthor where(@Stringly.SQL String condition, Object... binds) { + return where(DSL.condition(condition, binds)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Bookauthor where(@Stringly.SQL String condition, QueryPart... parts) { + return where(DSL.condition(condition, parts)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Bookauthor whereExists(Select select) { + return where(DSL.exists(select)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Bookauthor whereNotExists(Select select) { + return where(DSL.notExists(select)); + } +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Store.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Store.java new file mode 100644 index 0000000000..d97f3da21b --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/Store.java @@ -0,0 +1,223 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_.tables; + + +import com.baeldung.jooq.jointables.public_.Keys; +import com.baeldung.jooq.jointables.public_.Public; +import com.baeldung.jooq.jointables.public_.tables.records.StoreRecord; + +import java.util.Collection; + +import org.jooq.Condition; +import org.jooq.Field; +import org.jooq.Name; +import org.jooq.PlainSQL; +import org.jooq.QueryPart; +import org.jooq.SQL; +import org.jooq.Schema; +import org.jooq.Select; +import org.jooq.Stringly; +import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.TableOptions; +import org.jooq.UniqueKey; +import org.jooq.impl.DSL; +import org.jooq.impl.SQLDataType; +import org.jooq.impl.TableImpl; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class Store extends TableImpl { + + private static final long serialVersionUID = 1L; + + /** + * The reference instance of public.Store + */ + public static final Store STORE = new Store(); + + /** + * The class holding records for this type + */ + @Override + public Class getRecordType() { + return StoreRecord.class; + } + + /** + * The column public.Store.id. + */ + public final TableField ID = createField(DSL.name("id"), SQLDataType.INTEGER.nullable(false), this, ""); + + /** + * The column public.Store.name. + */ + public final TableField NAME = createField(DSL.name("name"), SQLDataType.VARCHAR.nullable(false), this, ""); + + private Store(Name alias, Table aliased) { + this(alias, aliased, (Field[]) null, null); + } + + private Store(Name alias, Table aliased, Field[] parameters, Condition where) { + super(alias, null, aliased, parameters, DSL.comment(""), TableOptions.table(), where); + } + + /** + * Create an aliased public.Store table reference + */ + public Store(String alias) { + this(DSL.name(alias), STORE); + } + + /** + * Create an aliased public.Store table reference + */ + public Store(Name alias) { + this(alias, STORE); + } + + /** + * Create a public.Store table reference + */ + public Store() { + this(DSL.name("Store"), null); + } + + @Override + public Schema getSchema() { + return aliased() ? null : Public.PUBLIC; + } + + @Override + public UniqueKey getPrimaryKey() { + return Keys.STORE_PKEY; + } + + @Override + public Store as(String alias) { + return new Store(DSL.name(alias), this); + } + + @Override + public Store as(Name alias) { + return new Store(alias, this); + } + + @Override + public Store as(Table alias) { + return new Store(alias.getQualifiedName(), this); + } + + /** + * Rename this table + */ + @Override + public Store rename(String name) { + return new Store(DSL.name(name), null); + } + + /** + * Rename this table + */ + @Override + public Store rename(Name name) { + return new Store(name, null); + } + + /** + * Rename this table + */ + @Override + public Store rename(Table name) { + return new Store(name.getQualifiedName(), null); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Store where(Condition condition) { + return new Store(getQualifiedName(), aliased() ? this : null, null, condition); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Store where(Collection conditions) { + return where(DSL.and(conditions)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Store where(Condition... conditions) { + return where(DSL.and(conditions)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Store where(Field condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Store where(SQL condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Store where(@Stringly.SQL String condition) { + return where(DSL.condition(condition)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Store where(@Stringly.SQL String condition, Object... binds) { + return where(DSL.condition(condition, binds)); + } + + /** + * Create an inline derived table from this table + */ + @Override + @PlainSQL + public Store where(@Stringly.SQL String condition, QueryPart... parts) { + return where(DSL.condition(condition, parts)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Store whereExists(Select select) { + return where(DSL.exists(select)); + } + + /** + * Create an inline derived table from this table + */ + @Override + public Store whereNotExists(Select select) { + return where(DSL.notExists(select)); + } +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/BookRecord.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/BookRecord.java new file mode 100644 index 0000000000..0636ff2fdf --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/BookRecord.java @@ -0,0 +1,124 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_.tables.records; + + +import com.baeldung.jooq.jointables.public_.tables.Book; + +import org.jooq.Record1; +import org.jooq.impl.UpdatableRecordImpl; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class BookRecord extends UpdatableRecordImpl { + + private static final long serialVersionUID = 1L; + + /** + * Setter for public.Book.id. + */ + public void setId(Integer value) { + set(0, value); + } + + /** + * Getter for public.Book.id. + */ + public Integer getId() { + return (Integer) get(0); + } + + /** + * Setter for public.Book.author_id. + */ + public void setAuthorId(Integer value) { + set(1, value); + } + + /** + * Getter for public.Book.author_id. + */ + public Integer getAuthorId() { + return (Integer) get(1); + } + + /** + * Setter for public.Book.title. + */ + public void setTitle(String value) { + set(2, value); + } + + /** + * Getter for public.Book.title. + */ + public String getTitle() { + return (String) get(2); + } + + /** + * Setter for public.Book.description. + */ + public void setDescription(String value) { + set(3, value); + } + + /** + * Getter for public.Book.description. + */ + public String getDescription() { + return (String) get(3); + } + + /** + * Setter for public.Book.store_id. + */ + public void setStoreId(Integer value) { + set(4, value); + } + + /** + * Getter for public.Book.store_id. + */ + public Integer getStoreId() { + return (Integer) get(4); + } + + // ------------------------------------------------------------------------- + // Primary key information + // ------------------------------------------------------------------------- + + @Override + public Record1 key() { + return (Record1) super.key(); + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Create a detached BookRecord + */ + public BookRecord() { + super(Book.BOOK); + } + + /** + * Create a detached, initialised BookRecord + */ + public BookRecord(Integer id, Integer authorId, String title, String description, Integer storeId) { + super(Book.BOOK); + + setId(id); + setAuthorId(authorId); + setTitle(title); + setDescription(description); + setStoreId(storeId); + resetChangedOnNotNull(); + } +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/BookauthorRecord.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/BookauthorRecord.java new file mode 100644 index 0000000000..0e99b2d93b --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/BookauthorRecord.java @@ -0,0 +1,94 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_.tables.records; + + +import com.baeldung.jooq.jointables.public_.tables.Bookauthor; + +import org.jooq.Record1; +import org.jooq.impl.UpdatableRecordImpl; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class BookauthorRecord extends UpdatableRecordImpl { + + private static final long serialVersionUID = 1L; + + /** + * Setter for public.BookAuthor.id. + */ + public void setId(Integer value) { + set(0, value); + } + + /** + * Getter for public.BookAuthor.id. + */ + public Integer getId() { + return (Integer) get(0); + } + + /** + * Setter for public.BookAuthor.name. + */ + public void setName(String value) { + set(1, value); + } + + /** + * Getter for public.BookAuthor.name. + */ + public String getName() { + return (String) get(1); + } + + /** + * Setter for public.BookAuthor.country. + */ + public void setCountry(String value) { + set(2, value); + } + + /** + * Getter for public.BookAuthor.country. + */ + public String getCountry() { + return (String) get(2); + } + + // ------------------------------------------------------------------------- + // Primary key information + // ------------------------------------------------------------------------- + + @Override + public Record1 key() { + return (Record1) super.key(); + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Create a detached BookauthorRecord + */ + public BookauthorRecord() { + super(Bookauthor.BOOKAUTHOR); + } + + /** + * Create a detached, initialised BookauthorRecord + */ + public BookauthorRecord(Integer id, String name, String country) { + super(Bookauthor.BOOKAUTHOR); + + setId(id); + setName(name); + setCountry(country); + resetChangedOnNotNull(); + } +} diff --git a/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/StoreRecord.java b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/StoreRecord.java new file mode 100644 index 0000000000..f8628818e3 --- /dev/null +++ b/persistence-modules/jooq/src/main/java/com/baeldung/jooq/jointables/public_/tables/records/StoreRecord.java @@ -0,0 +1,79 @@ +/* + * This file is generated by jOOQ. + */ +package com.baeldung.jooq.jointables.public_.tables.records; + + +import com.baeldung.jooq.jointables.public_.tables.Store; + +import org.jooq.Record1; +import org.jooq.impl.UpdatableRecordImpl; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" }) +public class StoreRecord extends UpdatableRecordImpl { + + private static final long serialVersionUID = 1L; + + /** + * Setter for public.Store.id. + */ + public void setId(Integer value) { + set(0, value); + } + + /** + * Getter for public.Store.id. + */ + public Integer getId() { + return (Integer) get(0); + } + + /** + * Setter for public.Store.name. + */ + public void setName(String value) { + set(1, value); + } + + /** + * Getter for public.Store.name. + */ + public String getName() { + return (String) get(1); + } + + // ------------------------------------------------------------------------- + // Primary key information + // ------------------------------------------------------------------------- + + @Override + public Record1 key() { + return (Record1) super.key(); + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Create a detached StoreRecord + */ + public StoreRecord() { + super(Store.STORE); + } + + /** + * Create a detached, initialised StoreRecord + */ + public StoreRecord(Integer id, String name) { + super(Store.STORE); + + setId(id); + setName(name); + resetChangedOnNotNull(); + } +} diff --git a/persistence-modules/jooq/src/test/java/com/baeldung/jooq/jointables/JoinTablesIntegrationTest.java b/persistence-modules/jooq/src/test/java/com/baeldung/jooq/jointables/JoinTablesIntegrationTest.java new file mode 100644 index 0000000000..5396c27e14 --- /dev/null +++ b/persistence-modules/jooq/src/test/java/com/baeldung/jooq/jointables/JoinTablesIntegrationTest.java @@ -0,0 +1,111 @@ +package com.baeldung.jooq.jointables; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.sql.Connection; +import java.sql.DriverManager; + +import org.jooq.DSLContext; +import org.jooq.Record; +import org.jooq.Result; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.baeldung.jooq.jointables.public_.Tables; +import com.baeldung.jooq.jointables.public_.tables.Book; +import com.baeldung.jooq.jointables.public_.tables.Bookauthor; +import com.baeldung.jooq.jointables.public_.tables.Store; + +public class JoinTablesIntegrationTest { + + static DSLContext context; + + @BeforeClass + public static void setUp() throws Exception { + // URL jooqConfigURL = getClass().getClassLoader().getResource("jooq-config-2.xml"); + // File file = new File(jooqConfigURL.getFile()); + // GenerationTool.generate(Files.readString(file.toPath())); + + String url = "jdbc:postgresql://localhost:5432/postgres"; + String username = "postgres"; + String password = ""; + + Connection conn = DriverManager.getConnection(url, username, password); + context = DSL.using(conn, SQLDialect.POSTGRES); + + context.insertInto(Tables.STORE, Store.STORE.ID, Store.STORE.NAME) + .values(1, "ABC Branch I ") + .values(2, "ABC Branch II") + .execute(); + + context.insertInto(Tables.BOOK, Book.BOOK.ID, Book.BOOK.TITLE, Book.BOOK.DESCRIPTION, Book.BOOK.AUTHOR_ID, Book.BOOK.STORE_ID) + .values(1, "Book 1", "This is book 1", 1, 1) + .values(2, "Book 2", "This is book 2", 2, 2) + .values(3, "Book 3", "This is book 3", 1, 2) + .values(4, "Book 4", "This is book 4", 5, 1) + .execute(); + + context.insertInto(Tables.BOOKAUTHOR, Bookauthor.BOOKAUTHOR.ID, Bookauthor.BOOKAUTHOR.NAME, Bookauthor.BOOKAUTHOR.COUNTRY) + .values(1, "John Smith", "Japan") + .values(2, "William Walce", "Japan") + .values(3, "Marry Sity", "South Korea") + .values(4, "Morry Toh", "England") + .execute(); + } + + @AfterClass + public static void cleanup() throws Exception { + context.truncateTable(Store.STORE) + .execute(); + context.truncateTable(Book.BOOK) + .execute(); + context.truncateTable(Bookauthor.BOOKAUTHOR) + .execute(); + } + + @Test + public void _whenUsingJoinMethod_thenQueryExecuted() { + Result result = JoinTables.usingJoinMethod(context); + assertEquals(3, result.size()); + } + + @Test + public void _whenUsingMultipleJoinMethod_thenQueryExecuted() { + Result result = JoinTables.usingMultipleJoinMethod(context); + assertEquals(3, result.size()); + } + + @Test + public void givenContext_whenUsingLeftOuterJoinMethod_thenQueryExecuted() { + Result result = JoinTables.usingLeftOuterJoinMethod(context); + assertEquals(4, result.size()); + } + + @Test + public void whenUsingRightOuterJoinMethod_thenQueryExecuted() { + Result result = JoinTables.usingRightOuterJoinMethod(context); + assertEquals(5, result.size()); + } + + @Test + public void whenUsingFullOuterJoinMethod_thenQueryExecuted() { + Result result = JoinTables.usingFullOuterJoinMethod(context); + assertEquals(6, result.size()); + } + + @Test + public void whenUsingNaturalJoinMethod_thenQueryExecuted() { + Result result = JoinTables.usingNaturalJoinMethod(context); + assertEquals(4, result.size()); + } + + @Test + public void whenUsingCrossJoinMethod_thenQueryExecuted() { + Result result = JoinTables.usingCrossJoinMethod(context); + assertEquals(8, result.size()); + } +} diff --git a/persistence-modules/jooq/src/test/resources/jooq-config-2.xml b/persistence-modules/jooq/src/test/resources/jooq-config-2.xml new file mode 100644 index 0000000000..a644e3fef5 --- /dev/null +++ b/persistence-modules/jooq/src/test/resources/jooq-config-2.xml @@ -0,0 +1,19 @@ + + + org.postgresql.Driver + jdbc:postgresql://localhost:5432/postgres + postgres + + + + + org.jooq.meta.postgres.PostgresDatabase + Store|Book|BookAuthor + + + + com.baeldung.jooq.jointables + src/main/java + + + diff --git a/persistence-modules/pom.xml b/persistence-modules/pom.xml index 188ea132c6..7512e99441 100644 --- a/persistence-modules/pom.xml +++ b/persistence-modules/pom.xml @@ -88,6 +88,7 @@ spring-data-jpa-query spring-data-jpa-query-2 spring-data-jpa-query-3 + spring-data-jpa-query-4 spring-data-jpa-repo spring-data-jpa-repo-2 spring-data-jpa-repo-4 @@ -107,6 +108,7 @@ spring-hibernate-6 spring-jpa spring-jpa-2 + spring-jpa-3 spring-jdbc spring-jdbc-2 diff --git a/persistence-modules/spring-data-jpa-query-4/pom.xml b/persistence-modules/spring-data-jpa-query-4/pom.xml new file mode 100644 index 0000000000..3b843aed20 --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + spring-data-jpa-query-4 + spring-data-jpa-query-4 + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + com.h2database + h2 + + + org.postgresql + postgresql + ${postgresql.version} + + + + + 42.7.1 + + + \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/Product.java b/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/Product.java new file mode 100644 index 0000000000..ffe81fa13b --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/Product.java @@ -0,0 +1,52 @@ +package com.baeldung.spring.data.jpa.queryjsonb; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Product { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + @Column(columnDefinition = "jsonb") + private String attributes; + + public Product() { + } + + public Product(String name, String attributes) { + this.name = name; + this.attributes = attributes; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAttributes() { + return attributes; + } + + public void setAttributes(String attributes) { + this.attributes = attributes; + } + +} diff --git a/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductApplication.java b/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductApplication.java new file mode 100644 index 0000000000..dc237ddf58 --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.spring.data.jpa.queryjsonb; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ProductApplication { + + public static void main(String[] args) { + SpringApplication.run(ProductApplication.class); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductRepository.java b/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductRepository.java new file mode 100644 index 0000000000..8667afb3f4 --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductRepository.java @@ -0,0 +1,24 @@ +package com.baeldung.spring.data.jpa.queryjsonb; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface ProductRepository extends JpaRepository, JpaSpecificationExecutor { + @Query(value = "SELECT * FROM product WHERE attributes ->> ?1 = ?2", nativeQuery = true) + List findByAttribute(String key, String value); + + @Query(value = "SELECT * FROM product WHERE attributes -> ?1 ->> ?2 = ?3", nativeQuery = true) + List findByNestedAttribute(String key1, String key2, String value); + + @Query(value = "SELECT * FROM product WHERE jsonb_extract_path_text(attributes, ?1) = ?2", nativeQuery = true) + List findByJsonPath(String path, String value); + + @Query(value = "SELECT * FROM product WHERE jsonb_extract_path_text(attributes, ?1, ?2) = ?3", nativeQuery = true) + List findByNestedJsonPath(String key1, String key2, String value); + + +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductSpecification.java b/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductSpecification.java new file mode 100644 index 0000000000..65c9507cf0 --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/src/main/java/com/baeldung/spring/data/jpa/queryjsonb/ProductSpecification.java @@ -0,0 +1,22 @@ +package com.baeldung.spring.data.jpa.queryjsonb; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import org.springframework.data.jpa.domain.Specification; +public class ProductSpecification implements Specification { + + private final String key; + private final String value; + + public ProductSpecification(String key, String value) { + this.key = key; this.value = value; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + return cb.equal(cb.function("jsonb_extract_path_text", String.class, root.get("attributes"), cb.literal(key)), value); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-query-4/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-query-4/src/main/resources/application.properties new file mode 100644 index 0000000000..de0685b9bd --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/src/main/resources/application.properties @@ -0,0 +1,10 @@ +spring.datasource.url=jdbc:postgresql://localhost:5432/postgres +spring.datasource.username=postgres +spring.datasource.password= +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.hibernate.ddl-auto=create +spring.jpa.show-sql=true + +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +spring.jpa.properties.hibernate.format_sql=true \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-query-4/src/test/java/com/baeldung/spring/data/jpa/queryjsonb/ProductRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-query-4/src/test/java/com/baeldung/spring/data/jpa/queryjsonb/ProductRepositoryIntegrationTest.java new file mode 100644 index 0000000000..2385590d7f --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/src/test/java/com/baeldung/spring/data/jpa/queryjsonb/ProductRepositoryIntegrationTest.java @@ -0,0 +1,77 @@ +package com.baeldung.spring.data.jpa.queryjsonb; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import javax.transaction.Transactional; + +import org.junit.Before; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.PageImpl; +import org.springframework.test.annotation.Commit; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.transaction.annotation.Propagation; +import com.fasterxml.jackson.databind.ObjectMapper; + +@SpringBootTest +@ActiveProfiles("test") +@Sql(scripts = "/testdata.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) +public class ProductRepositoryIntegrationTest { + + @Autowired + private ProductRepository productRepository; + + @Test + void whenFindByAttribute_thenReturnTheObject() { + List redProducts = productRepository.findByAttribute("color", "red"); + + assertEquals(1, redProducts.size()); + assertEquals("Laptop", redProducts.get(0).getName()); + } + + @Test + void whenFindByNestedAttribute_thenReturnTheObject() { + List electronicProducts = productRepository.findByNestedAttribute("details", "category", "electronics"); + + assertEquals(1, electronicProducts.size()); + assertEquals("Headphones", electronicProducts.get(0) + .getName()); + } + + @Test + void whenFindByJsonPath_thenReturnTheObject() { + List redProducts = productRepository.findByJsonPath("color", "red"); + assertEquals(1, redProducts.size()); + assertEquals("Laptop", redProducts.get(0) + .getName()); + } + + @Test + void givenNestedJsonAttribute_whenFindByJsonPath_thenReturnTheObject() { + List electronicProducts = productRepository.findByNestedJsonPath("details", "category", "electronics"); + assertEquals(1, electronicProducts.size()); + assertEquals("Headphones", electronicProducts.get(0) + .getName()); + } + + @Test + void whenUsingJPASpecification_thenReturnTheObject() { + ProductSpecification spec = new ProductSpecification("color", "red"); + Page redProducts = productRepository.findAll(spec, Pageable.unpaged()); + + assertEquals(1, redProducts.getContent() + .size()); + assertEquals("Laptop", redProducts.getContent() + .get(0) + .getName()); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-query-4/src/test/resources/application-test.properties b/persistence-modules/spring-data-jpa-query-4/src/test/resources/application-test.properties new file mode 100644 index 0000000000..af1e12cb9c --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/src/test/resources/application-test.properties @@ -0,0 +1,10 @@ +spring.datasource.url=jdbc:postgresql://localhost:5432/postgres +spring.datasource.username=postgres +spring.datasource.password= +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.hibernate.ddl-auto=create +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true + +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE diff --git a/persistence-modules/spring-data-jpa-query-4/src/test/resources/testdata.sql b/persistence-modules/spring-data-jpa-query-4/src/test/resources/testdata.sql new file mode 100644 index 0000000000..71e9a3123d --- /dev/null +++ b/persistence-modules/spring-data-jpa-query-4/src/test/resources/testdata.sql @@ -0,0 +1,13 @@ +DELETE FROM product; + +INSERT INTO product (name, attributes) +VALUES ('Laptop', '{"color": "red", "size": "15 inch"}'); + +INSERT INTO product (name, attributes) +VALUES ('Phone', '{"color": "blue", "size": "6 inch"}'); + +INSERT INTO product (name, attributes) +VALUES ('Headphones', '{"brand": "Sony", "details": {"category": "electronics", "model": "WH-1000XM4"}}'); + +INSERT INTO product (name, attributes) +VALUES ('Laptop', '{"brand": "Dell", "details": {"category": "computers", "model": "XPS 13"}}'); \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/AppConfig.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/AppConfig.java new file mode 100644 index 0000000000..840d26edf7 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/AppConfig.java @@ -0,0 +1,52 @@ +package com.baeldung.spring.data.jpa.joinquery; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +import javax.sql.DataSource; +import java.util.Properties; + +@Configuration +@EnableAutoConfiguration +public class AppConfig { + + @Bean + public DataSource dataSource() { + return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2) + .build(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { + LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); + emf.setDataSource(dataSource); + emf.setPackagesToScan("com.baeldung.spring.data.jpa.joinquery.entities" + , "com.baeldung.spring.data.jpa.joinquery.DTO" + , "com.baeldung.spring.data.jpa.joinquery.repositories"); + emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); + emf.setJpaProperties(getHibernateProperties()); + return emf; + } + + @Bean + public JpaTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactory) { + return new JpaTransactionManager(entityManagerFactory.getObject()); + } + + private Properties getHibernateProperties() { + Properties properties = new Properties(); + properties.setProperty("hibernate.hbm2ddl.auto", "create"); + properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); + return properties; + } +} + + + + diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/DTO/ResultDTO.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/DTO/ResultDTO.java new file mode 100644 index 0000000000..9464c25ce8 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/DTO/ResultDTO.java @@ -0,0 +1,119 @@ +package com.baeldung.spring.data.jpa.joinquery.DTO; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import org.springframework.context.annotation.Bean; + +import java.time.LocalDate; + +class DTO { + private Long customer_id; + private Long order_id; + private Long product_id; + + public DTO(Long customer_id, Long order_id, Long product_id) { + this.customer_id = customer_id; + this.order_id = order_id; + this.product_id = product_id; + } +} + +@Entity +@IdClass(DTO.class) +public class ResultDTO { + @Id + private Long customer_id; + + @Id + private Long order_id; + + @Id + private Long product_id; + + public void setCustomer_id(Long customer_id) { + this.customer_id = customer_id; + } + + public Long getProduct_id() { + return product_id; + } + + public void setProduct_id(Long product_id) { + this.product_id = product_id; + } + + public ResultDTO(Long customer_id, Long order_id, Long product_id, String customerName, String customerEmail, LocalDate orderDate, String productName, Double productPrice) { + this.customer_id = customer_id; + this.order_id = order_id; + this.product_id = product_id; + this.customerName = customerName; + this.customerEmail = customerEmail; + this.orderDate = orderDate; + this.productName = productName; + this.productPrice = productPrice; + } + + private String customerName; + private String customerEmail; + private LocalDate orderDate; + private String productName; + private Double productPrice; + + public Long getCustomer_id() { + return customer_id; + } + + public void setCustoemr_id(Long custoemr_id) { + this.customer_id = custoemr_id; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public String getCustomerEmail() { + return customerEmail; + } + + public void setCustomerEmail(String customerEmail) { + this.customerEmail = customerEmail; + } + + public Long getOrder_id() { + return order_id; + } + + public void setOrder_id(Long order_id) { + this.order_id = order_id; + } + + public LocalDate getOrderDate() { + return orderDate; + } + + public void setOrderDate(LocalDate orderDate) { + this.orderDate = orderDate; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public Double getProductPrice() { + return productPrice; + } + + public void setProductPrice(Double productPrice) { + this.productPrice = productPrice; + } + +} diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/DTO/ResultDTO_wo_Ids.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/DTO/ResultDTO_wo_Ids.java new file mode 100644 index 0000000000..540ad9fc91 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/DTO/ResultDTO_wo_Ids.java @@ -0,0 +1,66 @@ +package com.baeldung.spring.data.jpa.joinquery.DTO; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; + +import java.time.LocalDate; + +//@Entity +//@IdClass(DTO.class) +public class ResultDTO_wo_Ids { + public ResultDTO_wo_Ids(String customerName, String customerEmail, LocalDate orderDate, String productName, Double productPrice) { + this.customerName = customerName; + this.customerEmail = customerEmail; + this.orderDate = orderDate; + this.productName = productName; + this.productPrice = productPrice; + } + + private String customerName; + private String customerEmail; + private LocalDate orderDate; + private String productName; + private Double productPrice; + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public String getCustomerEmail() { + return customerEmail; + } + + public void setCustomerEmail(String customerEmail) { + this.customerEmail = customerEmail; + } + + public LocalDate getOrderDate() { + return orderDate; + } + + public void setOrderDate(LocalDate orderDate) { + this.orderDate = orderDate; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public Double getProductPrice() { + return productPrice; + } + + public void setProductPrice(Double productPrice) { + this.productPrice = productPrice; + } + +} diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/Customer.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/Customer.java new file mode 100644 index 0000000000..8da7aa21a2 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/Customer.java @@ -0,0 +1,104 @@ +package com.baeldung.spring.data.jpa.joinquery.entities; + +import jakarta.persistence.*; + +import java.time.LocalDate; +import java.util.Objects; +import java.util.Set; + +@Entity +public class Customer { + public Customer(){} + @Id + //@GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToMany(mappedBy = "customer" + , cascade = CascadeType.ALL) + private Set customerOrders; + + public Customer(Long id, Set customerOrders, String email, LocalDate dob, String name) { + this.id = id; + this.customerOrders = customerOrders; + this.email = email; + this.dob = dob; + this.name = name; + } + + public void setId(Long id) { + this.id = id; + } + + public void setCustomerOrders(Set customerOrders) { + this.customerOrders = customerOrders; + } + + @Override + public String toString() { + return "Patient{" + + "id=" + id + + ", orders=" + customerOrders + + ", email='" + email + '\'' + + ", dob=" + dob + + ", name='" + name + '\'' + + '}'; + } + + public Set getOrders() { + return customerOrders; + } + + public void setOrders(Set orders) { + this.customerOrders = orders; + } + + @Column + private String email; + + @Column(name = "dob", columnDefinition = "DATE") + private LocalDate dob; + + @Column + private String name; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Customer patient = (Customer) o; + return Objects.equals(id, patient.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + public Long getId() { + return id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public LocalDate getDob() { + return dob; + } + + public void setDob(LocalDate dob) { + this.dob = dob; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/CustomerOrder.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/CustomerOrder.java new file mode 100644 index 0000000000..d147543e2f --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/CustomerOrder.java @@ -0,0 +1,88 @@ +package com.baeldung.spring.data.jpa.joinquery.entities; + +import jakarta.persistence.*; + +import java.time.LocalDate; +import java.util.Objects; +import java.util.Set; + +@Entity +public class CustomerOrder { + public CustomerOrder(){} + @Id + //@GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + public void setId(Long id) { + this.id = id; + } + + public Set getProducts() { + return products; + } + + public void setProducts(Set products) { + this.products = products; + } + + @OneToMany(mappedBy = "customerOrder" + , cascade = CascadeType.ALL) + private Set products; + + @Column + private LocalDate orderDate; + + public CustomerOrder(Long id, Set products, LocalDate orderDate, Customer customer) { + this.id = id; + this.products = products; + this.orderDate = orderDate; + this.customer = customer; + } + + @Override + public String toString() { + return "Consult{" + + "id=" + id + + ", products=" + products + + ", orderDate=" + orderDate + + ", customer=" + customer + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CustomerOrder co = (CustomerOrder) o; + return Objects.equals(id, co.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="customer_id", nullable = false) + private Customer customer; + + public Long getId() { + return id; + } + + public LocalDate getOrderDate() { + return orderDate; + } + + public void setOrderDate(LocalDate orderDate) { + this.orderDate = orderDate; + } + + public Customer getCustomer() { + return customer; + } + + public void setCustomer(Customer customer) { + this.customer = customer; + } +} diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/Product.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/Product.java new file mode 100644 index 0000000000..ea18e6225e --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/entities/Product.java @@ -0,0 +1,84 @@ +package com.baeldung.spring.data.jpa.joinquery.entities; + +import jakarta.persistence.*; + +import java.util.Objects; + +@Entity +public class Product { + public Product(){} + @Id + //@GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + public Product(Long id, String productName, Double price, CustomerOrder customerOrder) { + this.id = id; + this.productName = productName; + this.price = price; + this.customerOrder = customerOrder; + } + + @Column + private String productName; + + @Column + private Double price; + + @Override + public String toString() { + return "Dispense{" + + "id=" + id + + ", productName='" + productName + '\'' + + ", price=" + price + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Product dispense = (Product) o; + return Objects.equals(id, dispense.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public CustomerOrder getCustomerOrder() { + return customerOrder; + } + + public void setCustomerOrder(CustomerOrder co) { + this.customerOrder = co; + } + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="customerorder_id", nullable = false) + private CustomerOrder customerOrder; +} diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/repositories/CustomerRepository.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/repositories/CustomerRepository.java new file mode 100644 index 0000000000..ba7ece74b8 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/repositories/CustomerRepository.java @@ -0,0 +1,9 @@ +package com.baeldung.spring.data.jpa.joinquery.repositories; + +import com.baeldung.spring.data.jpa.joinquery.entities.Customer; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CustomerRepository extends CrudRepository { +} diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/repositories/JoinRepository.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/repositories/JoinRepository.java new file mode 100644 index 0000000000..056c91f973 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/joinquery/repositories/JoinRepository.java @@ -0,0 +1,34 @@ +package com.baeldung.spring.data.jpa.joinquery.repositories; + +import com.baeldung.spring.data.jpa.joinquery.DTO.ResultDTO; +import com.baeldung.spring.data.jpa.joinquery.DTO.ResultDTO_wo_Ids; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Map; + +@Repository +public interface JoinRepository extends CrudRepository { + @Query(value = "SELECT new com.baeldung.spring.data.jpa.joinquery.DTO.ResultDTO(c.id, o.id, p.id, c.name, c.email, o.orderDate, p.productName, p.price) " + + " from Customer c, CustomerOrder o ,Product p " + + " where c.id=o.customer.id " + + " and o.id=p.customerOrder.id " + + " and c.id=?1 ") + List findResultDTOByCustomer(Long id); + + @Query(value = "SELECT new com.baeldung.spring.data.jpa.joinquery.DTO.ResultDTO_wo_Ids(c.name, c.email, o.orderDate, p.productName, p.price) " + + " from Customer c, CustomerOrder o ,Product p " + + " where c.id=o.customer.id " + + " and o.id=p.customerOrder.id " + + " and c.id=?1 ") + List findResultDTOByCustomerWithoutIds(Long id); + + @Query(value = "SELECT c.*, o.*, p.* " + + " from Customer c, CustomerOrder o ,Product p " + + " where c.id=o.customer_id " + + " and o.id=p.customerOrder_id " + + " and c.id=?1 " + , nativeQuery = true) + List> findByCustomer(Long id); +} diff --git a/persistence-modules/spring-data-jpa-repo-3/src/test/java/com/baeldung/spring/data/jpa/joinquery/JoinRepositoryUnitTest.java b/persistence-modules/spring-data-jpa-repo-3/src/test/java/com/baeldung/spring/data/jpa/joinquery/JoinRepositoryUnitTest.java new file mode 100644 index 0000000000..dd91399128 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-3/src/test/java/com/baeldung/spring/data/jpa/joinquery/JoinRepositoryUnitTest.java @@ -0,0 +1,106 @@ +package com.baeldung.spring.data.jpa.joinquery; + +import com.baeldung.spring.data.jpa.joinquery.DTO.ResultDTO_wo_Ids; +import com.baeldung.spring.data.jpa.joinquery.entities.Customer; +import com.baeldung.spring.data.jpa.joinquery.entities.CustomerOrder; +import com.baeldung.spring.data.jpa.joinquery.DTO.ResultDTO; +import com.baeldung.spring.data.jpa.joinquery.entities.Product; +import com.baeldung.spring.data.jpa.joinquery.repositories.CustomerRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.PersistenceUnit; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import com.baeldung.spring.data.jpa.joinquery.repositories.JoinRepository; +import java.time.LocalDate; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { AppConfig.class }) +class JoinRepositoryUnitTest { + + @Autowired + JoinRepository joinRepository; + + @Autowired + CustomerRepository customerRepository; + + @PersistenceUnit + EntityManagerFactory entityManagerFactory; + + EntityManager entityManager; + + @BeforeEach + void setUp() { + customerRepository.deleteAll(); + entityManager = entityManagerFactory.createEntityManager(); + Customer c = new Customer(1L, null, "email1@email.com", LocalDate.now(), "Customer"); + CustomerOrder o1 = new CustomerOrder(1L, null, LocalDate.now(), c); + CustomerOrder o2 = new CustomerOrder(2L, null, LocalDate.of(2024,1,1), c); + + Product p1 = new Product(1L,"Product1", 25.20, o1); + Product p2 = new Product(2L, "Product2", 26.20, o1); + Product p3 = new Product(3L, "Product3", 27.20, o2); + + Set cos01 = new HashSet<>(); + cos01.add(o1); + cos01.add(o2); + Set productsO1 = new HashSet<>(); + productsO1.add(p1); + productsO1.add(p2); + Set productsO2 = new HashSet<>(); + productsO1.add(p3); + + c.setCustomerOrders(cos01); + o1.setProducts(productsO1); + o2.setProducts(productsO2); + + EntityTransaction et = getTransaction(); + entityManager.persist(c); + entityManager.flush(); + et.commit(); + } + + @AfterEach + void tearDown() { + } + + @Test + void whenFindResultDTOByCustomer_thenReturnResultDTO() { + List resultDTO = joinRepository.findResultDTOByCustomer(1L); + assertInstanceOf(ResultDTO.class, resultDTO.get(0)); + } + + @Test + void whenFindByCustomerNativeQuery_thenReturnMapsForObjects() { + List> res = joinRepository.findByCustomer(1L); + //the map objects in the res List has 9 String keys aka 9 columns corresponding to the columns of customer, customerOrder, product + res.forEach(map -> { + assertEquals(9, map.keySet().size()); + }); + } + + @Test + void whenFindResultDTOByCustomerWithoutIds_thenReturnResultDTO() { + List resultDTO = joinRepository.findResultDTOByCustomerWithoutIds(1L); + assertInstanceOf(ResultDTO_wo_Ids.class, resultDTO.get(0)); + } + + private EntityTransaction getTransaction() { + EntityTransaction transaction = entityManager.getTransaction(); + transaction.begin(); + return transaction; + } +} \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/config/PersistenceConfig.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/config/PersistenceConfig.java index 36a1fa9389..b142933240 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/config/PersistenceConfig.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/config/PersistenceConfig.java @@ -32,7 +32,7 @@ public class PersistenceConfig { public LocalContainerEntityManagerFactoryBean entityManagerFactory() { final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); - em.setPackagesToScan("com.baeldung.jpa.simple.entity"); + em.setPackagesToScan("com.baeldung.jpa.simple.model"); final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/Book.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/Book.java similarity index 96% rename from persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/Book.java rename to persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/Book.java index 0e01e983b5..30e9003b2f 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/Book.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/Book.java @@ -1,4 +1,4 @@ -package com.baeldung.jpa.simple.entity; +package com.baeldung.jpa.simple.model; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/Foo.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/Foo.java similarity index 97% rename from persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/Foo.java rename to persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/Foo.java index bf82ab61f7..5cabacf389 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/Foo.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/Foo.java @@ -1,4 +1,4 @@ -package com.baeldung.jpa.simple.entity; +package com.baeldung.jpa.simple.model; import java.io.Serializable; diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/User.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/User.java similarity index 97% rename from persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/User.java rename to persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/User.java index 07759af0fb..f9a2db8437 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/entity/User.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/model/User.java @@ -1,4 +1,4 @@ -package com.baeldung.jpa.simple.entity; +package com.baeldung.jpa.simple.model; import java.time.ZonedDateTime; diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/BookListRepository.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/BookListRepository.java index e1540c64bf..e7a781defd 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/BookListRepository.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/BookListRepository.java @@ -5,7 +5,7 @@ import java.util.List; import org.springframework.data.repository.ListCrudRepository; import org.springframework.stereotype.Repository; -import com.baeldung.jpa.simple.entity.Book; +import com.baeldung.jpa.simple.model.Book; @Repository public interface BookListRepository extends ListCrudRepository { diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/BookPagingAndSortingRepository.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/BookPagingAndSortingRepository.java index cb519ed5ea..b802406c1b 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/BookPagingAndSortingRepository.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/BookPagingAndSortingRepository.java @@ -7,7 +7,7 @@ import org.springframework.data.repository.ListCrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.stereotype.Repository; -import com.baeldung.jpa.simple.entity.Book; +import com.baeldung.jpa.simple.model.Book; @Repository public interface BookPagingAndSortingRepository extends PagingAndSortingRepository, ListCrudRepository { diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/IFooDAO.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/IFooDAO.java index d6d1d46122..20123c73cd 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/IFooDAO.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/IFooDAO.java @@ -4,7 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import com.baeldung.jpa.simple.entity.Foo; +import com.baeldung.jpa.simple.model.Foo; public interface IFooDAO extends JpaRepository { diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepository.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepository.java index cc13aac5db..368a0bcb5b 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepository.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepository.java @@ -6,7 +6,7 @@ import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -import com.baeldung.jpa.simple.entity.User; +import com.baeldung.jpa.simple.model.User; public interface UserRepository extends JpaRepository, UserRepositoryCustom { diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepositoryCustom.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepositoryCustom.java index 8584f3f3bb..e4e1a3e73b 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepositoryCustom.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepositoryCustom.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.Set; import java.util.function.Predicate; -import com.baeldung.jpa.simple.entity.User; +import com.baeldung.jpa.simple.model.User; public interface UserRepositoryCustom { diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepositoryCustomImpl.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepositoryCustomImpl.java index b60ca65d0a..b702735b97 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepositoryCustomImpl.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/repository/UserRepositoryCustomImpl.java @@ -7,7 +7,7 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import com.baeldung.jpa.simple.entity.User; +import com.baeldung.jpa.simple.model.User; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/service/FooService.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/service/FooService.java index f02c1eebad..93d405c689 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/service/FooService.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/service/FooService.java @@ -3,7 +3,7 @@ package com.baeldung.jpa.simple.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.baeldung.jpa.simple.entity.Foo; +import com.baeldung.jpa.simple.model.Foo; import com.baeldung.jpa.simple.repository.IFooDAO; @Service diff --git a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/service/IFooService.java b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/service/IFooService.java index 4fc56bde87..f2950b81fb 100644 --- a/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/service/IFooService.java +++ b/persistence-modules/spring-data-jpa-simple/src/main/java/com/baeldung/jpa/simple/service/IFooService.java @@ -1,6 +1,6 @@ package com.baeldung.jpa.simple.service; -import com.baeldung.jpa.simple.entity.Foo; +import com.baeldung.jpa.simple.model.Foo; public interface IFooService { Foo create(Foo foo); diff --git a/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/BookListRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/BookListRepositoryIntegrationTest.java index ec2ada1e52..f393e305bc 100644 --- a/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/BookListRepositoryIntegrationTest.java +++ b/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/BookListRepositoryIntegrationTest.java @@ -1,14 +1,15 @@ package com.baeldung.jpa.simple; +import static org.junit.jupiter.api.Assertions.assertEquals; + import java.util.Arrays; import java.util.List; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import com.baeldung.jpa.simple.entity.Book; +import com.baeldung.jpa.simple.model.Book; import com.baeldung.jpa.simple.repository.BookListRepository; @SpringBootTest(classes = JpaApplication.class) @@ -25,6 +26,6 @@ class BookListRepositoryIntegrationTest { bookListRepository.saveAll(Arrays.asList(book1, book2, book3)); List books = bookListRepository.findBooksByAuthor("John Doe"); - Assertions.assertEquals(3, books.size()); + assertEquals(3, books.size()); } } \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/BookPagingAndSortingRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/BookPagingAndSortingRepositoryIntegrationTest.java index d5c8c1bca0..390308b5ba 100644 --- a/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/BookPagingAndSortingRepositoryIntegrationTest.java +++ b/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/BookPagingAndSortingRepositoryIntegrationTest.java @@ -1,9 +1,11 @@ package com.baeldung.jpa.simple; +import static org.junit.jupiter.api.Assertions.assertEquals; + import java.util.Arrays; import java.util.List; -import org.junit.jupiter.api.Assertions; + import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -11,7 +13,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import com.baeldung.jpa.simple.entity.Book; +import com.baeldung.jpa.simple.model.Book; import com.baeldung.jpa.simple.repository.BookPagingAndSortingRepository; @SpringBootTest @@ -29,8 +31,8 @@ class BookPagingAndSortingRepositoryIntegrationTest { Pageable pageable = PageRequest.of(0, 2, Sort.by("title").descending()); List books = bookPagingAndSortingRepository.findBooksByAuthor("John Miller", pageable); - Assertions.assertEquals(2, books.size()); - Assertions.assertEquals(book3.getId(), books.get(0).getId()); - Assertions.assertEquals(book2.getId(), books.get(1).getId()); + assertEquals(2, books.size()); + assertEquals(book3.getId(), books.get(0).getId()); + assertEquals(book2.getId(), books.get(1).getId()); } } \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/FooServiceIntegrationTest.java b/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/FooServiceIntegrationTest.java index 7e2dd98ada..d4a4c2a2f7 100644 --- a/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/FooServiceIntegrationTest.java +++ b/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/FooServiceIntegrationTest.java @@ -10,7 +10,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; -import com.baeldung.jpa.simple.entity.Foo; +import com.baeldung.jpa.simple.model.Foo; import com.baeldung.jpa.simple.service.IFooService; @RunWith(SpringRunner.class) diff --git a/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/UserRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/UserRepositoryIntegrationTest.java index 37afc90b59..c690d601e2 100644 --- a/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/UserRepositoryIntegrationTest.java +++ b/persistence-modules/spring-data-jpa-simple/src/test/java/com/baeldung/jpa/simple/UserRepositoryIntegrationTest.java @@ -14,7 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; -import com.baeldung.jpa.simple.entity.User; +import com.baeldung.jpa.simple.model.User; import com.baeldung.jpa.simple.repository.UserRepository; @ExtendWith(SpringExtension.class) diff --git a/persistence-modules/spring-data-redis/pom.xml b/persistence-modules/spring-data-redis/pom.xml index 9e3903a587..ff4732926c 100644 --- a/persistence-modules/spring-data-redis/pom.xml +++ b/persistence-modules/spring-data-redis/pom.xml @@ -90,7 +90,9 @@ 3.2.4 0.10.0 0.6 - 4.3.2 + 5.1.2 + 3.2.0 + 5.10.2 com.baeldung.spring.data.redis.SpringRedisApplication diff --git a/persistence-modules/spring-jpa-3/.gitignore b/persistence-modules/spring-jpa-3/.gitignore new file mode 100644 index 0000000000..83c05e60c8 --- /dev/null +++ b/persistence-modules/spring-jpa-3/.gitignore @@ -0,0 +1,13 @@ +*.class + +#folders# +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear \ No newline at end of file diff --git a/persistence-modules/spring-jpa-3/README.md b/persistence-modules/spring-jpa-3/README.md new file mode 100644 index 0000000000..9db4527148 --- /dev/null +++ b/persistence-modules/spring-jpa-3/README.md @@ -0,0 +1,4 @@ +## Spring JPA (3) + +### Relevant Articles: +- More articles: [[<-- prev]](/spring-jpa-2) diff --git a/persistence-modules/spring-jpa-3/pom.xml b/persistence-modules/spring-jpa-3/pom.xml new file mode 100644 index 0000000000..a249de33ea --- /dev/null +++ b/persistence-modules/spring-jpa-3/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + spring-jpa-3 + 0.1-SNAPSHOT + spring-jpa-3 + war + + + com.baeldung + persistence-modules + 1.0.0-SNAPSHOT + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring-boot.version} + + + com.h2database + h2 + ${h2.version} + + + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + + + + + + 3.2.4 + + + diff --git a/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/InvoiceRepository.java b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/InvoiceRepository.java new file mode 100644 index 0000000000..b19ea156ec --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/InvoiceRepository.java @@ -0,0 +1,128 @@ +package com.baeldung.continuetransactionafterexception; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import com.baeldung.continuetransactionafterexception.model.InvoiceEntity; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; + +@Repository +public class InvoiceRepository { + private final Logger logger = LoggerFactory.getLogger(InvoiceRepository.class); + @PersistenceContext + private EntityManager entityManager; + + @Autowired + private EntityManagerFactory entityManagerFactory; + + @Transactional + public void saveBatch(List invoiceEntities) { + invoiceEntities.forEach(i -> entityManager.persist(i)); + try { + entityManager.flush(); + } catch (Exception e) { + logger.error("Duplicates detected, save individually", e); + + invoiceEntities.forEach(i -> { + try { + save(i); + } catch (Exception ex) { + logger.error("Problem saving individual entity {}", i.getSerialNumber(), ex); + } + }); + } + } + + @Transactional + public void save(InvoiceEntity invoiceEntity) { + if (invoiceEntity.getId() == null) { + entityManager.persist(invoiceEntity); + } else { + entityManager.merge(invoiceEntity); + } + + entityManager.flush(); + logger.info("Entity is saved: {}", invoiceEntity.getSerialNumber()); + } + + public void saveBatchUsingManualTransaction(List testEntities) { + EntityTransaction transaction = null; + try (EntityManager em = em()) { + transaction = em.getTransaction(); + transaction.begin(); + testEntities.forEach(em::persist); + try { + em.flush(); + } catch (Exception e) { + logger.error("Duplicates detected, save individually", e); + transaction.rollback(); + testEntities.forEach(t -> { + EntityTransaction newTransaction = em.getTransaction(); + try { + newTransaction.begin(); + saveUsingManualTransaction(t, em); + } catch (Exception ex) { + logger.error("Problem saving individual entity <{}>", t.getSerialNumber(), ex); + newTransaction.rollback(); + } finally { + commitTransactionIfNeeded(newTransaction); + } + }); + + } + } finally { + commitTransactionIfNeeded(transaction); + } + } + + private void commitTransactionIfNeeded(EntityTransaction newTransaction) { + if (newTransaction != null && newTransaction.isActive()) { + if (!newTransaction.getRollbackOnly()) { + newTransaction.commit(); + } + } + } + + private void saveUsingManualTransaction(InvoiceEntity invoiceEntity, EntityManager em) { + if (invoiceEntity.getId() == null) { + em.persist(invoiceEntity); + } else { + em.merge(invoiceEntity); + } + + em.flush(); + logger.info("Entity is saved: {}", invoiceEntity.getSerialNumber()); + } + + private EntityManager em() { + return entityManagerFactory.createEntityManager(); + } + + @Transactional + public void saveBatchOnly(List testEntities) { + testEntities.forEach(entityManager::persist); + entityManager.flush(); + } + + public List findAll() { + TypedQuery query = entityManager.createQuery("SELECT i From InvoiceEntity i", InvoiceEntity.class); + return query.getResultList(); + } + + @Transactional + public void deleteAll() { + Query query = entityManager.createQuery("DELETE FROM InvoiceEntity"); + query.executeUpdate(); + } +} diff --git a/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/InvoiceService.java b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/InvoiceService.java new file mode 100644 index 0000000000..e1db310d03 --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/InvoiceService.java @@ -0,0 +1,27 @@ +package com.baeldung.continuetransactionafterexception; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.baeldung.continuetransactionafterexception.model.InvoiceEntity; + +@Service +public class InvoiceService { + @Autowired + private InvoiceRepository repository; + @Transactional + public void saveInvoice(InvoiceEntity invoice) { + repository.save(invoice); + sendNotification(); + } + @Transactional(noRollbackFor = NotificationSendingException.class) + public void saveInvoiceWithoutRollback(InvoiceEntity entity) { + repository.save(entity); + sendNotification(); + } + + private void sendNotification() { + throw new NotificationSendingException("Notification sending is failed"); + } +} diff --git a/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/NotificationSendingException.java b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/NotificationSendingException.java new file mode 100644 index 0000000000..8c2fd3cd5b --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/NotificationSendingException.java @@ -0,0 +1,8 @@ +package com.baeldung.continuetransactionafterexception; + +public class NotificationSendingException extends RuntimeException { + + public NotificationSendingException(String text) { + super(text); + } +} diff --git a/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/model/InvoiceEntity.java b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/model/InvoiceEntity.java new file mode 100644 index 0000000000..bcae12b2ab --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/main/java/com/baeldung/continuetransactionafterexception/model/InvoiceEntity.java @@ -0,0 +1,62 @@ +package com.baeldung.continuetransactionafterexception.model; + +import java.util.Objects; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; + +@Entity +@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "serialNumber")}) +public class InvoiceEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Integer id; + private String serialNumber; + private String description; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InvoiceEntity that = (InvoiceEntity) o; + return Objects.equals(serialNumber, that.serialNumber); + } + + @Override + public int hashCode() { + return Objects.hash(serialNumber); + } +} diff --git a/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/com/baeldung/continuetransactionafterexception/ContinueWithTransactionAfterExceptionIntegrationConfiguration.java b/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/com/baeldung/continuetransactionafterexception/ContinueWithTransactionAfterExceptionIntegrationConfiguration.java new file mode 100644 index 0000000000..3a5bd1cde2 --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/com/baeldung/continuetransactionafterexception/ContinueWithTransactionAfterExceptionIntegrationConfiguration.java @@ -0,0 +1,61 @@ +package com.baeldung.com.baeldung.continuetransactionafterexception; + +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +import jakarta.persistence.EntityManagerFactory; + +@Configuration +@PropertySource("continuetransactionafterexception/test.properties") +public class ContinueWithTransactionAfterExceptionIntegrationConfiguration { + + @Bean + public DataSource dataSource() { + EmbeddedDatabaseBuilder dbBuilder = new EmbeddedDatabaseBuilder(); + return dbBuilder.setType(EmbeddedDatabaseType.H2) + .build(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Value("${hibernate.hbm2ddl.auto}") String hbm2ddlType, @Value("${hibernate.dialect}") String dialect, @Value("${hibernate.show_sql}") boolean showSql) { + LocalContainerEntityManagerFactoryBean result = new LocalContainerEntityManagerFactoryBean(); + + result.setDataSource(dataSource()); + result.setPackagesToScan("com.baeldung.continuetransactionafterexception.model"); + result.setJpaVendorAdapter(jpaVendorAdapter()); + + Map jpaProperties = new HashMap<>(); + jpaProperties.put("hibernate.hbm2ddl.auto", hbm2ddlType); + jpaProperties.put("hibernate.dialect", dialect); + jpaProperties.put("hibernate.show_sql", showSql); + result.setJpaPropertyMap(jpaProperties); + + return result; + } + + @Bean + public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory); + transactionManager.setNestedTransactionAllowed(true); + + return transactionManager; + } + + public JpaVendorAdapter jpaVendorAdapter() { + return new HibernateJpaVendorAdapter(); + } +} diff --git a/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/com/baeldung/continuetransactionafterexception/ContinueWithTransactionAfterExceptionIntegrationTest.java b/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/com/baeldung/continuetransactionafterexception/ContinueWithTransactionAfterExceptionIntegrationTest.java new file mode 100644 index 0000000000..7f5f60f556 --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/test/java/com/baeldung/com/baeldung/continuetransactionafterexception/ContinueWithTransactionAfterExceptionIntegrationTest.java @@ -0,0 +1,165 @@ +package com.baeldung.com.baeldung.continuetransactionafterexception; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.UnexpectedRollbackException; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import com.baeldung.continuetransactionafterexception.InvoiceRepository; +import com.baeldung.continuetransactionafterexception.InvoiceService; +import com.baeldung.continuetransactionafterexception.NotificationSendingException; +import com.baeldung.continuetransactionafterexception.model.InvoiceEntity; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + ContinueWithTransactionAfterExceptionIntegrationConfiguration.class, + InvoiceRepository.class, InvoiceService.class}) +@DirtiesContext +@EnableTransactionManagement +class ContinueWithTransactionAfterExceptionIntegrationTest { + + @Autowired + private InvoiceRepository repository; + @Autowired + private InvoiceService service; + + @Test + void givenInvoiceService_whenExceptionOccursDuringNotificationSending_thenNoDataShouldBeSaved() { + InvoiceEntity invoiceEntity = new InvoiceEntity(); + invoiceEntity.setSerialNumber("#1"); + invoiceEntity.setDescription("First invoice"); + + assertThrows( + NotificationSendingException.class, + () -> service.saveInvoice(invoiceEntity) + ); + + List entityList = repository.findAll(); + Assertions.assertTrue(entityList.isEmpty()); + } + + @Test + void givenInvoiceRepository_whenExceptionOccursDuringBatchSavingInternally_thenNoDataShouldBeSaved() { + + List testEntities = new ArrayList<>(); + + InvoiceEntity invoiceEntity = new InvoiceEntity(); + invoiceEntity.setSerialNumber("#1"); + invoiceEntity.setDescription("First invoice"); + testEntities.add(invoiceEntity); + + InvoiceEntity invoiceEntity2 = new InvoiceEntity(); + invoiceEntity2.setSerialNumber("#1"); + invoiceEntity.setDescription("First invoice (duplicated)"); + testEntities.add(invoiceEntity2); + + InvoiceEntity invoiceEntity3 = new InvoiceEntity(); + invoiceEntity3.setSerialNumber("#2"); + invoiceEntity.setDescription("Second invoice"); + testEntities.add(invoiceEntity3); + + UnexpectedRollbackException exception = assertThrows(UnexpectedRollbackException.class, + () -> repository.saveBatch(testEntities)); + assertEquals("Transaction silently rolled back because it has been marked as rollback-only", + exception.getMessage()); + + List entityList = repository.findAll(); + Assertions.assertTrue(entityList.isEmpty()); + } + + @Test + void givenInvoiceService_whenNotificationSendingExceptionOccurs_thenTheInvoiceBeSaved() { + InvoiceEntity invoiceEntity = new InvoiceEntity(); + invoiceEntity.setSerialNumber("#1"); + invoiceEntity.setDescription("We want to save this invoice anyway"); + + assertThrows( + NotificationSendingException.class, + () -> service.saveInvoiceWithoutRollback(invoiceEntity) + ); + + List entityList = repository.findAll(); + Assertions.assertTrue(entityList.contains(invoiceEntity)); + } + + @Test + void givenInvoiceRepository_whenExceptionOccursDuringBatchSavingInternally_thenDataShouldBeSavedInSeparateTransaction() { + + List testEntities = new ArrayList<>(); + + InvoiceEntity invoiceEntity1 = new InvoiceEntity(); + invoiceEntity1.setSerialNumber("#1"); + invoiceEntity1.setDescription("First invoice"); + testEntities.add(invoiceEntity1); + + InvoiceEntity invoiceEntity2 = new InvoiceEntity(); + invoiceEntity2.setSerialNumber("#1"); + invoiceEntity1.setDescription("First invoice (duplicated)"); + testEntities.add(invoiceEntity2); + + InvoiceEntity invoiceEntity3 = new InvoiceEntity(); + invoiceEntity3.setSerialNumber("#2"); + invoiceEntity1.setDescription("Second invoice"); + testEntities.add(invoiceEntity3); + + repository.saveBatchUsingManualTransaction(testEntities); + + List entityList = repository.findAll(); + Assertions.assertTrue(entityList.contains(invoiceEntity1)); + Assertions.assertTrue(entityList.contains(invoiceEntity3)); + } + + @Test + void givenInvoiceRepository_whenExceptionOccursDuringBatchSaving_thenDataShouldBeSavedUsingSaveMethod() { + + List testEntities = new ArrayList<>(); + + InvoiceEntity invoiceEntity1 = new InvoiceEntity(); + invoiceEntity1.setSerialNumber("#1"); + invoiceEntity1.setDescription("First invoice"); + testEntities.add(invoiceEntity1); + + InvoiceEntity invoiceEntity2 = new InvoiceEntity(); + invoiceEntity2.setSerialNumber("#1"); + invoiceEntity1.setDescription("First invoice (duplicated)"); + testEntities.add(invoiceEntity2); + + InvoiceEntity invoiceEntity3 = new InvoiceEntity(); + invoiceEntity3.setSerialNumber("#2"); + invoiceEntity1.setDescription("Second invoice"); + testEntities.add(invoiceEntity3); + + try { + repository.saveBatchOnly(testEntities); + } catch (Exception e) { + testEntities.forEach(t -> { + try { + repository.save(t); + } catch (Exception e2) { + System.err.println(e2.getMessage()); + } + }); + } + + List entityList = repository.findAll(); + Assertions.assertTrue(entityList.contains(invoiceEntity1)); + Assertions.assertTrue(entityList.contains(invoiceEntity3)); + } + + @AfterEach + void clean() { + repository.deleteAll(); + } +} diff --git a/persistence-modules/spring-jpa-3/src/test/resources/continuetransactionafterexception/test.properties b/persistence-modules/spring-jpa-3/src/test/resources/continuetransactionafterexception/test.properties new file mode 100644 index 0000000000..191dfc85f5 --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/test/resources/continuetransactionafterexception/test.properties @@ -0,0 +1,7 @@ +jdbc.driverClassName=org.h2.Driver +jdbc.driverClassName=org.h2.Driver +jdbc.url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1 + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.show_sql=false +hibernate.hbm2ddl.auto=update diff --git a/persistence-modules/spring-jpa-3/src/test/resources/logback.xml b/persistence-modules/spring-jpa-3/src/test/resources/logback.xml new file mode 100644 index 0000000000..4ecb8017f3 --- /dev/null +++ b/persistence-modules/spring-jpa-3/src/test/resources/logback.xml @@ -0,0 +1,12 @@ + + + + + [%d{ISO8601}]-[%thread] %-5level %logger - %msg%n + + + + + + + diff --git a/pom.xml b/pom.xml index 57bc8bbfb3..7496d4ca93 100644 --- a/pom.xml +++ b/pom.xml @@ -1187,7 +1187,7 @@ 3.3.0 3.21.0 1.18.30 - 2.1.214 + 2.2.224 33.0.0-jre 3.3.0 diff --git a/spring-5-rest-docs/.gitignore b/spring-5-rest-docs/.gitignore new file mode 100644 index 0000000000..dec013dfa4 --- /dev/null +++ b/spring-5-rest-docs/.gitignore @@ -0,0 +1,12 @@ +#folders# +.idea +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear \ No newline at end of file diff --git a/spring-5-rest-docs/README.md b/spring-5-rest-docs/README.md new file mode 100644 index 0000000000..02c018a07c --- /dev/null +++ b/spring-5-rest-docs/README.md @@ -0,0 +1,8 @@ +## Spring 5 REST Docs + +This module contains articles about Spring 5 + +### Relevant Articles + +- [Introduction to Spring REST Docs](https://www.baeldung.com/spring-rest-docs) +- [Document Query Parameters with Spring REST Docs](https://www.baeldung.com/spring-rest-document-query-parameters) diff --git a/spring-5-rest-docs/pom.xml b/spring-5-rest-docs/pom.xml new file mode 100644 index 0000000000..c1f5dc0681 --- /dev/null +++ b/spring-5-rest-docs/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + spring-5 + 0.0.1-SNAPSHOT + spring-5 + jar + spring 5 sample project about new features + + + com.baeldung + parent-boot-3 + 0.0.1-SNAPSHOT + ../parent-boot-3 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-hateoas + + + com.h2database + h2 + runtime + + + org.springframework + spring-test + + + org.springframework.security + spring-security-test + test + + + org.junit.jupiter + junit-jupiter-api + + + + org.springframework.restdocs + spring-restdocs-mockmvc + test + + + org.springframework.restdocs + spring-restdocs-restassured + test + + + org.springframework.restdocs + spring-restdocs-webtestclient + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + com.baeldung.Spring5Application + JAR + + + + org.asciidoctor + asciidoctor-maven-plugin + ${asciidoctor-plugin.version} + + + generate-docs + package + + process-asciidoc + + + html + book + + ${snippetsDirectory} + + src/docs/asciidocs + target/generated-docs + + + + + + + + + 2.2.6 + ${project.build.directory}/generated-snippets + true + + + \ No newline at end of file diff --git a/spring-5/src/docs/asciidocs/api-guide.adoc b/spring-5-rest-docs/src/docs/asciidocs/api-guide.adoc similarity index 100% rename from spring-5/src/docs/asciidocs/api-guide.adoc rename to spring-5-rest-docs/src/docs/asciidocs/api-guide.adoc diff --git a/spring-5/src/main/java/com/baeldung/queryparamdoc/Application.java b/spring-5-rest-docs/src/main/java/com/baeldung/queryparamdoc/Application.java similarity index 100% rename from spring-5/src/main/java/com/baeldung/queryparamdoc/Application.java rename to spring-5-rest-docs/src/main/java/com/baeldung/queryparamdoc/Application.java diff --git a/spring-5/src/main/java/com/baeldung/queryparamdoc/Book.java b/spring-5-rest-docs/src/main/java/com/baeldung/queryparamdoc/Book.java similarity index 100% rename from spring-5/src/main/java/com/baeldung/queryparamdoc/Book.java rename to spring-5-rest-docs/src/main/java/com/baeldung/queryparamdoc/Book.java diff --git a/spring-5/src/main/java/com/baeldung/queryparamdoc/BookController.java b/spring-5-rest-docs/src/main/java/com/baeldung/queryparamdoc/BookController.java similarity index 100% rename from spring-5/src/main/java/com/baeldung/queryparamdoc/BookController.java rename to spring-5-rest-docs/src/main/java/com/baeldung/queryparamdoc/BookController.java diff --git a/spring-5/src/main/java/com/baeldung/queryparamdoc/BookService.java b/spring-5-rest-docs/src/main/java/com/baeldung/queryparamdoc/BookService.java similarity index 100% rename from spring-5/src/main/java/com/baeldung/queryparamdoc/BookService.java rename to spring-5-rest-docs/src/main/java/com/baeldung/queryparamdoc/BookService.java diff --git a/spring-5/src/main/java/com/baeldung/restdocs/CRUDController.java b/spring-5-rest-docs/src/main/java/com/baeldung/restdocs/CRUDController.java similarity index 100% rename from spring-5/src/main/java/com/baeldung/restdocs/CRUDController.java rename to spring-5-rest-docs/src/main/java/com/baeldung/restdocs/CRUDController.java diff --git a/spring-5/src/main/java/com/baeldung/restdocs/CrudInput.java b/spring-5-rest-docs/src/main/java/com/baeldung/restdocs/CrudInput.java similarity index 100% rename from spring-5/src/main/java/com/baeldung/restdocs/CrudInput.java rename to spring-5-rest-docs/src/main/java/com/baeldung/restdocs/CrudInput.java diff --git a/spring-5/src/main/java/com/baeldung/restdocs/IndexController.java b/spring-5-rest-docs/src/main/java/com/baeldung/restdocs/IndexController.java similarity index 100% rename from spring-5/src/main/java/com/baeldung/restdocs/IndexController.java rename to spring-5-rest-docs/src/main/java/com/baeldung/restdocs/IndexController.java diff --git a/spring-5/src/main/java/com/baeldung/restdocs/SpringRestDocsApplication.java b/spring-5-rest-docs/src/main/java/com/baeldung/restdocs/SpringRestDocsApplication.java similarity index 100% rename from spring-5/src/main/java/com/baeldung/restdocs/SpringRestDocsApplication.java rename to spring-5-rest-docs/src/main/java/com/baeldung/restdocs/SpringRestDocsApplication.java diff --git a/spring-5/src/test/java/com/baeldung/queryparamdoc/BookControllerMvcIntegrationTest.java b/spring-5-rest-docs/src/test/java/com/baeldung/queryparamdoc/BookControllerMvcIntegrationTest.java similarity index 100% rename from spring-5/src/test/java/com/baeldung/queryparamdoc/BookControllerMvcIntegrationTest.java rename to spring-5-rest-docs/src/test/java/com/baeldung/queryparamdoc/BookControllerMvcIntegrationTest.java diff --git a/spring-5/src/test/java/com/baeldung/queryparamdoc/BookControllerReactiveIntegrationTest.java b/spring-5-rest-docs/src/test/java/com/baeldung/queryparamdoc/BookControllerReactiveIntegrationTest.java similarity index 100% rename from spring-5/src/test/java/com/baeldung/queryparamdoc/BookControllerReactiveIntegrationTest.java rename to spring-5-rest-docs/src/test/java/com/baeldung/queryparamdoc/BookControllerReactiveIntegrationTest.java diff --git a/spring-5/src/test/java/com/baeldung/queryparamdoc/BookControllerRestAssuredIntegrationTest.java b/spring-5-rest-docs/src/test/java/com/baeldung/queryparamdoc/BookControllerRestAssuredIntegrationTest.java similarity index 100% rename from spring-5/src/test/java/com/baeldung/queryparamdoc/BookControllerRestAssuredIntegrationTest.java rename to spring-5-rest-docs/src/test/java/com/baeldung/queryparamdoc/BookControllerRestAssuredIntegrationTest.java diff --git a/spring-5/src/test/java/com/baeldung/restdocs/ApiDocumentationJUnit4IntegrationTest.java b/spring-5-rest-docs/src/test/java/com/baeldung/restdocs/ApiDocumentationJUnit4IntegrationTest.java similarity index 100% rename from spring-5/src/test/java/com/baeldung/restdocs/ApiDocumentationJUnit4IntegrationTest.java rename to spring-5-rest-docs/src/test/java/com/baeldung/restdocs/ApiDocumentationJUnit4IntegrationTest.java diff --git a/spring-5/src/test/java/com/baeldung/restdocs/ApiDocumentationJUnit5IntegrationTest.java b/spring-5-rest-docs/src/test/java/com/baeldung/restdocs/ApiDocumentationJUnit5IntegrationTest.java similarity index 100% rename from spring-5/src/test/java/com/baeldung/restdocs/ApiDocumentationJUnit5IntegrationTest.java rename to spring-5-rest-docs/src/test/java/com/baeldung/restdocs/ApiDocumentationJUnit5IntegrationTest.java diff --git a/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/ex/NotFoundException.java b/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/ex/NotFoundException.java new file mode 100644 index 0000000000..5401a4d133 --- /dev/null +++ b/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/ex/NotFoundException.java @@ -0,0 +1,12 @@ +package com.baeldung.webflux.exceptionhandeling.ex; + +public class NotFoundException extends RuntimeException { + + public NotFoundException(String message) { + super(message); + } + + public NotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/model/User.java b/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/model/User.java new file mode 100644 index 0000000000..6bc39da062 --- /dev/null +++ b/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/model/User.java @@ -0,0 +1,29 @@ +package com.baeldung.webflux.exceptionhandeling.model; + +public class User { + private String id; + private String username; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public User(String userId, String userName) { + this.id = userId; + this.username = userName; + + } +} + diff --git a/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/repository/UserRepository.java b/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/repository/UserRepository.java new file mode 100644 index 0000000000..8c153aa2e6 --- /dev/null +++ b/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/repository/UserRepository.java @@ -0,0 +1,22 @@ +package com.baeldung.webflux.exceptionhandeling.repository; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.stereotype.Repository; + +import com.baeldung.webflux.exceptionhandeling.model.User; + +@Repository +public class UserRepository { + private final Map userDatabase = new ConcurrentHashMap<>(); + + public UserRepository() { + userDatabase.put("1", new User("1", "John Doe")); + userDatabase.put("2", new User("2", "Jane Smith")); + } + + public User findById(String id) { + return userDatabase.get(id); + } +} diff --git a/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/service/UserService.java b/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/service/UserService.java new file mode 100644 index 0000000000..da5a672183 --- /dev/null +++ b/spring-5-webflux-2/src/main/java/com/baeldung/webflux/exceptionhandeling/service/UserService.java @@ -0,0 +1,34 @@ +package com.baeldung.webflux.exceptionhandeling.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import com.baeldung.webflux.exceptionhandeling.ex.NotFoundException; +import com.baeldung.webflux.exceptionhandeling.model.User; +import com.baeldung.webflux.exceptionhandeling.repository.UserRepository; + +import reactor.core.publisher.Mono; + +public class UserService { + + private final UserRepository userRepository; + + @Autowired + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public Mono getUserByIdThrowingException(String id) { + User user = userRepository.findById(id); + if (user == null) + throw new NotFoundException("User Not Found"); + return Mono.justOrEmpty(user); + } + + public Mono getUserByIdUsingMonoError(String id) { + User user = userRepository.findById(id); + return (user != null) ? Mono.justOrEmpty(user) : Mono.error(new NotFoundException("User Not Found")); + + } +} diff --git a/spring-5-webflux-2/src/test/java/com/baeldung/webflux/exceptionhandeling/UserControllerUnitTest.java b/spring-5-webflux-2/src/test/java/com/baeldung/webflux/exceptionhandeling/UserControllerUnitTest.java new file mode 100644 index 0000000000..e968cfeea3 --- /dev/null +++ b/spring-5-webflux-2/src/test/java/com/baeldung/webflux/exceptionhandeling/UserControllerUnitTest.java @@ -0,0 +1,36 @@ +package com.baeldung.webflux.exceptionhandeling; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import com.baeldung.webflux.exceptionhandeling.ex.NotFoundException; +import com.baeldung.webflux.exceptionhandeling.model.User; +import com.baeldung.webflux.exceptionhandeling.service.UserService; +import com.baeldung.webflux.exceptionhandeling.repository.UserRepository; + +public class UserControllerUnitTest { + UserRepository repositoryMock = mock(UserRepository.class); + private final UserService userService = new UserService(repositoryMock); + + @Test + public void givenNonExistUser_whenFailureCall_then_Throws_exception() { + assertThrows(NotFoundException.class, () -> userService.getUserByIdThrowingException("3")); + + } + + @Test + public void givenNonExistUser_whenFailureCall_then_returnMonoError() { + Mono result = userService.getUserByIdUsingMonoError("3"); + StepVerifier.create(result) + .expectError(NotFoundException.class) + .verify(); + + } + +} diff --git a/spring-5/README.md b/spring-5/README.md index d1487913ac..9af8bdfaf2 100644 --- a/spring-5/README.md +++ b/spring-5/README.md @@ -6,9 +6,7 @@ This module contains articles about Spring 5 - [Spring 5 Functional Bean Registration](https://www.baeldung.com/spring-5-functional-beans) - [The SpringJUnitConfig and SpringJUnitWebConfig Annotations in Spring 5](https://www.baeldung.com/spring-5-junit-config) -- [Introduction to Spring REST Docs](https://www.baeldung.com/spring-rest-docs) - [Spring ResponseStatusException](https://www.baeldung.com/spring-response-status-exception) - [Spring Assert Statements](https://www.baeldung.com/spring-assert) - [Difference between context:annotation-config vs context:component-scan](https://www.baeldung.com/spring-contextannotation-contextcomponentscan) - [Configuring a Hikari Connection Pool with Spring Boot](https://www.baeldung.com/spring-boot-hikari) -- [Document Query Parameters with Spring REST Docs](https://www.baeldung.com/spring-rest-document-query-parameters) diff --git a/spring-5/pom.xml b/spring-5/pom.xml index d66f0fa01f..46c13cd28c 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -131,36 +131,11 @@ JAR - - org.asciidoctor - asciidoctor-maven-plugin - ${asciidoctor-plugin.version} - - - generate-docs - package - - process-asciidoc - - - html - book - - ${snippetsDirectory} - - src/docs/asciidocs - target/generated-docs - - - - 1.0 - 2.2.6 - ${project.build.directory}/generated-snippets 5.1.0 true 2.0.1 diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index 9b8a3b318a..bb1af47126 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -91,6 +91,7 @@ spring-boot-redis spring-boot-cassandre spring-boot-react + spring-caching-3 spring-boot-3-grpc spring-boot-3-native @@ -113,6 +114,7 @@ spring-boot-graalvm-docker spring-boot-validations spring-boot-openapi + spring-boot-brave diff --git a/spring-boot-modules/spring-boot-brave/pom.xml b/spring-boot-modules/spring-boot-brave/pom.xml new file mode 100644 index 0000000000..1cc8f44cf7 --- /dev/null +++ b/spring-boot-modules/spring-boot-brave/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + com.baeldung + spring-boot-brave + 1.0.0-SNAPSHOT + spring-boot-brave + jar + + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + io.zipkin.brave + brave + 6.0.2 + + + io.zipkin.reporter2 + zipkin-reporter + 3.3.0 + + + io.zipkin.reporter2 + zipkin-sender-okhttp3 + 3.3.0 + + + io.zipkin.reporter2 + zipkin-reporter-brave + 3.3.0 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + \ No newline at end of file diff --git a/spring-core-2/src/main/java/com/baeldung/concurrentrequest/ConcurrentRequestApplication.java b/spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/Application.java similarity index 55% rename from spring-core-2/src/main/java/com/baeldung/concurrentrequest/ConcurrentRequestApplication.java rename to spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/Application.java index 21d031555b..7c7bc05a07 100644 --- a/spring-core-2/src/main/java/com/baeldung/concurrentrequest/ConcurrentRequestApplication.java +++ b/spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/Application.java @@ -1,11 +1,12 @@ -package com.baeldung.concurrentrequest; +package com.baeldung.brave; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class ConcurrentRequestApplication { +public class Application { + public static void main(String[] args) { - SpringApplication.run(ConcurrentRequestApplication.class, args); + SpringApplication.run(Application.class, args); } } diff --git a/spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/configuration/TracingConfiguration.java b/spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/configuration/TracingConfiguration.java new file mode 100644 index 0000000000..5b36fecce2 --- /dev/null +++ b/spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/configuration/TracingConfiguration.java @@ -0,0 +1,36 @@ +package com.baeldung.brave.configuration; + +import brave.Tracer; +import brave.Tracing; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import zipkin2.reporter.BytesMessageSender; +import zipkin2.reporter.brave.AsyncZipkinSpanHandler; +import zipkin2.reporter.okhttp3.OkHttpSender; + +@Configuration +public class TracingConfiguration { + + @Bean + BytesMessageSender sender() { + return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans"); + } + + @Bean + AsyncZipkinSpanHandler zipkinSpanHandler(BytesMessageSender sender) { + return AsyncZipkinSpanHandler.create(sender); + } + + @Bean + public Tracing tracing(AsyncZipkinSpanHandler zipkinSpanHandler) { + return Tracing.newBuilder() + .localServiceName("Dummy Service") + .addSpanHandler(zipkinSpanHandler) + .build(); + } + + @Bean + public Tracer tracer(Tracing tracing) { + return tracing.tracer(); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/service/TracingService.java b/spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/service/TracingService.java new file mode 100644 index 0000000000..139718c54d --- /dev/null +++ b/spring-boot-modules/spring-boot-brave/src/main/java/com/baeldung/brave/service/TracingService.java @@ -0,0 +1,28 @@ +package com.baeldung.brave.service; + +import brave.Span; +import brave.Tracer; +import jakarta.annotation.PostConstruct; +import org.springframework.stereotype.Service; + +@Service +public class TracingService { + + private final Tracer tracer; + + public TracingService(Tracer tracer) { + this.tracer = tracer; + } + + @PostConstruct + private void postConstruct() { + Span span = tracer.nextSpan().name("Hello from Service").start(); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + span.finish(); + } + } +} diff --git a/spring-boot-modules/spring-boot-libraries-3/pom.xml b/spring-boot-modules/spring-boot-libraries-3/pom.xml index 988ce0bafe..e88ae4c078 100644 --- a/spring-boot-modules/spring-boot-libraries-3/pom.xml +++ b/spring-boot-modules/spring-boot-libraries-3/pom.xml @@ -17,7 +17,6 @@ org.springframework.boot spring-boot-starter-data-jpa - org.springframework.kafka spring-kafka @@ -28,20 +27,27 @@ postgresql ${postgresql.version} + org.springframework.modulith spring-modulith-events-api - ${spring-modulith-events-kafka.version} + ${spring-modulith.version} org.springframework.modulith spring-modulith-events-kafka - ${spring-modulith-events-kafka.version} + ${spring-modulith.version} org.springframework.modulith spring-modulith-starter-jpa - ${spring-modulith-events-kafka.version} + ${spring-modulith.version} + + + org.springframework.modulith + spring-modulith-starter-test + ${spring-modulith.version} + test @@ -54,7 +60,6 @@ spring-boot-testcontainers test - org.testcontainers kafka @@ -73,7 +78,6 @@ ${testcontainers.version} test - org.testcontainers postgresql @@ -87,16 +91,23 @@ ${awaitility.version} test + + com.h2database + h2 + ${h2.version} + test + 17 3.1.5 - 1.1.2 + 1.1.3 1.19.3 4.2.0 42.3.1 + 2.2.224 diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/Order.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/Order.java new file mode 100644 index 0000000000..c448bd44dd --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/Order.java @@ -0,0 +1,12 @@ +package com.baeldung.springmodulith.application.events.orders; + +import java.time.Instant; +import java.util.List; + +record Order(String id, String customerId, List productIds, Instant timestamp) { + + public Order(String customerId, List productIds) { + this(null, customerId, productIds, Instant.now()); + } + +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderCompletedEvent.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderCompletedEvent.java new file mode 100644 index 0000000000..4344b336ac --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderCompletedEvent.java @@ -0,0 +1,6 @@ +package com.baeldung.springmodulith.application.events.orders; + +import java.time.Instant; + +public record OrderCompletedEvent(String orderId, String customerId, Instant timestamp) { +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderRepository.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderRepository.java new file mode 100644 index 0000000000..7c159e3582 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderRepository.java @@ -0,0 +1,27 @@ +package com.baeldung.springmodulith.application.events.orders; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.springframework.stereotype.Component; + +@Component +class OrderRepository { + private final List orders = new ArrayList<>(); + + public Order save(Order order) { + order = new Order(UUID.randomUUID() + .toString(), order.customerId(), order.productIds(), order.timestamp()); + orders.add(order); + return order; + } + + public List ordersByCustomer(String customerId) { + return orders.stream() + .filter(it -> it.customerId() + .equals(customerId)) + .toList(); + } + +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderService.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderService.java new file mode 100644 index 0000000000..c60792813c --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderService.java @@ -0,0 +1,29 @@ +package com.baeldung.springmodulith.application.events.orders; + +import java.util.Arrays; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +@Service +public class OrderService { + + private final OrderRepository repository; + private final ApplicationEventPublisher eventPublisher; + + public OrderService(OrderRepository orders, ApplicationEventPublisher eventsPublisher) { + this.repository = orders; + this.eventPublisher = eventsPublisher; + } + + public void placeOrder(String customerId, String... productIds) { + Order order = new Order(customerId, Arrays.asList(productIds)); + // business logic to validate and place the order + + Order savedOrder = repository.save(order); + + OrderCompletedEvent event = new OrderCompletedEvent(savedOrder.id(), savedOrder.customerId(), savedOrder.timestamp()); + eventPublisher.publishEvent(event); + } + +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyalCustomersRepository.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyalCustomersRepository.java new file mode 100644 index 0000000000..29ba6fa8e2 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyalCustomersRepository.java @@ -0,0 +1,44 @@ +package com.baeldung.springmodulith.application.events.rewards; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Component; + +@Component +public class LoyalCustomersRepository { + + private List customers = new ArrayList<>(); + + public Optional find(String customerId) { + return customers.stream() + .filter(it -> it.customerId() + .equals(customerId)) + .findFirst(); + } + + public void awardPoints(String customerId, int points) { + var customer = find(customerId).orElseGet(() -> save(new LoyalCustomer(customerId, 0))); + + customers.remove(customer); + customers.add(customer.addPoints(points)); + } + + public LoyalCustomer save(LoyalCustomer customer) { + customers.add(customer); + return customer; + } + + public boolean isLoyalCustomer(String customerId) { + return find(customerId).isPresent(); + } + + public record LoyalCustomer(String customerId, int points) { + + LoyalCustomer addPoints(int points) { + return new LoyalCustomer(customerId, this.points() + points); + } + } + +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyaltyPointsService.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyaltyPointsService.java new file mode 100644 index 0000000000..8cd1afe329 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyaltyPointsService.java @@ -0,0 +1,24 @@ +package com.baeldung.springmodulith.application.events.rewards; + +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +import com.baeldung.springmodulith.application.events.orders.OrderCompletedEvent; + +@Service +public class LoyaltyPointsService { + + public static final int ORDER_COMPLETED_POINTS = 60; + private final LoyalCustomersRepository loyalCustomers; + + public LoyaltyPointsService(LoyalCustomersRepository loyalCustomers) { + this.loyalCustomers = loyalCustomers; + } + + @EventListener + public void onOrderCompleted(OrderCompletedEvent event) { + // business logic to award points to loyal customers + loyalCustomers.awardPoints(event.customerId(), ORDER_COMPLETED_POINTS); + } + +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventListenerUnitTest.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventListenerUnitTest.java new file mode 100644 index 0000000000..676bc1173b --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventListenerUnitTest.java @@ -0,0 +1,36 @@ +package com.baeldung.springmodulith.application.events; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Instant; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationEventPublisher; + +import com.baeldung.springmodulith.application.events.orders.OrderCompletedEvent; +import com.baeldung.springmodulith.application.events.rewards.LoyalCustomersRepository; + +@SpringBootTest +class EventListenerUnitTest { + + @Autowired + private LoyalCustomersRepository customers; + + @Autowired + private ApplicationEventPublisher testEventPublisher; + + @Test + void whenPublishingOrderCompletedEvent_thenRewardCustomerWithLoyaltyPoints() { + OrderCompletedEvent event = new OrderCompletedEvent("order-1", "customer-1", Instant.now()); + + testEventPublisher.publishEvent(event); + + assertThat(customers.find("customer-1")) + .isPresent().get() + .hasFieldOrPropertyWithValue("customerId", "customer-1") + .hasFieldOrPropertyWithValue("points", 60); + } + +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventPublisherUnitTest.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventPublisherUnitTest.java new file mode 100644 index 0000000000..f4bdeee90d --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventPublisherUnitTest.java @@ -0,0 +1,37 @@ +package com.baeldung.springmodulith.application.events; + +import com.baeldung.springmodulith.application.events.orders.OrderService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +class EventPublisherUnitTest { + + @Autowired + OrderService orderService; + + @Autowired + TestEventListener testEventListener; + + @BeforeEach + void beforeEach() { + testEventListener.reset(); + } + + @Test + void whenPlacingOrder_thenPublishApplicationEvent() { + orderService.placeOrder("customer1", "product1", "product2"); + + assertThat(testEventListener.getEvents()) + .hasSize(1).first() + .hasFieldOrPropertyWithValue("customerId", "customer1") + .hasFieldOrProperty("orderId") + .hasFieldOrProperty("timestamp"); + } + +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/SpringModulithScenarioApiUnitTest.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/SpringModulithScenarioApiUnitTest.java new file mode 100644 index 0000000000..f36a0c30e6 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/SpringModulithScenarioApiUnitTest.java @@ -0,0 +1,46 @@ +package com.baeldung.springmodulith.application.events; + +import com.baeldung.springmodulith.application.events.orders.OrderCompletedEvent; +import com.baeldung.springmodulith.application.events.orders.OrderService; +import com.baeldung.springmodulith.application.events.rewards.LoyalCustomersRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.modulith.test.ApplicationModuleTest; +import org.springframework.modulith.test.ApplicationModuleTest.BootstrapMode; +import org.springframework.modulith.test.Scenario; + +import java.time.Duration; +import java.time.Instant; + +import static java.time.Duration.ofMillis; +import static org.assertj.core.api.Assertions.assertThat; + +@ApplicationModuleTest +class SpringModulithScenarioApiUnitTest { + + @Autowired + OrderService orderService; + + @Autowired + LoyalCustomersRepository loyalCustomers; + + @Test + void whenPlacingOrder_thenPublishOrderCompletedEvent(Scenario scenario) { + scenario.stimulate(() -> orderService.placeOrder("customer-1", "product-1", "product-2")) + .andWaitForEventOfType(OrderCompletedEvent.class) + .toArriveAndVerify(evt -> assertThat(evt) + .hasFieldOrPropertyWithValue("customerId", "customer-1") + .hasFieldOrProperty("orderId") + .hasFieldOrProperty("timestamp")); + } + + @Test + void whenReceivingPublishOrderCompletedEvent_thenRewardCustomerWithLoyaltyPoints(Scenario scenario) { + scenario.publish(new OrderCompletedEvent("order-1", "customer-1", Instant.now())) + .andWaitForStateChange(() -> loyalCustomers.find("customer-1")) + .andVerify(it -> assertThat(it) + .isPresent().get() + .hasFieldOrPropertyWithValue("customerId", "customer-1") + .hasFieldOrPropertyWithValue("points", 60)); + } +} diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/TestEventListener.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/TestEventListener.java new file mode 100644 index 0000000000..8973a99355 --- /dev/null +++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/TestEventListener.java @@ -0,0 +1,29 @@ +package com.baeldung.springmodulith.application.events; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import com.baeldung.springmodulith.application.events.orders.OrderCompletedEvent; + +@Component +class TestEventListener { + + private final List events = new ArrayList<>(); + + @EventListener + void onEvent(OrderCompletedEvent event) { + events.add(event); + } + + public List getEvents() { + return events; + } + + public void reset() { + events.clear(); + } +} + diff --git a/spring-boot-modules/spring-caching-3/README.md b/spring-boot-modules/spring-caching-3/README.md new file mode 100644 index 0000000000..5429a0bb2f --- /dev/null +++ b/spring-boot-modules/spring-caching-3/README.md @@ -0,0 +1,2 @@ +## Relevant articles + diff --git a/spring-boot-modules/spring-caching-3/pom.xml b/spring-boot-modules/spring-caching-3/pom.xml new file mode 100644 index 0000000000..dcdf886502 --- /dev/null +++ b/spring-boot-modules/spring-caching-3/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + spring-caching-3 + 0.1-SNAPSHOT + spring-caching-3 + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + jar + + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + runtime + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookRepository.java b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookRepository.java new file mode 100644 index 0000000000..07898aa090 --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookRepository.java @@ -0,0 +1,10 @@ +package com.baeldung.caching.disable; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BookRepository extends JpaRepository { + + List findByIsbn(String isbn); +} diff --git a/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReview.java b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReview.java new file mode 100644 index 0000000000..210cdb0d8a --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReview.java @@ -0,0 +1,70 @@ +package com.baeldung.caching.disable; + +import jakarta.persistence.*; + +import java.util.Objects; + +@Entity +@Table(name = "BOOK_REVIEWS") +public class BookReview { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_reviews_reviews_id_seq") + @SequenceGenerator(name = "book_reviews_reviews_id_seq", sequenceName = "book_reviews_reviews_id_seq", allocationSize = 1) + private Long reviewsId; + private String userId; + private String isbn; + private String bookRating; + + public Long getReviewsId() { + return reviewsId; + } + + public void setReviewsId(Long reviewsId) { + this.reviewsId = reviewsId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getIsbn() { + return isbn; + } + + public void setIsbn(String isbn) { + this.isbn = isbn; + } + + public String getBookRating() { + return bookRating; + } + + public void setBookRating(String bookRating) { + this.bookRating = bookRating; + } + + @Override + public String toString() { + return "BookReview{" + "reviewsId=" + reviewsId + ", userId='" + userId + '\'' + ", isbn='" + isbn + '\'' + ", bookRating='" + bookRating + '\'' + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + BookReview that = (BookReview) o; + return Objects.equals(reviewsId, that.reviewsId) && Objects.equals(userId, that.userId) && Objects.equals(isbn, that.isbn) && Objects.equals(bookRating, that.bookRating); + } + + @Override + public int hashCode() { + return Objects.hash(reviewsId, userId, isbn, bookRating); + } +} diff --git a/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReviewApplication.java b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReviewApplication.java new file mode 100644 index 0000000000..c035424632 --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReviewApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.caching.disable; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; + +@SpringBootApplication +@EnableCaching +@EnableAutoConfiguration +public class BookReviewApplication { + public static void main(String[] args) { + SpringApplication.run(BookReviewApplication.class, args); + } +} diff --git a/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReviewsLogic.java b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReviewsLogic.java new file mode 100644 index 0000000000..1af8cdf4ab --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/BookReviewsLogic.java @@ -0,0 +1,25 @@ +package com.baeldung.caching.disable; + +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +@Service +public class BookReviewsLogic { + + @Autowired + private BookRepository bookRepository; + + @Autowired + private CacheManager cacheManager; + + @Cacheable(value = "book_reviews", key = "#isbn") + public List getBooksByIsbn(String isbn) { + return bookRepository.findByIsbn(isbn); + } +} diff --git a/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/CacheConfig.java b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/CacheConfig.java new file mode 100644 index 0000000000..23f90370f5 --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/main/java/com/baeldung/caching/disable/CacheConfig.java @@ -0,0 +1,23 @@ +package com.baeldung.caching.disable; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.cache.support.NoOpCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class CacheConfig { + + @Bean + public CacheManager cacheManager(@Value("${appconfig.cache.enabled}") String isCacheEnabled) { + if (isCacheEnabled.equalsIgnoreCase("false")) { + return new NoOpCacheManager(); + } + + return new ConcurrentMapCacheManager(); + } +} diff --git a/spring-boot-modules/spring-caching-3/src/main/resources/application.properties b/spring-boot-modules/spring-caching-3/src/main/resources/application.properties new file mode 100644 index 0000000000..f3c5e31283 --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/main/resources/application.properties @@ -0,0 +1,9 @@ +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= +# Enabling H2 Console +spring.h2.console.enabled=true +spring.h2.console.path=/h2 +spring.jpa.hibernate.ddl-auto=update +spring.jpa.show-sql=true diff --git a/spring-boot-modules/spring-caching-3/src/test/java/com/baeldung/caching/disable/BookReviewsLogicCacheDisabledUnitTest.java b/spring-boot-modules/spring-caching-3/src/test/java/com/baeldung/caching/disable/BookReviewsLogicCacheDisabledUnitTest.java new file mode 100644 index 0000000000..34f1052363 --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/test/java/com/baeldung/caching/disable/BookReviewsLogicCacheDisabledUnitTest.java @@ -0,0 +1,68 @@ +package com.baeldung.caching.disable; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest(classes = BookReviewApplication.class) +@ExtendWith(OutputCaptureExtension.class) +@TestPropertySource(properties = { + "appconfig.cache.enabled=false" +}) +public class BookReviewsLogicCacheDisabledUnitTest { + + @Autowired + private BookReviewsLogic bookReviewsLogic; + + @Autowired + private BookRepository bookRepository; + + @Test + public void givenCacheDisabled_whenLogicExecuted2ndTime_thenItQueriesDB(CapturedOutput output){ + BookReview bookReview = insertBookReview(); + + bookReviewsLogic.getBooksByIsbn(bookReview.getIsbn()); + + String target = "Hibernate: select b1_0.reviews_id," + + "b1_0.book_rating,b1_0.isbn,b1_0.user_id " + + "from book_reviews b1_0 " + + "where b1_0.isbn=?"; + + String[] logs = output.toString() + .split("\\r?\\n"); + assertThat(logs).anyMatch(e -> e.contains(target)); + + bookReviewsLogic.getBooksByIsbn(bookReview.getIsbn()); + logs = output.toString() + .split("\\r?\\n"); + + long count = Arrays.stream(logs) + .filter(e -> e.contains(target)) + .count(); + + // count 2 means the select query log from 1st and 2nd execution. + assertEquals(2, count); + } + + private BookReview insertBookReview() { + BookReview bookReview = new BookReview(); + bookReview.setReviewsId(123L); + bookReview.setBookRating("3.2"); + bookReview.setUserId("111"); + bookReview.setIsbn("1234"); + bookRepository.save(bookReview); + return bookReview; + } +} diff --git a/spring-boot-modules/spring-caching-3/src/test/java/com/baeldung/caching/disable/BookReviewsLogicUnitTest.java b/spring-boot-modules/spring-caching-3/src/test/java/com/baeldung/caching/disable/BookReviewsLogicUnitTest.java new file mode 100644 index 0000000000..8b1b2faa49 --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/test/java/com/baeldung/caching/disable/BookReviewsLogicUnitTest.java @@ -0,0 +1,64 @@ +package com.baeldung.caching.disable; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest(classes = BookReviewApplication.class) +@ExtendWith(OutputCaptureExtension.class) +public class BookReviewsLogicUnitTest { + + @Autowired + private BookReviewsLogic bookReviewsLogic; + + @Autowired + private BookRepository bookRepository; + + @Test + public void givenCacheEnabled_whenLogicExecuted2ndTime_thenItDoesntQueriesDB(CapturedOutput output){ + BookReview bookReview = insertBookReview(); + + String target = "Hibernate: select b1_0.reviews_id," + + "b1_0.book_rating,b1_0.isbn,b1_0.user_id " + + "from book_reviews b1_0 " + + "where b1_0.isbn=?"; + + // 1st execution + bookReviewsLogic.getBooksByIsbn(bookReview.getIsbn()); + String[] logs = output.toString() + .split("\\r?\\n"); + assertThat(logs).anyMatch(e -> e.contains(target)); + + // 2nd execution + bookReviewsLogic.getBooksByIsbn(bookReview.getIsbn()); + logs = output.toString() + .split("\\r?\\n"); + System.out.println(logs); + long count = Arrays.stream(logs) + .filter(e -> e.equals(target)) + .count(); + + // count 1 means the select query log from 1st execution. + assertEquals(1,count); + } + + private BookReview insertBookReview() { + BookReview bookReview = new BookReview(); + bookReview.setReviewsId(123L); + bookReview.setBookRating("3.2"); + bookReview.setUserId("111"); + bookReview.setIsbn("1234"); + bookRepository.save(bookReview); + return bookReview; + } +} diff --git a/spring-boot-modules/spring-caching-3/src/test/resources/application.properties b/spring-boot-modules/spring-caching-3/src/test/resources/application.properties new file mode 100644 index 0000000000..c62237927f --- /dev/null +++ b/spring-boot-modules/spring-caching-3/src/test/resources/application.properties @@ -0,0 +1,12 @@ +appconfig.cache.enabled=true +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= + +# Enabling H2 Console +spring.h2.console.enabled=true +spring.h2.console.path=/h2 +spring.jpa.hibernate.ddl-auto=update +server.port=8000 +spring.jpa.show-sql=true \ No newline at end of file diff --git a/spring-core-2/pom.xml b/spring-core-2/pom.xml index d22178004c..e05b839f00 100644 --- a/spring-core-2/pom.xml +++ b/spring-core-2/pom.xml @@ -10,52 +10,57 @@ com.baeldung - parent-boot-2 + parent-spring-5 0.0.1-SNAPSHOT - ../parent-boot-2 + ../parent-spring-5 - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-web - com.fasterxml.jackson.core jackson-databind + ${jackson.version} + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + org.springframework spring-web + ${spring.version} org.springframework spring-webmvc + ${spring.version} org.springframework spring-orm + ${spring.version} org.springframework spring-context + ${spring.version} org.springframework.retry spring-retry + ${spring-retry.version} org.springframework spring-websocket + ${spring.version} org.springframework spring-messaging + ${spring.version} javax.annotation @@ -66,41 +71,30 @@ org.springframework spring-aspects + ${spring.version} org.hibernate hibernate-core + ${hibernate.version} org.javassist javassist ${javassist.version} - - com.mysql - mysql-connector-j - runtime - - - org.hsqldb - hsqldb - org.hibernate.validator hibernate-validator + ${hibernate.version} javax.servlet javax.servlet-api - provided - - - javax.servlet - jstl - runtime + ${servlet-api.version} @@ -112,11 +106,7 @@ org.springframework spring-test - test - - - org.springframework.boot - spring-boot-starter-test + ${spring.version} test @@ -124,6 +114,12 @@ lombok ${lombok.version} + + org.springframework.boot + spring-boot-test-autoconfigure + ${spring-boot.version} + test + @@ -147,12 +143,15 @@ - com.baeldung.sample.App 1.3.2 + 4.0.0 + 6.2.5.Final 3.6 3.22.0-GA 3.2.2 + 2.0.5 + 2.2.2.RELEASE \ No newline at end of file diff --git a/spring-core-2/src/test/java/com/baeldung/concurrentrequest/ConcurrentRequestManualTest.java b/spring-core-2/src/test/java/com/baeldung/concurrentrequest/ConcurrentRequestManualTest.java index e7a3d0fc1a..1bd8a13836 100644 --- a/spring-core-2/src/test/java/com/baeldung/concurrentrequest/ConcurrentRequestManualTest.java +++ b/spring-core-2/src/test/java/com/baeldung/concurrentrequest/ConcurrentRequestManualTest.java @@ -4,20 +4,22 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultMatcher; /** * Test need to pause the main thread for up to 60 seconds */ + @SpringBootTest @AutoConfigureMockMvc public class ConcurrentRequestManualTest { diff --git a/spring-core-2/src/test/java/com/baeldung/version/VersionObtainerUnitTest.java b/spring-core-2/src/test/java/com/baeldung/version/VersionObtainerUnitTest.java index af074d5783..96ca803580 100644 --- a/spring-core-2/src/test/java/com/baeldung/version/VersionObtainerUnitTest.java +++ b/spring-core-2/src/test/java/com/baeldung/version/VersionObtainerUnitTest.java @@ -3,9 +3,7 @@ package com.baeldung.version; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; -import org.springframework.boot.test.context.SpringBootTest; -@SpringBootTest(classes = VersionObtainer.class) public class VersionObtainerUnitTest { public VersionObtainer version = new VersionObtainer(); diff --git a/spring-core-4/pom.xml b/spring-core-4/pom.xml index fb544e29fd..5e86542224 100644 --- a/spring-core-4/pom.xml +++ b/spring-core-4/pom.xml @@ -80,7 +80,6 @@ 1.3.2 - 2.2.2.RELEASE 4.0.2 4.0.0 1.3.2 diff --git a/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/managingkafkaconsumergroups/ManagingConsumerGroupsIntegrationTest.java b/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/managingkafkaconsumergroups/ManagingConsumerGroupsIntegrationTest.java index 6103fa884d..8fa07e4901 100644 --- a/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/managingkafkaconsumergroups/ManagingConsumerGroupsIntegrationTest.java +++ b/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/managingkafkaconsumergroups/ManagingConsumerGroupsIntegrationTest.java @@ -8,11 +8,13 @@ import org.springframework.kafka.config.KafkaListenerEndpointRegistry; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.listener.MessageListenerContainer; import org.springframework.kafka.test.context.EmbeddedKafka; +import org.springframework.util.CollectionUtils; import java.util.Objects; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ActiveProfiles; +import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -24,8 +26,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class ManagingConsumerGroupsIntegrationTest { private static final String CONSUMER_1_IDENTIFIER = "org.springframework.kafka.KafkaListenerEndpointContainer#1"; - private static final int TOTAL_PRODUCED_MESSAGES = 50000; - private static final int MESSAGE_WHERE_CONSUMER_1_LEAVES_GROUP = 10000; + private static final int TOTAL_PRODUCED_MESSAGES = 5000; + private static final int MESSAGE_WHERE_CONSUMER_1_LEAVES_GROUP = 10; @Autowired KafkaTemplate kafkaTemplate; @@ -57,11 +59,16 @@ public class ManagingConsumerGroupsIntegrationTest { } } while (currentMessage != TOTAL_PRODUCED_MESSAGES); Thread.sleep(2000); - if (consumerService.consumedPartitions != null - && consumerService.consumedPartitions.get("consumer-1") != null - && consumerService.consumedPartitions.get("consumer-0") != null) { - assertTrue(consumerService.consumedPartitions.get("consumer-1").size() >= 1); - assertTrue( consumerService.consumedPartitions.get("consumer-0").size() >= 1); + + Set partitionsConsumedBy1 = consumerService.consumedPartitions.get("consumer-1"); + Set partitionsConsumedBy0 = consumerService.consumedPartitions.get("consumer-0"); + + if (!CollectionUtils.isEmpty(partitionsConsumedBy0)) { + assertEquals(2, partitionsConsumedBy0.size()); + } + + if (!CollectionUtils.isEmpty(partitionsConsumedBy1)) { + assertEquals(1, partitionsConsumedBy1.size()); } } } diff --git a/spring-swagger-codegen/spring-openapi-generator-api-client/build.gradle b/spring-swagger-codegen/spring-openapi-generator-api-client/build.gradle index 595676158b..dba5c91e4e 100644 --- a/spring-swagger-codegen/spring-openapi-generator-api-client/build.gradle +++ b/spring-swagger-codegen/spring-openapi-generator-api-client/build.gradle @@ -100,9 +100,8 @@ ext { jackson_databind_version = "2.10.1" jackson_databind_nullable_version = "0.2.1" spring_web_version = "4.3.9.RELEASE" - jodatime_version = "2.9.9" junit_version = "4.13" - jackson_threeten_version = "2.9.10" + jackson_threeten_version = "2.15.2" } dependencies { diff --git a/spring-swagger-codegen/spring-openapi-generator-api-client/pom.xml b/spring-swagger-codegen/spring-openapi-generator-api-client/pom.xml index 91c8561652..1d99f0d439 100644 --- a/spring-swagger-codegen/spring-openapi-generator-api-client/pom.xml +++ b/spring-swagger-codegen/spring-openapi-generator-api-client/pom.xml @@ -266,7 +266,7 @@ 2.11.1 0.2.1 - 2.9.10 + 2.15.2 1.0.0 5.8.1 1.3.2 diff --git a/spring-swagger-codegen/spring-openapi-generator-api-client/src/main/java/com/baeldung/petstore/client/invoker/CustomInstantDeserializer.java b/spring-swagger-codegen/spring-openapi-generator-api-client/src/main/java/com/baeldung/petstore/client/invoker/CustomInstantDeserializer.java index 6957818fb4..01627bfb45 100644 --- a/spring-swagger-codegen/spring-openapi-generator-api-client/src/main/java/com/baeldung/petstore/client/invoker/CustomInstantDeserializer.java +++ b/spring-swagger-codegen/spring-openapi-generator-api-client/src/main/java/com/baeldung/petstore/client/invoker/CustomInstantDeserializer.java @@ -1,5 +1,6 @@ package com.baeldung.petstore.client.invoker; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonTokenId; import com.fasterxml.jackson.databind.DeserializationContext; @@ -148,11 +149,18 @@ public class CustomInstantDeserializer } @Override - protected JsonDeserializer withDateFormat(DateTimeFormatter dtf) { - if (dtf == _formatter) { - return this; - } - return new CustomInstantDeserializer(this, dtf); + protected ThreeTenDateTimeDeserializerBase withDateFormat(DateTimeFormatter dateTimeFormatter) { + return null; + } + + @Override + protected ThreeTenDateTimeDeserializerBase withLeniency(Boolean aBoolean) { + return null; + } + + @Override + protected ThreeTenDateTimeDeserializerBase withShape(JsonFormat.Shape shape) { + return null; } @Override diff --git a/spring-swagger-codegen/spring-swagger-codegen-api-client/build.gradle b/spring-swagger-codegen/spring-swagger-codegen-api-client/build.gradle index d1818ba78b..41ba18da76 100644 --- a/spring-swagger-codegen/spring-swagger-codegen-api-client/build.gradle +++ b/spring-swagger-codegen/spring-swagger-codegen-api-client/build.gradle @@ -97,7 +97,7 @@ ext { swagger_annotations_version = "1.5.15" jackson_version = "2.13.3" spring_web_version = "4.3.9.RELEASE" - jodatime_version = "2.9.9" + jodatime_version = "2.12.7" junit_version = "4.12" } diff --git a/spring-swagger-codegen/spring-swagger-codegen-api-client/pom.xml b/spring-swagger-codegen/spring-swagger-codegen-api-client/pom.xml index 5ad15f6851..7a6165cd97 100644 --- a/spring-swagger-codegen/spring-swagger-codegen-api-client/pom.xml +++ b/spring-swagger-codegen/spring-swagger-codegen-api-client/pom.xml @@ -190,7 +190,7 @@ 1.5.15 4.3.9.RELEASE - 2.9.9 + 2.12.7 1.3.2 2.2 1.5 diff --git a/spring-web-modules/spring-mvc-basics-2/pom.xml b/spring-web-modules/spring-mvc-basics-2/pom.xml index f79671ae63..4633d359ed 100644 --- a/spring-web-modules/spring-mvc-basics-2/pom.xml +++ b/spring-web-modules/spring-mvc-basics-2/pom.xml @@ -10,9 +10,9 @@ com.baeldung - parent-spring-5 + parent-spring-6 0.0.1-SNAPSHOT - ../../parent-spring-5 + ../../parent-spring-6 @@ -23,13 +23,13 @@ com.sun.mail - javax.mail - ${javax.mail.version} + jakarta.mail + ${jakarta.mail.version} - javax.servlet - javax.servlet-api - ${javax.servlet-api.version} + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet-api.version} javax.servlet.jsp @@ -158,8 +158,9 @@ 1.4.9 5.1.0 20240303 - 1.6.1 - 2.5.6 + 2.0.1 + 3.1.5 + 6.1.0-M2 \ No newline at end of file diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java index e70e801577..c1f0f9cd86 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/ApplicationConfiguration.java @@ -11,7 +11,7 @@ import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.feed.RssChannelHttpMessageConverter; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.multipart.MultipartResolver; -import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.multipart.support.StandardServletMultipartResolver; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -50,8 +50,7 @@ public class ApplicationConfiguration implements WebMvcConfigurer { @Bean public MultipartResolver multipartResolver() { - CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); - multipartResolver.setMaxUploadSize(5242880); + StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver(); return multipartResolver; } diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/WebInitializer.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/WebInitializer.java index aaa2443105..2964f82883 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/WebInitializer.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/WebInitializer.java @@ -5,9 +5,9 @@ import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRegistration; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRegistration; public class WebInitializer implements WebApplicationInitializer { diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/FileUploadController.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/FileUploadController.java index 2ca9e2b135..16ef37ccdc 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/FileUploadController.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/FileUploadController.java @@ -4,8 +4,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/MailController.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/MailController.java index f4088b8bb3..10af97ed85 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/MailController.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/MailController.java @@ -6,8 +6,8 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import javax.mail.MessagingException; -import javax.servlet.http.HttpServletRequest; +import jakarta.mail.MessagingException; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/SimpleControllerHandlerAdapterExample.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/SimpleControllerHandlerAdapterExample.java index d3800d4e1c..bf2ebc5941 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/SimpleControllerHandlerAdapterExample.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/SimpleControllerHandlerAdapterExample.java @@ -1,7 +1,7 @@ package com.baeldung.spring.controller; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java index cfbd33cd57..f325997b4a 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/rss/ArticleFeedView.java @@ -6,8 +6,8 @@ import com.rometools.rome.feed.rss.Item; import org.springframework.stereotype.Service; import org.springframework.web.servlet.view.feed.AbstractRssFeedView; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Date; import java.util.List; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/scribe/GithubController.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/scribe/GithubController.java index 61b91cbd4d..2f66ebeef3 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/scribe/GithubController.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/scribe/GithubController.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.view.RedirectView; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Random; import java.util.concurrent.ExecutionException; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/scribe/TwitterController.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/scribe/TwitterController.java index af47797ac5..174365f786 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/scribe/TwitterController.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/controller/scribe/TwitterController.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.view.RedirectView; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.concurrent.ExecutionException; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java index 2f3c44cf12..6a78008bdd 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/interceptor/FileUploadExceptionAdvice.java @@ -1,8 +1,8 @@ package com.baeldung.spring.interceptor; import org.springframework.web.servlet.ModelAndView; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailService.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailService.java index b7d5be09c8..15d69118d1 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailService.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailService.java @@ -3,7 +3,7 @@ package com.baeldung.spring.mail; import java.io.IOException; import java.util.Map; -import javax.mail.MessagingException; +import jakarta.mail.MessagingException; import freemarker.template.TemplateException; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailServiceImpl.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailServiceImpl.java index a0c8907a87..67ad64a005 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailServiceImpl.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/mail/EmailServiceImpl.java @@ -4,8 +4,8 @@ import java.io.File; import java.io.IOException; import java.util.Map; -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/push/controller/PushController.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/push/controller/PushController.java index e812e5df90..84626e9a74 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/push/controller/PushController.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/push/controller/PushController.java @@ -1,6 +1,6 @@ package com.baeldung.spring.push.controller; -import javax.servlet.http.PushBuilder; +import jakarta.servlet.http.PushBuilder; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/ForwardedServlet.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/ForwardedServlet.java index 222ed16c17..3028937df8 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/ForwardedServlet.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/ForwardedServlet.java @@ -1,10 +1,10 @@ package com.baeldung.spring.servlets; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/HelloServlet.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/HelloServlet.java index 89bfca795f..00a90d4076 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/HelloServlet.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/HelloServlet.java @@ -1,11 +1,11 @@ package com.baeldung.spring.servlets; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/hello") diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/RedirectedServlet.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/RedirectedServlet.java index 3074eec018..3dce7f325f 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/RedirectedServlet.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/RedirectedServlet.java @@ -1,10 +1,10 @@ package com.baeldung.spring.servlets; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; diff --git a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/WelcomeServlet.java b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/WelcomeServlet.java index 6f9ee8e7c4..a9c36af9fa 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/WelcomeServlet.java +++ b/spring-web-modules/spring-mvc-basics-2/src/main/java/com/baeldung/spring/servlets/WelcomeServlet.java @@ -1,10 +1,10 @@ package com.baeldung.spring.servlets; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/welcome") diff --git a/spring-web-modules/spring-mvc-basics-2/src/test/java/com/baeldung/spring/servlets/HelloServletIntegrationTest.java b/spring-web-modules/spring-mvc-basics-2/src/test/java/com/baeldung/spring/servlets/HelloServletIntegrationTest.java index 46cc280875..7196961f79 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/test/java/com/baeldung/spring/servlets/HelloServletIntegrationTest.java +++ b/spring-web-modules/spring-mvc-basics-2/src/test/java/com/baeldung/spring/servlets/HelloServletIntegrationTest.java @@ -3,7 +3,7 @@ package com.baeldung.spring.servlets; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/spring-web-modules/spring-mvc-basics-2/src/test/java/com/baeldung/spring/servlets/WelcomeServletIntegrationTest.java b/spring-web-modules/spring-mvc-basics-2/src/test/java/com/baeldung/spring/servlets/WelcomeServletIntegrationTest.java index d942fdd8d6..f941e3ba67 100644 --- a/spring-web-modules/spring-mvc-basics-2/src/test/java/com/baeldung/spring/servlets/WelcomeServletIntegrationTest.java +++ b/spring-web-modules/spring-mvc-basics-2/src/test/java/com/baeldung/spring/servlets/WelcomeServletIntegrationTest.java @@ -3,7 +3,7 @@ package com.baeldung.spring.servlets; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/spring-web-modules/spring-session/spring-session-redis/pom.xml b/spring-web-modules/spring-session/spring-session-redis/pom.xml index 6b1aec24e4..93a5656b3b 100644 --- a/spring-web-modules/spring-session/spring-session-redis/pom.xml +++ b/spring-web-modules/spring-session/spring-session-redis/pom.xml @@ -10,9 +10,9 @@ com.baeldung - parent-boot-2 + parent-boot-3 0.0.1-SNAPSHOT - ../../../parent-boot-2 + ../../../parent-boot-3 diff --git a/spring-web-modules/spring-session/spring-session-redis/src/main/java/com/baeldung/spring/session/SecurityConfig.java b/spring-web-modules/spring-session/spring-session-redis/src/main/java/com/baeldung/spring/session/SecurityConfig.java index d5add05025..d8ad2bfc84 100644 --- a/spring-web-modules/spring-session/spring-session-redis/src/main/java/com/baeldung/spring/session/SecurityConfig.java +++ b/spring-web-modules/spring-session/spring-session-redis/src/main/java/com/baeldung/spring/session/SecurityConfig.java @@ -1,9 +1,12 @@ package com.baeldung.spring.session; +import static org.springframework.security.config.Customizer.withDefaults; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.PasswordEncoder; @@ -25,13 +28,12 @@ public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.httpBasic() - .and() - .authorizeRequests() - .antMatchers("/") - .hasRole("ADMIN") - .anyRequest() - .authenticated(); + http.httpBasic(withDefaults()) + .sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)) + .authorizeRequests((authorizeRequests) -> authorizeRequests.requestMatchers("/") + .hasRole("ADMIN") + .anyRequest() + .authenticated()); return http.build(); } } diff --git a/spring-web-modules/spring-session/spring-session-redis/src/test/java/com/baeldung/spring/session/SessionControllerIntegrationTest.java b/spring-web-modules/spring-session/spring-session-redis/src/test/java/com/baeldung/spring/session/SessionControllerIntegrationTest.java index 065533c73f..64910cee09 100644 --- a/spring-web-modules/spring-session/spring-session-redis/src/test/java/com/baeldung/spring/session/SessionControllerIntegrationTest.java +++ b/spring-web-modules/spring-session/spring-session-redis/src/test/java/com/baeldung/spring/session/SessionControllerIntegrationTest.java @@ -5,7 +5,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; diff --git a/testing-modules/jmeter/pom.xml b/testing-modules/jmeter/pom.xml index 868023e762..f9521f9dd1 100644 --- a/testing-modules/jmeter/pom.xml +++ b/testing-modules/jmeter/pom.xml @@ -37,6 +37,17 @@ org.springframework.boot spring-boot-starter-security + + org.apache.jmeter + ApacheJMeter_core + 5.4.1 + + + + org.apache.jmeter + ApacheJMeter_http + 5.4.1 + diff --git a/testing-modules/jmeter/src/main/resources/jmeter.properties b/testing-modules/jmeter/src/main/resources/jmeter.properties new file mode 100644 index 0000000000..5321c72d7c --- /dev/null +++ b/testing-modules/jmeter/src/main/resources/jmeter.properties @@ -0,0 +1 @@ +jmeter.save.saveservice.output_format=xml \ No newline at end of file diff --git a/testing-modules/jmeter/src/test/java/com/baeldung/jmeterscript/JMeterLiveTest.java b/testing-modules/jmeter/src/test/java/com/baeldung/jmeterscript/JMeterLiveTest.java new file mode 100644 index 0000000000..e2a5eb59cf --- /dev/null +++ b/testing-modules/jmeter/src/test/java/com/baeldung/jmeterscript/JMeterLiveTest.java @@ -0,0 +1,116 @@ +package com.baeldung.jmeterscript; + + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Objects; + +import org.apache.jmeter.control.gui.LoopControlPanel; +import org.apache.jmeter.control.gui.TestPlanGui; +import org.apache.jmeter.engine.StandardJMeterEngine; +import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; +import org.apache.jmeter.reporters.ResultCollector; +import org.apache.jmeter.reporters.Summariser; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.TestPlan; +import org.apache.jmeter.threads.ThreadGroup; +import org.apache.jmeter.control.LoopController; +import org.apache.jmeter.threads.gui.ThreadGroupGui; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.collections.HashTree; +import org.junit.jupiter.api.Test; + +/** + * This is a Live Test so that JMETER_HOME Environment variable will be available to run this test. + */ +public class JMeterLiveTest { + + @Test + void givenJMeterScript_whenUsingCode_thenExecuteViaJavaProgram() throws IOException { + String jmeterHome = System.getenv("JMETER_HOME"); + if (jmeterHome == null) { + throw new RuntimeException("JMETER_HOME environment variable is not set."); + } + + String file = Objects.requireNonNull(JMeterLiveTest.class.getClassLoader().getResource("jmeter.properties")).getFile(); + JMeterUtils.setJMeterHome(jmeterHome); + + JMeterUtils.loadJMeterProperties(file); + JMeterUtils.initLocale(); + + StandardJMeterEngine jmeter = new StandardJMeterEngine(); + + HTTPSamplerProxy httpSampler = getHttpSamplerProxy(); + + LoopController loopController = getLoopController(); + + ThreadGroup threadGroup = getThreadGroup(loopController); + + TestPlan testPlan = getTestPlan(threadGroup); + + HashTree testPlanTree = new HashTree(); + HashTree threadGroupHashTree = testPlanTree.add(testPlan, threadGroup); + threadGroupHashTree.add(httpSampler); + + SaveService.saveTree(testPlanTree, Files.newOutputStream(Paths.get("script.jmx"))); + Summariser summer = null; + String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary"); + if (summariserName.length() > 0) { + summer = new Summariser(summariserName); + } + + String logFile = "output-logs.jtl"; + ResultCollector logger = new ResultCollector(summer); + logger.setFilename(logFile); + testPlanTree.add(testPlanTree.getArray()[0], logger); + + jmeter.configure(testPlanTree); + jmeter.run(); + + System.out.println("Test completed. See output-logs.jtl file for results"); + System.out.println("JMeter .jmx script is available at script.jmx"); + } + + private static TestPlan getTestPlan(ThreadGroup threadGroup) { + TestPlan testPlan = new TestPlan("Sample Test Plan"); + testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName()); + testPlan.setProperty(TestElement.GUI_CLASS, TestPlanGui.class.getName()); + testPlan.addThreadGroup(threadGroup); + return testPlan; + } + + private static ThreadGroup getThreadGroup(LoopController loopController) { + ThreadGroup threadGroup = new ThreadGroup(); + threadGroup.setName("Sample Thread Group"); + threadGroup.setNumThreads(10); + threadGroup.setRampUp(5); + threadGroup.setSamplerController(loopController); + threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName()); + threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName()); + return threadGroup; + } + + private static LoopController getLoopController() { + LoopController loopController = new LoopController(); + loopController.setLoops(1); + loopController.setFirst(true); + loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName()); + loopController.setProperty(TestElement.GUI_CLASS, LoopControlPanel.class.getName()); + loopController.initialize(); + return loopController; + } + + private static HTTPSamplerProxy getHttpSamplerProxy() { + HTTPSamplerProxy httpSampler = new HTTPSamplerProxy(); + httpSampler.setDomain("www.google.com"); + httpSampler.setPort(80); + httpSampler.setPath("/"); + httpSampler.setMethod("GET"); + httpSampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName()); + httpSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName()); + return httpSampler; + } +} diff --git a/vaadin/.gitignore b/vaadin/.gitignore new file mode 100644 index 0000000000..1ef3efefbd --- /dev/null +++ b/vaadin/.gitignore @@ -0,0 +1 @@ +frontend/generated \ No newline at end of file diff --git a/vaadin/frontend/index.html b/vaadin/frontend/index.html new file mode 100644 index 0000000000..d36e593475 --- /dev/null +++ b/vaadin/frontend/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + +
+ + diff --git a/vaadin/pom.xml b/vaadin/pom.xml index e3786471f5..ea9a3ed75a 100644 --- a/vaadin/pom.xml +++ b/vaadin/pom.xml @@ -1,13 +1,12 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.test vaadin 1.0-SNAPSHOT vaadin - war com.baeldung @@ -16,6 +15,11 @@ ../parent-boot-3 + + 17 + 24.3.3 + + @@ -30,39 +34,24 @@ - javax.servlet - javax.servlet-api - 4.0.1 - provided + com.vaadin + vaadin-core com.vaadin - vaadin-server - ${vaadin-server.version} - - - com.vaadin - vaadin-push - ${vaadin-push.version} - - - com.vaadin - vaadin-client-compiled - ${vaadin-client-compiled.version} - - - com.vaadin - vaadin-themes - ${vaadin-themes.version} + vaadin-spring-boot-starter org.springframework.boot spring-boot-starter-data-jpa - com.vaadin - vaadin-spring-boot-starter - ${vaadin-spring-boot-starter.version} + org.springframework.boot + spring-boot-starter-validation + + + io.projectreactor + reactor-core com.h2database @@ -77,47 +66,17 @@ - - org.apache.maven.plugins - maven-war-plugin - ${maven-war-plugin.version} - - false - - WEB-INF/classes/VAADIN/widgetsets/WEB-INF/** - - com.vaadin vaadin-maven-plugin - ${vaadin.plugin.version} - - - org.apache.maven.plugins - maven-clean-plugin - ${maven-clean-plugin.version} - - - - - src/main/webapp/VAADIN/themes - - **/styles.css - **/styles.scss.cache - - - - - - - - - org.eclipse.jetty - jetty-maven-plugin - ${jetty.plugin.version} - - 2 - + ${vaadin.version} + + + + prepare-frontend + + + org.springframework.boot @@ -126,68 +85,41 @@ - - - vaadin-addons - https://maven.vaadin.com/vaadin-addons - - - - vaadin-prerelease - - false - - - - - vaadin-prereleases - https://maven.vaadin.com/vaadin-prereleases - - - vaadin-snapshots - https://oss.sonatype.org/content/repositories/vaadin-snapshots/ - - false - - - true - - - - - - vaadin-prereleases - https://maven.vaadin.com/vaadin-prereleases - - - vaadin-snapshots - https://oss.sonatype.org/content/repositories/vaadin-snapshots/ - - false - - - true - - - + + production + + + + com.vaadin + vaadin-core + + + com.vaadin + vaadin-dev + + + + + + + + com.vaadin + vaadin-maven-plugin + ${vaadin.version} + + + + build-frontend + + compile + + + + + - - - 13.0.9 - 13.0.9 - 13.0.9 - 8.8.5 - 8.8.5 - 8.8.5 - 8.8.5 - 9.3.9.v20160517 - local - mytheme - 3.0.0 - - \ No newline at end of file diff --git a/vaadin/src/main/java/com/baeldung/Application.java b/vaadin/src/main/java/com/baeldung/Application.java index 1d3084723a..b62dd79158 100644 --- a/vaadin/src/main/java/com/baeldung/Application.java +++ b/vaadin/src/main/java/com/baeldung/Application.java @@ -1,15 +1,19 @@ package com.baeldung; +import com.baeldung.data.Employee; +import com.baeldung.data.EmployeeRepository; +import com.vaadin.flow.component.page.AppShellConfigurator; +import com.vaadin.flow.component.page.Push; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +@Push @SpringBootApplication -public class Application { +public class Application implements AppShellConfigurator { private static final Logger log = LoggerFactory.getLogger(Application.class); diff --git a/vaadin/src/main/java/com/baeldung/EmployeeEditor.java b/vaadin/src/main/java/com/baeldung/EmployeeEditor.java deleted file mode 100644 index ee312786d1..0000000000 --- a/vaadin/src/main/java/com/baeldung/EmployeeEditor.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.baeldung; - -import com.vaadin.flow.component.Key; -import com.vaadin.flow.component.KeyNotifier; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.icon.VaadinIcon; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.component.textfield.TextField; -import com.vaadin.flow.data.binder.Binder; -import com.vaadin.flow.spring.annotation.SpringComponent; -import com.vaadin.flow.spring.annotation.UIScope; -import org.springframework.beans.factory.annotation.Autowired; - -@SpringComponent -@UIScope -public class EmployeeEditor extends VerticalLayout implements KeyNotifier { - - private final EmployeeRepository repository; - - private Employee employee; - - TextField firstName = new TextField("First name"); - TextField lastName = new TextField("Last name"); - - Button save = new Button("Save", VaadinIcon.CHECK.create()); - Button cancel = new Button("Cancel"); - Button delete = new Button("Delete", VaadinIcon.TRASH.create()); - HorizontalLayout actions = new HorizontalLayout(save, cancel, delete); - - Binder binder = new Binder<>(Employee.class); - private ChangeHandler changeHandler; - - @Autowired - public EmployeeEditor(EmployeeRepository repository) { - this.repository = repository; - - add(firstName, lastName, actions); - - binder.bindInstanceFields(this); - - setSpacing(true); - - save.getElement().getThemeList().add("primary"); - delete.getElement().getThemeList().add("error"); - - addKeyPressListener(Key.ENTER, e -> save()); - - save.addClickListener(e -> save()); - delete.addClickListener(e -> delete()); - cancel.addClickListener(e -> editEmployee(employee)); - setVisible(false); - } - - void delete() { - repository.delete(employee); - changeHandler.onChange(); - } - - void save() { - repository.save(employee); - changeHandler.onChange(); - } - - public interface ChangeHandler { - void onChange(); - } - - public final void editEmployee(Employee c) { - if (c == null) { - setVisible(false); - return; - } - final boolean persisted = c.getId() != null; - if (persisted) { - employee = repository.findById(c.getId()).get(); - } else { - employee = c; - } - - cancel.setVisible(persisted); - binder.setBean(employee); - setVisible(true); - firstName.focus(); - } - - public void setChangeHandler(ChangeHandler h) { - changeHandler = h; - } -} diff --git a/vaadin/src/main/java/com/baeldung/IndexView.java b/vaadin/src/main/java/com/baeldung/IndexView.java new file mode 100644 index 0000000000..45ab534ef6 --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/IndexView.java @@ -0,0 +1,25 @@ +package com.baeldung; + +import com.baeldung.introduction.PushView; +import com.baeldung.introduction.basics.VaadinFlowBasics; +import com.baeldung.introduction.FormView; +import com.baeldung.introduction.GridView; +import com.baeldung.spring.EmployeesView; +import com.vaadin.flow.component.html.H1; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouterLink; + +@Route("") +public class IndexView extends VerticalLayout { + + public IndexView() { + add(new H1("Vaadin Flow examples")); + + add(new RouterLink("Basics", VaadinFlowBasics.class)); + add(new RouterLink("Grid", GridView.class)); + add(new RouterLink("Form", FormView.class)); + add(new RouterLink("Push", PushView.class)); + add(new RouterLink("CRUD", EmployeesView.class)); + } +} diff --git a/vaadin/src/main/java/com/baeldung/MainView.java b/vaadin/src/main/java/com/baeldung/MainView.java deleted file mode 100644 index 6d4c0aaa88..0000000000 --- a/vaadin/src/main/java/com/baeldung/MainView.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.baeldung; - -import org.springframework.util.StringUtils; - -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.icon.VaadinIcon; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.component.textfield.TextField; -import com.vaadin.flow.data.value.ValueChangeMode; -import com.vaadin.flow.router.Route; - -@Route -public class MainView extends VerticalLayout { - - private final EmployeeRepository employeeRepository; - - private final EmployeeEditor editor; - - final Grid grid; - - final TextField filter; - - private final Button addNewBtn; - - public MainView(EmployeeRepository repo, EmployeeEditor editor) { - this.employeeRepository = repo; - this.editor = editor; - this.grid = new Grid<>(Employee.class); - this.filter = new TextField(); - this.addNewBtn = new Button("New employee", VaadinIcon.PLUS.create()); - - HorizontalLayout actions = new HorizontalLayout(filter, addNewBtn); - add(actions, grid, editor); - - grid.setHeight("200px"); - grid.setColumns("id", "firstName", "lastName"); - grid.getColumnByKey("id").setWidth("50px").setFlexGrow(0); - - filter.setPlaceholder("Filter by last name"); - - filter.setValueChangeMode(ValueChangeMode.EAGER); - filter.addValueChangeListener(e -> listEmployees(e.getValue())); - - grid.asSingleSelect().addValueChangeListener(e -> { - editor.editEmployee(e.getValue()); - }); - - addNewBtn.addClickListener(e -> editor.editEmployee(new Employee("", ""))); - - editor.setChangeHandler(() -> { - editor.setVisible(false); - listEmployees(filter.getValue()); - }); - - listEmployees(null); - } - - void listEmployees(String filterText) { - if (StringUtils.isEmpty(filterText)) { - grid.setItems(employeeRepository.findAll()); - } else { - grid.setItems(employeeRepository.findByLastNameStartsWithIgnoreCase(filterText)); - } - } -} diff --git a/vaadin/src/main/java/com/baeldung/data/Contact.java b/vaadin/src/main/java/com/baeldung/data/Contact.java new file mode 100644 index 0000000000..8e534d5bb0 --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/data/Contact.java @@ -0,0 +1,73 @@ +package com.baeldung.data; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; + +@Entity +public class Contact { + + @Id + @GeneratedValue + private Long id; + @NotBlank + private String name; + @Email + private String email; + @Pattern(regexp = "\\d{3}-\\d{3}-\\d{4}") + private String phone; + + public Contact() { + } + + public Contact(String name, String email, String phone) { + this.name = name; + this.email = email; + this.phone = phone; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @Override + public String toString() { + return "Contact {" + + "id=" + id + + ", name='" + name + "'" + + ", email='" + email + "'" + + ", phone='" + phone + "'" + + " }"; + } +} diff --git a/vaadin/src/main/java/com/baeldung/data/ContactRepository.java b/vaadin/src/main/java/com/baeldung/data/ContactRepository.java new file mode 100644 index 0000000000..15c4adaaa9 --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/data/ContactRepository.java @@ -0,0 +1,6 @@ +package com.baeldung.data; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ContactRepository extends JpaRepository { +} diff --git a/vaadin/src/main/java/com/baeldung/data/DataInitializer.java b/vaadin/src/main/java/com/baeldung/data/DataInitializer.java new file mode 100644 index 0000000000..dba162d4e4 --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/data/DataInitializer.java @@ -0,0 +1,46 @@ +package com.baeldung.data; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@Component +public class DataInitializer implements CommandLineRunner { + + private final ContactRepository contactRepository; + + public DataInitializer(ContactRepository contactRepository) { + this.contactRepository = contactRepository; + } + + private final List firstNames = List.of( + "John", "Jane", "Emily", "Michael", "Sarah", "James", "Mary", "Robert", + "Patricia", "William", "Linda", "David", "Barbara", "Richard", "Susan", + "Joseph", "Jessica", "Thomas", "Karen", "Charles" + ); + private final List lastNames = List.of( + "Doe", "Smith", "Johnson", "Brown", "Davis", "Miller", "Wilson", "Moore", + "Taylor", "Anderson", "Thomas", "Jackson", "White", "Harris", "Martin", + "Thompson", "Garcia", "Martinez", "Robinson", "Clark" + ); + + @Override + public void run(String... args) throws Exception { + var contacts = new ArrayList(); + Random random = new Random(); + + for (int i = 0; i < 100; i++) { + String firstName = firstNames.get(random.nextInt(firstNames.size())); + String lastName = lastNames.get(random.nextInt(lastNames.size())); + String email = String.format("%s.%s@example.com", firstName.toLowerCase(), lastName.toLowerCase()); + String phone = String.format("555-%03d-%04d", random.nextInt(1000), random.nextInt(10000)); + + contacts.add(new Contact(firstName + " " + lastName, email, phone)); + } + + contactRepository.saveAll(contacts); + } +} diff --git a/vaadin/src/main/java/com/baeldung/Employee.java b/vaadin/src/main/java/com/baeldung/data/Employee.java similarity index 75% rename from vaadin/src/main/java/com/baeldung/Employee.java rename to vaadin/src/main/java/com/baeldung/data/Employee.java index 75a0dc84b3..cdaa426eeb 100644 --- a/vaadin/src/main/java/com/baeldung/Employee.java +++ b/vaadin/src/main/java/com/baeldung/data/Employee.java @@ -1,8 +1,10 @@ -package com.baeldung; +package com.baeldung.data; + import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.validation.constraints.Size; @Entity public class Employee { @@ -11,11 +13,13 @@ public class Employee { @GeneratedValue private Long id; + @Size(min = 2, message = "First name must have at least 2 characters") private String firstName; + @Size(min = 2, message = "Last name must have at least 2 characters") private String lastName; - protected Employee() { + public Employee() { } public Employee(String firstName, String lastName) { @@ -27,6 +31,10 @@ public class Employee { return id; } + public void setId(Long id) { + this.id = id; + } + public String getFirstName() { return firstName; } diff --git a/vaadin/src/main/java/com/baeldung/EmployeeRepository.java b/vaadin/src/main/java/com/baeldung/data/EmployeeRepository.java similarity index 89% rename from vaadin/src/main/java/com/baeldung/EmployeeRepository.java rename to vaadin/src/main/java/com/baeldung/data/EmployeeRepository.java index 044160da78..0d54148c85 100644 --- a/vaadin/src/main/java/com/baeldung/EmployeeRepository.java +++ b/vaadin/src/main/java/com/baeldung/data/EmployeeRepository.java @@ -1,4 +1,4 @@ -package com.baeldung; +package com.baeldung.data; import java.util.List; diff --git a/vaadin/src/main/java/com/baeldung/introduction/BindData.java b/vaadin/src/main/java/com/baeldung/introduction/BindData.java deleted file mode 100644 index 299554c039..0000000000 --- a/vaadin/src/main/java/com/baeldung/introduction/BindData.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.baeldung.introduction; - -public class BindData { - - private String bindName; - - public BindData(String bindName){ - this.bindName = bindName; - } - - public String getBindName() { - return bindName; - } - - public void setBindName(String bindName) { - this.bindName = bindName; - } - - -} diff --git a/vaadin/src/main/java/com/baeldung/introduction/FormView.java b/vaadin/src/main/java/com/baeldung/introduction/FormView.java new file mode 100644 index 0000000000..34bc334b27 --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/introduction/FormView.java @@ -0,0 +1,88 @@ +package com.baeldung.introduction; + +import com.baeldung.data.Contact; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.html.H2; +import com.vaadin.flow.component.notification.Notification; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.component.textfield.TextField; +import com.vaadin.flow.data.binder.BeanValidationBinder; +import com.vaadin.flow.data.binder.Binder; +import com.vaadin.flow.router.Route; + +@Route("form") +public class FormView extends HorizontalLayout { + + public FormView() { + addBeanValidationExample(); + addCustomValidationExample(); + } + + private void addBeanValidationExample() { + var nameField = new TextField("Name"); + var emailField = new TextField("Email"); + var phoneField = new TextField("Phone"); + var saveButton = new Button("Save"); + + var binder = new BeanValidationBinder<>(Contact.class); + + binder.forField(nameField).bind(Contact::getName, Contact::setName); + binder.forField(emailField).bind(Contact::getEmail, Contact::setEmail); + binder.forField(phoneField).bind(Contact::getPhone, Contact::setPhone); + + var contact = new Contact("John Doe", "john@doe.com", "123-456-7890"); + binder.setBean(contact); + + saveButton.addClickListener(e -> { + if (binder.validate().isOk()) { + Notification.show("Saved " + contact); + } + }); + + add(new VerticalLayout( + new H2("Bean Validation Example"), + nameField, + emailField, + phoneField, + saveButton + )); + } + + private void addCustomValidationExample() { + + var nameField = new TextField("Name"); + var emailField = new TextField("Email"); + var phoneField = new TextField("Phone"); + var saveButton = new Button("Save"); + + var binder = new Binder<>(Contact.class); + + binder.forField(nameField) + .asRequired() + .bind(Contact::getName, Contact::setName); + binder.forField(emailField) + .withValidator(email -> email.contains("@"), "Not a valid email address") + .bind(Contact::getEmail, Contact::setEmail); + binder.forField(phoneField) + .withValidator(phone -> phone.matches("\\d{3}-\\d{3}-\\d{4}"), "Not a valid phone number") + .bind(Contact::getPhone, Contact::setPhone); + + var contact = new Contact("John Doe", "john@doe.com", "123-456-7890"); + binder.setBean(contact); + + saveButton.addClickListener(e -> { + if (binder.validate().isOk()) { + Notification.show("Saved " + contact); + } + }); + + add(new VerticalLayout( + new H2("Custom Validation Example"), + nameField, + emailField, + phoneField, + saveButton + )); + } +} diff --git a/vaadin/src/main/java/com/baeldung/introduction/GridView.java b/vaadin/src/main/java/com/baeldung/introduction/GridView.java new file mode 100644 index 0000000000..3d9500d7d8 --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/introduction/GridView.java @@ -0,0 +1,40 @@ +package com.baeldung.introduction; + +import com.baeldung.data.Contact; +import com.baeldung.data.ContactRepository; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.html.H1; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.spring.data.VaadinSpringDataHelpers; + +@Route("grid") +public class GridView extends VerticalLayout { + + public GridView(ContactRepository contactRepository) { + + add(new H1("Using data grids")); + + // Define a grid for a Contact entity + var grid = new Grid(); + grid.addColumn(Contact::getName).setHeader("Name"); + grid.addColumn(Contact::getEmail).setHeader("Email"); + grid.addColumn(Contact::getPhone).setHeader("Phone"); + + // There are two ways to populate the grid with data: + + // 1) In-memory with a list of items + var contacts = contactRepository.findAll(); + grid.setItems(contacts); + + // 2) with a callback to lazily load items from a data source. + grid.setItems(query -> { + // Turn the page, offset, filter, sort and other info in query into a Spring Data PageRequest + var pageRequest = VaadinSpringDataHelpers.toSpringPageRequest(query); + return contactRepository.findAll(pageRequest).stream(); + }); + + add(grid); + + } +} diff --git a/vaadin/src/main/java/com/baeldung/introduction/PushView.java b/vaadin/src/main/java/com/baeldung/introduction/PushView.java new file mode 100644 index 0000000000..b49aa06e4d --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/introduction/PushView.java @@ -0,0 +1,32 @@ +package com.baeldung.introduction; + +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; +import reactor.core.publisher.Flux; + +import java.time.Duration; +import java.time.Instant; + +// Ensure you have @Push annotation in your Application class +@Route("push") +public class PushView extends VerticalLayout { + + public PushView() { + var output = new Span(); + + // Publish server time once a second + var serverTime = Flux.interval(Duration.ofSeconds(1)) + .map(o -> "Server time: " + Instant.now()); + + + serverTime.subscribe(time -> + // ui.access is required to update the UI from a background thread + getUI().ifPresent(ui -> + ui.access(() -> output.setText(time)) + ) + ); + + add(output); + } +} diff --git a/vaadin/src/main/java/com/baeldung/introduction/Row.java b/vaadin/src/main/java/com/baeldung/introduction/Row.java deleted file mode 100644 index 412a286376..0000000000 --- a/vaadin/src/main/java/com/baeldung/introduction/Row.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.baeldung.introduction; - -public class Row { - - private String column1; - - private String column2; - - private String column3; - - public Row() { - - } - - public Row(String column1, String column2, String column3) { - super(); - this.column1 = column1; - this.column2 = column2; - this.column3 = column3; - } - - public String getColumn1() { - return column1; - } - - public void setColumn1(String column1) { - this.column1 = column1; - } - - public String getColumn2() { - return column2; - } - - public void setColumn2(String column2) { - this.column2 = column2; - } - - public String getColumn3() { - return column3; - } - - public void setColumn3(String column3) { - this.column3 = column3; - } -} \ No newline at end of file diff --git a/vaadin/src/main/java/com/baeldung/introduction/VaadinUI.java b/vaadin/src/main/java/com/baeldung/introduction/VaadinUI.java deleted file mode 100644 index 05a8340bde..0000000000 --- a/vaadin/src/main/java/com/baeldung/introduction/VaadinUI.java +++ /dev/null @@ -1,281 +0,0 @@ -package com.baeldung.introduction; - -import java.time.Instant; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import com.vaadin.annotations.Push; -import com.vaadin.annotations.Theme; -import com.vaadin.annotations.VaadinServletConfiguration; -import com.vaadin.data.Binder; -import com.vaadin.data.validator.StringLengthValidator; -import com.vaadin.icons.VaadinIcons; -import com.vaadin.server.ExternalResource; -import com.vaadin.server.VaadinRequest; -import com.vaadin.server.VaadinServlet; -import com.vaadin.ui.Button; -import com.vaadin.ui.CheckBox; -import com.vaadin.ui.ComboBox; -import com.vaadin.ui.DateField; -import com.vaadin.ui.FormLayout; -import com.vaadin.ui.Grid; -import com.vaadin.ui.GridLayout; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.InlineDateField; -import com.vaadin.ui.Label; -import com.vaadin.ui.Link; -import com.vaadin.ui.ListSelect; -import com.vaadin.ui.NativeButton; -import com.vaadin.ui.NativeSelect; -import com.vaadin.ui.Panel; -import com.vaadin.ui.PasswordField; -import com.vaadin.ui.RichTextArea; -import com.vaadin.ui.TextArea; -import com.vaadin.ui.TextField; -import com.vaadin.ui.TwinColSelect; -import com.vaadin.ui.UI; -import com.vaadin.ui.VerticalLayout; -import jakarta.servlet.annotation.WebServlet; - -@SuppressWarnings("serial") -@Push -@Theme("mytheme") -public class VaadinUI extends UI { - - private Label currentTime; - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - protected void init(VaadinRequest vaadinRequest) { - final VerticalLayout verticalLayout = new VerticalLayout(); - verticalLayout.setSpacing(true); - verticalLayout.setMargin(true); - final GridLayout gridLayout = new GridLayout(3, 2); - gridLayout.setSpacing(true); - gridLayout.setMargin(true); - final HorizontalLayout horizontalLayout = new HorizontalLayout(); - horizontalLayout.setSpacing(true); - horizontalLayout.setMargin(true); - final FormLayout formLayout = new FormLayout(); - formLayout.setSpacing(true); - formLayout.setMargin(true); - final GridLayout buttonLayout = new GridLayout(3, 5); - buttonLayout.setMargin(true); - buttonLayout.setSpacing(true); - - final Label label = new Label(); - label.setId("Label"); - label.setValue("Label Value"); - label.setCaption("Label"); - gridLayout.addComponent(label); - - final Link link = new Link("Baeldung", new ExternalResource("http://www.baeldung.com/")); - link.setId("Link"); - link.setTargetName("_blank"); - gridLayout.addComponent(link); - - final TextField textField = new TextField(); - textField.setId("TextField"); - textField.setCaption("TextField:"); - textField.setValue("TextField Value"); - textField.setIcon(VaadinIcons.USER); - gridLayout.addComponent(textField); - - final TextArea textArea = new TextArea(); - textArea.setCaption("TextArea"); - textArea.setId("TextArea"); - textArea.setValue("TextArea Value"); - gridLayout.addComponent(textArea); - - final DateField dateField = new DateField("DateField", LocalDate.ofEpochDay(0)); - dateField.setId("DateField"); - gridLayout.addComponent(dateField); - - final PasswordField passwordField = new PasswordField(); - passwordField.setId("PasswordField"); - passwordField.setCaption("PasswordField:"); - passwordField.setValue("password"); - gridLayout.addComponent(passwordField); - - final RichTextArea richTextArea = new RichTextArea(); - richTextArea.setCaption("Rich Text Area"); - richTextArea.setValue("

RichTextArea

"); - richTextArea.setSizeFull(); - - Panel richTextPanel = new Panel(); - richTextPanel.setContent(richTextArea); - - final InlineDateField inlineDateField = new InlineDateField(); - inlineDateField.setValue(LocalDate.ofEpochDay(0)); - inlineDateField.setCaption("Inline Date Field"); - horizontalLayout.addComponent(inlineDateField); - - Button normalButton = new Button("Normal Button"); - normalButton.setId("NormalButton"); - normalButton.addClickListener(e -> { - label.setValue("CLICK"); - }); - buttonLayout.addComponent(normalButton); - - Button tinyButton = new Button("Tiny Button"); - tinyButton.addStyleName("tiny"); - buttonLayout.addComponent(tinyButton); - - Button smallButton = new Button("Small Button"); - smallButton.addStyleName("small"); - buttonLayout.addComponent(smallButton); - - Button largeButton = new Button("Large Button"); - largeButton.addStyleName("large"); - buttonLayout.addComponent(largeButton); - - Button hugeButton = new Button("Huge Button"); - hugeButton.addStyleName("huge"); - buttonLayout.addComponent(hugeButton); - - Button disabledButton = new Button("Disabled Button"); - disabledButton.setDescription("This button cannot be clicked"); - disabledButton.setEnabled(false); - buttonLayout.addComponent(disabledButton); - - Button dangerButton = new Button("Danger Button"); - dangerButton.addStyleName("danger"); - buttonLayout.addComponent(dangerButton); - - Button friendlyButton = new Button("Friendly Button"); - friendlyButton.addStyleName("friendly"); - buttonLayout.addComponent(friendlyButton); - - Button primaryButton = new Button("Primary Button"); - primaryButton.addStyleName("primary"); - buttonLayout.addComponent(primaryButton); - - NativeButton nativeButton = new NativeButton("Native Button"); - buttonLayout.addComponent(nativeButton); - - Button iconButton = new Button("Icon Button"); - iconButton.setIcon(VaadinIcons.ALIGN_LEFT); - buttonLayout.addComponent(iconButton); - - Button borderlessButton = new Button("BorderLess Button"); - borderlessButton.addStyleName("borderless"); - buttonLayout.addComponent(borderlessButton); - - Button linkButton = new Button("Link Button"); - linkButton.addStyleName("link"); - buttonLayout.addComponent(linkButton); - - Button quietButton = new Button("Quiet Button"); - quietButton.addStyleName("quiet"); - buttonLayout.addComponent(quietButton); - - horizontalLayout.addComponent(buttonLayout); - - final CheckBox checkbox = new CheckBox("CheckBox"); - checkbox.setValue(true); - checkbox.addValueChangeListener(e -> checkbox.setValue(!checkbox.getValue())); - formLayout.addComponent(checkbox); - - List numbers = new ArrayList(); - numbers.add("One"); - numbers.add("Ten"); - numbers.add("Eleven"); - ComboBox comboBox = new ComboBox("ComboBox"); - comboBox.setItems(numbers); - formLayout.addComponent(comboBox); - - ListSelect listSelect = new ListSelect("ListSelect"); - listSelect.setItems(numbers); - listSelect.setRows(2); - formLayout.addComponent(listSelect); - - NativeSelect nativeSelect = new NativeSelect("NativeSelect"); - nativeSelect.setItems(numbers); - formLayout.addComponent(nativeSelect); - - TwinColSelect twinColSelect = new TwinColSelect("TwinColSelect"); - twinColSelect.setItems(numbers); - - Grid grid = new Grid(Row.class); - grid.setColumns("column1", "column2", "column3"); - Row row1 = new Row("Item1", "Item2", "Item3"); - Row row2 = new Row("Item4", "Item5", "Item6"); - List rows = new ArrayList(); - rows.add(row1); - rows.add(row2); - grid.setItems(rows); - - Panel panel = new Panel("Panel"); - panel.setContent(grid); - panel.setSizeUndefined(); - - Panel serverPushPanel = new Panel("Server Push"); - FormLayout timeLayout = new FormLayout(); - timeLayout.setSpacing(true); - timeLayout.setMargin(true); - currentTime = new Label("No TIME..."); - timeLayout.addComponent(currentTime); - serverPushPanel.setContent(timeLayout); - serverPushPanel.setSizeUndefined(); - ScheduledExecutorService scheduleExecutor = Executors.newScheduledThreadPool(1); - Runnable task = () -> { - currentTime.setValue("Current Time : " + Instant.now()); - }; - scheduleExecutor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS); - - FormLayout dataBindingLayout = new FormLayout(); - dataBindingLayout.setSpacing(true); - dataBindingLayout.setMargin(true); - - Binder binder = new Binder<>(); - BindData bindData = new BindData("BindData"); - binder.readBean(bindData); - TextField bindedTextField = new TextField(); - bindedTextField.setWidth("250px"); - binder.forField(bindedTextField).bind(BindData::getBindName, BindData::setBindName); - dataBindingLayout.addComponent(bindedTextField); - - FormLayout validatorLayout = new FormLayout(); - validatorLayout.setSpacing(true); - validatorLayout.setMargin(true); - - HorizontalLayout textValidatorLayout = new HorizontalLayout(); - textValidatorLayout.setSpacing(true); - textValidatorLayout.setMargin(true); - - - BindData stringValidatorBindData = new BindData(""); - TextField stringValidator = new TextField(); - Binder stringValidatorBinder = new Binder<>(); - stringValidatorBinder.setBean(stringValidatorBindData); - stringValidatorBinder.forField(stringValidator) - .withValidator(new StringLengthValidator("String must have 2-5 characters lenght", 2, 5)) - .bind(BindData::getBindName, BindData::setBindName); - - textValidatorLayout.addComponent(stringValidator); - Button buttonStringValidator = new Button("Validate String"); - buttonStringValidator.addClickListener(e -> stringValidatorBinder.validate()); - textValidatorLayout.addComponent(buttonStringValidator); - - validatorLayout.addComponent(textValidatorLayout); - verticalLayout.addComponent(gridLayout); - verticalLayout.addComponent(richTextPanel); - verticalLayout.addComponent(horizontalLayout); - verticalLayout.addComponent(formLayout); - verticalLayout.addComponent(twinColSelect); - verticalLayout.addComponent(panel); - verticalLayout.addComponent(serverPushPanel); - verticalLayout.addComponent(dataBindingLayout); - verticalLayout.addComponent(validatorLayout); - setContent(verticalLayout); - } - - @WebServlet(urlPatterns = "/VAADIN/*", name = "MyUIServlet", asyncSupported = true) - @VaadinServletConfiguration(ui = VaadinUI.class, productionMode = false) - public static class MyUIServlet extends VaadinServlet { - } -} \ No newline at end of file diff --git a/vaadin/src/main/java/com/baeldung/introduction/basics/ExampleLayout.java b/vaadin/src/main/java/com/baeldung/introduction/basics/ExampleLayout.java new file mode 100644 index 0000000000..6d6e2b2b7d --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/introduction/basics/ExampleLayout.java @@ -0,0 +1,45 @@ +package com.baeldung.introduction.basics; + +import com.baeldung.data.Contact; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.component.splitlayout.SplitLayout; +import com.vaadin.flow.component.textfield.TextField; +import com.vaadin.flow.router.Route; + +import java.util.List; + +@Route("example-layout") +public class ExampleLayout extends SplitLayout { + + public ExampleLayout() { + var grid = new Grid<>(Contact.class); + grid.setColumns("name", "email", "phone"); + grid.setItems(List.of( + new Contact("John Doe", "john@doe.com", "123 456 789"), + new Contact("Jane Doe", "jane@doe.com", "987 654 321") + )); + + var form = new VerticalLayout(); + + var nameField = new TextField("Name"); + var emailField = new TextField("Email"); + var phoneField = new TextField("Phone"); + var saveButton = new Button("Save"); + var cancelButton = new Button("Cancel"); + + form.add( + nameField, + emailField, + phoneField, + new HorizontalLayout(saveButton, cancelButton) + ); + + setSizeFull(); + setSplitterPosition(70); + addToPrimary(grid); + addToSecondary(form); + } +} diff --git a/vaadin/src/main/java/com/baeldung/introduction/basics/HelloWorldView.java b/vaadin/src/main/java/com/baeldung/introduction/basics/HelloWorldView.java new file mode 100644 index 0000000000..477cf378c6 --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/introduction/basics/HelloWorldView.java @@ -0,0 +1,13 @@ +package com.baeldung.introduction.basics; + +import com.vaadin.flow.component.html.H1; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; + +@Route("hello-world") +public class HelloWorldView extends VerticalLayout { + + public HelloWorldView() { + add(new H1("Hello, World!")); + } +} diff --git a/vaadin/src/main/java/com/baeldung/introduction/basics/VaadinFlowBasics.java b/vaadin/src/main/java/com/baeldung/introduction/basics/VaadinFlowBasics.java new file mode 100644 index 0000000000..7a22eea0ab --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/introduction/basics/VaadinFlowBasics.java @@ -0,0 +1,63 @@ +package com.baeldung.introduction.basics; + +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.checkbox.Checkbox; +import com.vaadin.flow.component.html.H1; +import com.vaadin.flow.component.notification.Notification; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.component.textfield.TextField; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouterLink; + +// The @Route annotation defines the URL path for the view +// Any component, most commonly a layout, can be used as a view +@Route("basics") +public class VaadinFlowBasics extends VerticalLayout { + public VaadinFlowBasics() { + + // Add components to the layout with the add method + add(new H1("Vaadin Flow Basics")); + + // Components are Java objects + var textField = new TextField("Name"); + var button = new Button("Click me"); + + // Layouts define the structure of the UI + var verticalLayout = new VerticalLayout( + new Button("Top"), + new Button("Middle"), + new Button("Bottom") + ); + add(verticalLayout); + + var horizontalLayout = new HorizontalLayout( + new Button("Left"), + new Button("Center"), + new Button("Right") + ); + add(horizontalLayout); + + // Layouts can be nested for more complex structures + var nestedLayout = new VerticalLayout( + new HorizontalLayout(new Button("Top Left"), new Button("Top Right")), + new HorizontalLayout(new Button("Bottom Left"), new Button("Bottom Right")) + ); + add(nestedLayout); + + add(new RouterLink("Example layout", ExampleLayout.class)); + + // Use RouterLink to navigate to other views + var link = new RouterLink("Hello world view", HelloWorldView.class); + add(link); + + // Use events to react to user input + var nameField = new TextField("Your name"); + var helloButton = new Button("Say hello"); + helloButton.addClickListener(e -> { + Notification.show("Hello, " + nameField.getValue()); + }); + add(nameField, helloButton); + + } +} diff --git a/vaadin/src/main/java/com/baeldung/spring/EmployeeEditor.java b/vaadin/src/main/java/com/baeldung/spring/EmployeeEditor.java new file mode 100644 index 0000000000..7984ecce05 --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/spring/EmployeeEditor.java @@ -0,0 +1,86 @@ +package com.baeldung.spring; + +import com.baeldung.data.Employee; +import com.vaadin.flow.component.Composite; +import com.vaadin.flow.component.Key; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; +import com.vaadin.flow.component.icon.VaadinIcon; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.component.textfield.TextField; +import com.vaadin.flow.data.binder.BeanValidationBinder; +import com.vaadin.flow.data.binder.Binder; + +public class EmployeeEditor extends Composite { + + public interface SaveListener { + void onSave(Employee employee); + } + + public interface DeleteListener { + void onDelete(Employee employee); + } + + public interface CancelListener { + void onCancel(); + } + + private Employee employee; + + private SaveListener saveListener; + private DeleteListener deleteListener; + private CancelListener cancelListener; + + private final Binder binder = new BeanValidationBinder<>(Employee.class); + +public EmployeeEditor() { + var firstName = new TextField("First name"); + var lastName = new TextField("Last name"); + + var save = new Button("Save", VaadinIcon.CHECK.create()); + var cancel = new Button("Cancel"); + var delete = new Button("Delete", VaadinIcon.TRASH.create()); + + binder.forField(firstName).bind("firstName"); + binder.forField(lastName).bind("lastName"); + + save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + save.addClickListener(e -> save()); + save.addClickShortcut(Key.ENTER); + + delete.addThemeVariants(ButtonVariant.LUMO_ERROR); + delete.addClickListener(e -> deleteListener.onDelete(employee)); + + cancel.addClickListener(e -> cancelListener.onCancel()); + + getContent().add(firstName, lastName, new HorizontalLayout(save, cancel, delete)); +} + + private void save() { + // Save the form into a new instance of Employee + var updated = new Employee(); + updated.setId(employee.getId()); + + if (binder.writeBeanIfValid(updated)) { + saveListener.onSave(updated); + } + } + + public void setEmployee(Employee employee) { + this.employee = employee; + binder.readBean(employee); + } + + public void setSaveListener(SaveListener saveListener) { + this.saveListener = saveListener; + } + + public void setDeleteListener(DeleteListener deleteListener) { + this.deleteListener = deleteListener; + } + + public void setCancelListener(CancelListener cancelListener) { + this.cancelListener = cancelListener; + } +} diff --git a/vaadin/src/main/java/com/baeldung/spring/EmployeesView.java b/vaadin/src/main/java/com/baeldung/spring/EmployeesView.java new file mode 100644 index 0000000000..edb1f7a75b --- /dev/null +++ b/vaadin/src/main/java/com/baeldung/spring/EmployeesView.java @@ -0,0 +1,95 @@ +package com.baeldung.spring; + +import com.baeldung.data.Employee; +import com.baeldung.data.EmployeeRepository; + +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.icon.VaadinIcon; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.component.textfield.TextField; +import com.vaadin.flow.data.value.ValueChangeMode; +import com.vaadin.flow.router.Route; + +@Route("employees") +public class EmployeesView extends VerticalLayout { + + private final EmployeeRepository employeeRepository; + + private final TextField filter; + private final Grid grid; + private final EmployeeEditor editor; + + + public EmployeesView(EmployeeRepository repo) { + employeeRepository = repo; + + // Create components + var addButton = new Button("New employee", VaadinIcon.PLUS.create()); + filter = new TextField(); + grid = new Grid<>(Employee.class); + editor = new EmployeeEditor(); + + // Configure components + configureEditor(); + + addButton.addClickListener(e -> editEmployee(new Employee())); + + filter.setPlaceholder("Filter by last name"); + filter.setValueChangeMode(ValueChangeMode.LAZY); + filter.addValueChangeListener(e -> updateEmployees(e.getValue())); + + grid.setHeight("200px"); + grid.asSingleSelect().addValueChangeListener(e -> editEmployee(e.getValue())); + + // Compose layout + var actionsLayout = new HorizontalLayout(filter, addButton); + add(actionsLayout, grid, editor); + + // List customers + updateEmployees(""); + } + + private void configureEditor() { + editor.setVisible(false); + + editor.setSaveListener(employee -> { + var saved = employeeRepository.save(employee); + updateEmployees(filter.getValue()); + editor.setEmployee(null); + grid.asSingleSelect().setValue(saved); + }); + + editor.setDeleteListener(employee -> { + employeeRepository.delete(employee); + updateEmployees(filter.getValue()); + editEmployee(null); + }); + + editor.setCancelListener(() -> { + editEmployee(null); + }); + } + + private void editEmployee(Employee employee) { + editor.setEmployee(employee); + + if (employee != null) { + editor.setVisible(true); + } else { + // Deselect grid + grid.asSingleSelect().setValue(null); + editor.setVisible(false); + } + + } + + private void updateEmployees(String filterText) { + if (filterText.isEmpty()) { + grid.setItems(employeeRepository.findAll()); + } else { + grid.setItems(employeeRepository.findByLastNameStartsWithIgnoreCase(filterText)); + } + } +} diff --git a/vaadin/src/main/webapp/VAADIN/themes/mytheme/addons.scss b/vaadin/src/main/webapp/VAADIN/themes/mytheme/addons.scss deleted file mode 100644 index a5670b70c7..0000000000 --- a/vaadin/src/main/webapp/VAADIN/themes/mytheme/addons.scss +++ /dev/null @@ -1,7 +0,0 @@ -/* This file is automatically managed and will be overwritten from time to time. */ -/* Do not manually edit this file. */ - -/* Import and include this mixin into your project theme to include the addon themes */ -@mixin addons { -} - diff --git a/vaadin/src/main/webapp/VAADIN/themes/mytheme/favicon.ico b/vaadin/src/main/webapp/VAADIN/themes/mytheme/favicon.ico deleted file mode 100644 index ffb34a65c7..0000000000 Binary files a/vaadin/src/main/webapp/VAADIN/themes/mytheme/favicon.ico and /dev/null differ diff --git a/vaadin/src/main/webapp/VAADIN/themes/mytheme/mytheme.scss b/vaadin/src/main/webapp/VAADIN/themes/mytheme/mytheme.scss deleted file mode 100644 index 2c5fb8b944..0000000000 --- a/vaadin/src/main/webapp/VAADIN/themes/mytheme/mytheme.scss +++ /dev/null @@ -1,38 +0,0 @@ -// If you edit this file you need to compile the theme. See README.md for details. - -// Global variable overrides. Must be declared before importing Valo. - -// Defines the plaintext font size, weight and family. Font size affects general component sizing. -//$v-font-size: 16px; -//$v-font-weight: 300; -//$v-font-family: "Open Sans", sans-serif; - -// Defines the border used by all components. -//$v-border: 1px solid (v-shade 0.7); -//$v-border-radius: 4px; - -// Affects the color of some component elements, e.g Button, Panel title, etc -//$v-background-color: hsl(210, 0%, 98%); -// Affects the color of content areas, e.g Panel and Window content, TextField input etc -//$v-app-background-color: $v-background-color; - -// Affects the visual appearance of all components -//$v-gradient: v-linear 8%; -//$v-bevel-depth: 30%; -//$v-shadow-opacity: 5%; - -// Defines colors for indicating status (focus, success, failure) -//$v-focus-color: valo-focus-color(); // Calculates a suitable color automatically -//$v-friendly-color: #2c9720; -//$v-error-indicator-color: #ed473b; - -// For more information, see: https://vaadin.com/book/-/page/themes.valo.html -// Example variants can be copy/pasted from https://vaadin.com/wiki/-/wiki/Main/Valo+Examples - -@import "../valo/valo.scss"; - -@mixin mytheme { - @include valo; - - // Insert your own theme rules here -} diff --git a/vaadin/src/main/webapp/VAADIN/themes/mytheme/styles.scss b/vaadin/src/main/webapp/VAADIN/themes/mytheme/styles.scss deleted file mode 100644 index bba1d493c0..0000000000 --- a/vaadin/src/main/webapp/VAADIN/themes/mytheme/styles.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import "mytheme.scss"; -@import "addons.scss"; - -// This file prefixes all rules with the theme name to avoid causing conflicts with other themes. -// The actual styles should be defined in mytheme.scss - -.mytheme { - @include addons; - @include mytheme; - -}