diff --git a/.gitignore b/.gitignore index 6bd173c0..4d5f508b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ target dump.rdb Test.java *.log +config +.env diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..cce8b46d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 HelloKoding + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 8f114609..8d5a18f2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ -# HelloKoding's Practical Coding Courses, Tutorials and Examples Series +# HelloKoding - Practical Coding Guides -https://hellokoding.com/tag/courses/ +HelloKoding provides practical coding guides series of Spring Boot, Java, Algorithms and other topics on software engineering + +https://hellokoding.com diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsByBruteForce.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsByBruteForce.java new file mode 100644 index 00000000..44f57ae1 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsByBruteForce.java @@ -0,0 +1,44 @@ +package com.hellokoding.algorithm; + +public class AnagramsByBruteForce { + static boolean areAnagrams(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + return areAnagramsByBackTracking(0, s, t); + } + + static boolean areAnagramsByBackTracking(int i, String s1, String s2) { + if (i == s1.length()-1) { + return s1.equals(s2); + } + + for(int j=i; j histogram = new HashMap<>(); + + for (int i = 0; i < s.length(); i++) { + histogram.compute(s.charAt(i), (k,v) -> (v == null ? 0 : v) + 1); + histogram.compute(t.charAt(i), (k,v) -> (v == null ? 0 : v) - 1); + } + + for (int count : histogram.values()) { + if (count != 0) { + return false; + } + } + + return true; + } + + public static void main(String[] args) { + System.out.println(areAnagrams("listen", "silent")); + System.out.println(areAnagrams("eleven plus two", "twelve plus one")); + System.out.println(areAnagrams("rat", "car")); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsBySorting.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsBySorting.java new file mode 100644 index 00000000..e726bb6d --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsBySorting.java @@ -0,0 +1,25 @@ +package com.hellokoding.algorithm; + +import java.util.Arrays; + +public class AnagramsBySorting { + static boolean areAnagrams(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + char[] a1 = s.toCharArray(); + char[] a2 = t.toCharArray(); + + Arrays.sort(a1); + Arrays.sort(a2); + + return Arrays.equals(a1, a2); + } + + public static void main(String[] args) { + System.out.println(areAnagrams("listen", "silent")); + System.out.println(areAnagrams("eleven plus two", "twelve plus one")); + System.out.println(areAnagrams("rat", "car")); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/BreadthFirstSearchOnGraph.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/BreadthFirstSearchOnGraph.java deleted file mode 100644 index 3c69c18a..00000000 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/BreadthFirstSearchOnGraph.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.hellokoding.algorithm; - -import com.hellokoding.datastructure.UndirectedGraphByAdjacencyList; - -import java.util.ArrayDeque; -import java.util.Queue; - -public class BreadthFirstSearchOnGraph { - public void breadthFirstSearch(UndirectedGraphByAdjacencyList g, int startingVertex) { - boolean[] visited = new boolean[g.getV()]; - Queue queue = new ArrayDeque<>(); - - visited[startingVertex] = true; - queue.offer(startingVertex); - - while (!queue.isEmpty()) { - Integer currentVertex = queue.poll(); - System.out.printf("%d ", currentVertex); - - for(Integer vertex : g.getAdjacencyList(currentVertex)) { - if (!visited[vertex]) { - visited[vertex] = true; - queue.offer(vertex); - } - } - } - } - - public static void main(String[] args) { - UndirectedGraphByAdjacencyList graph = new UndirectedGraphByAdjacencyList(5); - graph.addEdge(0, 1); - graph.addEdge(1, 2); - graph.addEdge(2, 0); - graph.addEdge(1, 3); - graph.addEdge(2, 4); - - new BreadthFirstSearchOnGraph().breadthFirstSearch(graph, 0); - } -} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/DP_LongestCommonSubsequence.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/DP_LongestCommonSubsequence.java index df1c63d3..41bf7796 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/DP_LongestCommonSubsequence.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/DP_LongestCommonSubsequence.java @@ -20,6 +20,7 @@ static int lengthOfLongestCommonSubsequence(String S, String T) { } public static void main(String[] args) { + System.out.println(lengthOfLongestCommonSubsequence("ABCD", "ACBAD")); System.out.println(lengthOfLongestCommonSubsequence("XMJYAUZ", "MZJAWXU")); } } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/DepthFirstSearchOnGraph.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/DepthFirstSearchOnGraph.java deleted file mode 100644 index 64fc20f4..00000000 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/DepthFirstSearchOnGraph.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.hellokoding.algorithm; - -import com.hellokoding.datastructure.UndirectedGraphByAdjacencyList; - -import java.util.ArrayDeque; -import java.util.Deque; - -public class DepthFirstSearchOnGraph { - public void depthFirstSearch(UndirectedGraphByAdjacencyList g, int startingVertex) { - boolean[] visited = new boolean[g.getV()]; - Deque stack = new ArrayDeque<>(); - - visited[startingVertex] = true; - stack.push(startingVertex); - - while (!stack.isEmpty()) { - Integer currentVertex = stack.pop(); - System.out.printf("%d ", currentVertex); - - for(Integer vertex : g.getAdjacencyList(currentVertex)) { - if (!visited[vertex]) { - visited[vertex] = true; - stack.push(vertex); - } - } - } - } - - public static void main(String[] args) { - UndirectedGraphByAdjacencyList graph = new UndirectedGraphByAdjacencyList(5); - graph.addEdge(0, 2); - graph.addEdge(1, 2); - graph.addEdge(1, 0); - graph.addEdge(1, 3); - graph.addEdge(2, 4); - - new DepthFirstSearchOnGraph().depthFirstSearch(graph, 0); - } -} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/FairIndexOf2Arrays.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/FairIndexOf2Arrays.java new file mode 100644 index 00000000..39d6550c --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/FairIndexOf2Arrays.java @@ -0,0 +1,42 @@ +// you can also use imports, for example: +// import java.util.*; + +// you can write to stdout for debugging purposes, e.g. +// System.out.println("this is a debug message"); +package com.hellokoding.algorithm; +class FairIndexOf2Arrays { + public static int findFairIndex(int[] A, int[] B) { + int N = A.length; + int sumA = getSum(A); + int sumB = getSum(B); + int sumSubA = 0, sumSubB = 0; + //int ans = 0; + + for(int i=0; i findLIS(int[] arr) { } public static void main(String[] args) { - int arr[] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}; - System.out.println("LIS: " + findLIS(arr).toString()); + int[] arr = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}; + System.out.println(findLIS(arr).toString()); } } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LISLengthByBruteForce.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LISLengthByBruteForce.java new file mode 100644 index 00000000..c1f4f525 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LISLengthByBruteForce.java @@ -0,0 +1,23 @@ +package com.hellokoding.algorithm; + +public class LISLengthByBruteForce { + static int lengthOfLIS(int[] a, int prevIndex, int currIndex) { + if (currIndex == a.length) { + return 0; + } + + int included = 0; + if (prevIndex < 0 || a[currIndex] > a[prevIndex]) { + included = 1 + lengthOfLIS(a, currIndex, currIndex+1); + } + + int excluded = lengthOfLIS(a, prevIndex, currIndex+1); + + return Math.max(included, excluded); + } + + public static void main(String[] args) { + int[] arr = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}; + System.out.println(lengthOfLIS(arr, -1, 0)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LISLengthByDPBottomUp.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LISLengthByDPBottomUp.java index f7eab41d..66fea131 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LISLengthByDPBottomUp.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LISLengthByDPBottomUp.java @@ -3,13 +3,17 @@ import java.util.Arrays; public class LISLengthByDPBottomUp { - static int findLengthOfLIS(int[] arr) { - int[] cache = new int[arr.length]; + static int lengthOfLIS(int[] a) { + if (a.length == 0) { + return 0; + } + + int[] cache = new int[a.length]; Arrays.fill(cache, 1); - for (int i = 1; i < arr.length; i++) { + for (int i = 1; i < a.length; i++) { for (int j = 0; j < i; j++) { - if (arr[i] > arr[j]) { + if (a[i] > a[j]) { cache[i] = Math.max(cache[i], cache[j] + 1); } } @@ -20,7 +24,7 @@ static int findLengthOfLIS(int[] arr) { public static void main(String[] args) { - int arr[] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}; - System.out.println("Length of LIS: " + findLengthOfLIS(arr)); + int[] arr = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}; + System.out.println(lengthOfLIS(arr)); } } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LongestDistinctChars_BruteForce.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LongestDistinctChars_BruteForce.java new file mode 100644 index 00000000..adb0c907 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LongestDistinctChars_BruteForce.java @@ -0,0 +1,36 @@ +package com.hellokoding.algorithm; + +import java.util.HashSet; +import java.util.Set; + +public class LongestDistinctChars_BruteForce { + static String bruteforce(String s) { + int l = 0, i = 0, j = 0; + int n = s.length(); + + for (; i < n; i++) { + for (j = i+1; j <= n; j++) { + if (isUnique(s, i, j)) l = Math.max(l, j-i); + else break; + } + + if (j == n+1) break; + } + + return s.substring(i, i+l); + } + + static boolean isUnique(String s, int i, int j) { + HashSet set = new HashSet<>(); + for (int k = i; k < j; k++) { + if (set.contains(s.charAt(k))) return false; + set.add(s.charAt(k)); + } + + return true; + } + + public static void main(String[] args) { + System.out.println(bruteforce("HelloKoding")); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestDistinct.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LongestDistinctChars_SlidingWindow.java similarity index 76% rename from datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestDistinct.java rename to datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LongestDistinctChars_SlidingWindow.java index bbbeeab9..428fe789 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestDistinct.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/LongestDistinctChars_SlidingWindow.java @@ -3,8 +3,8 @@ import java.util.HashSet; import java.util.Set; -public class String_LongestDistinct { - static String longestDistinctCharacters(String s) { +public class LongestDistinctChars_SlidingWindow { + static String slidingWindow(String s) { int n = s.length(); int l = 0, i = 0, j = 0; Set chars = new HashSet<>(); @@ -22,6 +22,6 @@ static String longestDistinctCharacters(String s) { } public static void main(String[] args) { - System.out.println(longestDistinctCharacters("HelloKoding")); + System.out.println(slidingWindow("HelloKoding")); } } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/ReverseWordsByIterative.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/ReverseWordsByIterative.java new file mode 100644 index 00000000..b6aace46 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/ReverseWordsByIterative.java @@ -0,0 +1,23 @@ +package com.hellokoding.algorithm; + +public class ReverseWordsByIterative { + static String reverseWords(String s) { + StringBuilder result = new StringBuilder(); + int j = s.length(); + + for (int i = j-1; i >= 0; i--) { + char c = s.charAt(i); + if(Character.isWhitespace(c) || i==0) { + result.append(s, i, j); + j = i; + } + } + + return result.toString().trim(); + } + + public static void main(String[] args) { + System.out.println(reverseWords("Hello Koding")); + System.out.println(reverseWords(" Live on time, emit no evil ")); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/ReverseWordsBySplittingAndIterative.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/ReverseWordsBySplittingAndIterative.java new file mode 100644 index 00000000..9736e82b --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/ReverseWordsBySplittingAndIterative.java @@ -0,0 +1,21 @@ +package com.hellokoding.algorithm; + +public class ReverseWordsBySplittingAndIterative { + static String reverseWords(String s) { + StringBuilder result = new StringBuilder(); + + String[] words = s.split(" "); + for (int i = words.length - 1; i >= 0; i--) { + if (!words[i].isEmpty()) { + result.append(words[i]).append(" "); + } + } + + return result.toString().trim(); + } + + public static void main(String[] args) { + System.out.println(reverseWords("Hello Koding")); + System.out.println(reverseWords(" Live on time, emit no evil ")); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SmallestMissingPositiveNumberByHashtable.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SmallestMissingPositiveNumberByHashtable.java new file mode 100644 index 00000000..41ac270a --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SmallestMissingPositiveNumberByHashtable.java @@ -0,0 +1,28 @@ +package com.hellokoding.algorithm; + +import java.util.Arrays; +import java.util.HashSet; + +public class SmallestMissingPositiveNumberByHashtable { + public static int findSmallest(int[] a) { + HashSet hashSet = new HashSet<>(); + + for (int value : a) { + if (value > 0) { + hashSet.add(value); + } + } + + int min = 1; + while (hashSet.contains(min)) { + min++; + } + + return min; + } + + public static void main(String[] args) { + System.out.println(findSmallest(new int[]{1, 3, 6, 4, 1, 2})); + System.out.println(findSmallest(new int[]{-1, -3, 5})); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SmallestMissingPositiveNumberBySorting.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SmallestMissingPositiveNumberBySorting.java new file mode 100644 index 00000000..d5d2634e --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SmallestMissingPositiveNumberBySorting.java @@ -0,0 +1,23 @@ +package com.hellokoding.algorithm; + +import java.util.Arrays; + +public class SmallestMissingPositiveNumberBySorting { + public static int findSmallest(int[] a) { + Arrays.sort(a); + int min = 1; + + for (int i = 0; i < a.length; i++) { + if (min == a[i]) { + min++; + } + } + + return min; + } + + public static void main(String[] args) { + System.out.println(findSmallest(new int[]{1, 3, 6, 4, 1, 2})); + System.out.println(findSmallest(new int[]{-1, -3, 5})); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_Anagram.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_Anagram.java deleted file mode 100644 index f3817a74..00000000 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_Anagram.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.hellokoding.algorithm; - -import java.util.Arrays; - -public class String_Anagram { - static boolean areAnagram(String str1, String str2) { - char[] charArr1 = str1.toCharArray(); - char[] charArr2 = str2.toCharArray(); - - Arrays.sort(charArr1); - Arrays.sort(charArr2); - - return Arrays.equals(charArr1, charArr2); - } - - public static void main(String[] args) { - System.out.println(areAnagram("abc", "bca")); - System.out.println(areAnagram("abc", "bcb")); - } -} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestPalindrome_BruteForce.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestPalindrome_BruteForce.java index 063a89dd..03159b55 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestPalindrome_BruteForce.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestPalindrome_BruteForce.java @@ -1,12 +1,14 @@ package com.hellokoding.algorithm; public class String_LongestPalindrome_BruteForce { - static String findLongestPalindromicSubstring(String S) { + static String findLongestPalindromicSubstring(String s) { + if(s == null || s.isEmpty()) return ""; + String longestPalindrome = ""; - char[] charArr = S.toCharArray(); + char[] charArr = s.toCharArray(); for (int i = 0; i < charArr.length; i++) { for (int j = i; j < charArr.length; j++) { - String substr = S.substring(i, j+1); + String substr = s.substring(i, j+1); if(isPalindrome(substr) && substr.length() > longestPalindrome.length()) { longestPalindrome = substr; } @@ -16,9 +18,9 @@ static String findLongestPalindromicSubstring(String S) { return longestPalindrome; } - static boolean isPalindrome(String S) { - String reversedStr = new StringBuilder(S).reverse().toString(); - return S.equals(reversedStr); + static boolean isPalindrome(String s) { + String reversedStr = new StringBuilder(s).reverse().toString(); + return s.equals(reversedStr); } public static void main(String[] args) { diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestPalindrome_DP.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestPalindrome_DP.java index 57a62695..39ca8570 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestPalindrome_DP.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_LongestPalindrome_DP.java @@ -1,8 +1,10 @@ package com.hellokoding.algorithm; public class String_LongestPalindrome_DP { - static String findLongestPalindromicSubstring(String S) { - int n = S.length(); + static String findLongestPalindromicSubstring(String s) { + if(s == null || s.isEmpty()) return ""; + + int n = s.length(); int startIndex = 0; int maxLength = 1; boolean[][] P = new boolean[n][n]; @@ -12,7 +14,7 @@ static String findLongestPalindromicSubstring(String S) { } for (int i = 0; i < n - 1; i++) { - if (S.charAt(i) == S.charAt(i+1)) { + if (s.charAt(i) == s.charAt(i+1)) { P[i][i+1] = true; startIndex = i; maxLength = 2; @@ -22,7 +24,7 @@ static String findLongestPalindromicSubstring(String S) { for (int l = 3; l <= n; l++) { for (int i = 0; i < n - l + 1; i++) { int j = i + l - 1; - if (S.charAt(i) == S.charAt(j) && P[i+1][j-1]) { + if (s.charAt(i) == s.charAt(j) && P[i+1][j-1]) { P[i][j] = true; if (l > maxLength) { @@ -33,7 +35,7 @@ static String findLongestPalindromicSubstring(String S) { } } - return S.substring(startIndex, startIndex + maxLength); + return s.substring(startIndex, startIndex + maxLength); } public static void main(String[] args) { diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_ReverseWords.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_ReverseWords.java deleted file mode 100644 index 85a1e48d..00000000 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/String_ReverseWords.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.hellokoding.algorithm; - -public class String_ReverseWords { - static String reverseWords(String str) { - StringBuilder result = new StringBuilder(); - String[] words = str.split(" "); - for (int i = words.length - 1; i >= 0; i--) { - result.append(words[i]).append(" "); - } - - return result.toString(); - } - - public static void main(String[] args) { - System.out.println(reverseWords("Hello Koding")); - } -} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumBruteforce.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumBruteforce.java index 36408766..de466e52 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumBruteforce.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumBruteforce.java @@ -3,20 +3,20 @@ import java.util.Arrays; public class SubarrayGivenSumBruteforce { - public static int[] findSubarray(int[] A, int sum) { - for(int i = 0; i < A.length; i++) { + public static int[] findSubarray(int[] a, int k) { + for(int i = 0; i < a.length; i++) { - int currentSum = A[i]; + int currentSum = a[i]; - for(int j = i + 1; j <= A.length; j++) { + for(int j = i + 1; j <= a.length; j++) { - if (currentSum == sum) { - return Arrays.copyOfRange(A, i, j); - } else if (currentSum > sum || j == A.length) { + if (currentSum == k) { + return Arrays.copyOfRange(a, i, j); + } else if (currentSum > k || j == a.length) { break; } - currentSum += A[j]; + currentSum += a[j]; } } @@ -24,7 +24,8 @@ public static int[] findSubarray(int[] A, int sum) { } public static void main (String[] args) { - int[] A = {4, 0, 11, 6, 1, 7}; - System.out.println(Arrays.toString(findSubarray(A, 8))); + int[] a = {4, 0, 11, 6, 1, 7}; + int k = 8; + System.out.println(Arrays.toString(findSubarray(a, k))); } } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumHashtable.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumHashtable.java new file mode 100644 index 00000000..3e51b274 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumHashtable.java @@ -0,0 +1,37 @@ +package com.hellokoding.algorithm; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class SubarrayGivenSumHashtable { + public static int countSubArrays(int[] a, int k) { + int cumulativeCount = 0; + int ci = 0, cj = 0; + Map map = new HashMap<>(); + map.put(0, 1); + + for (int value : a) { + cj += value; + ci = cj - k; + if (map.containsKey(ci)) { + cumulativeCount += map.get(ci); + } + + map.put(cj, map.getOrDefault(cj, 0) + 1); + } + + return cumulativeCount; + } + + public static void main (String[] args) { + int[] a = {-4, 12, -11, 6, 1, 7}; + int k = 8; + System.out.println(countSubArrays(a, k)); + + int[] b = {0,0, 0}; + k = 0; + System.out.println(countSubArrays(b, k)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumWindowSliding.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumWindowSliding.java index 5b4bd53a..0fe40dc7 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumWindowSliding.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumWindowSliding.java @@ -3,26 +3,27 @@ import java.util.Arrays; public class SubarrayGivenSumWindowSliding { - public static int[] findSubarray(int[] A, int sum) { + public static int[] findSubarray(int[] a, int k) { int windowSum = 0, i = 0, j = 0; - while (i < A.length) { - while (j < A.length && windowSum < sum) { - windowSum += A[j++]; + while (i < a.length) { + while (j < a.length && windowSum < k) { + windowSum += a[j++]; } - if (windowSum == sum) { - return Arrays.copyOfRange(A, i, j); + if (windowSum == k) { + return Arrays.copyOfRange(a, i, j); } - windowSum -= A[i++]; + windowSum -= a[i++]; } return null; } public static void main (String[] args) { - int[] A = {4, 0, 11, 6, 1, 7}; - System.out.println(Arrays.toString(findSubarray(A, 8))); + int[] a = {4, 0, 11, 6, 1, 7}; + int k = 8; + System.out.println(Arrays.toString(findSubarray(a, k))); } } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TreeBreadthFirstTraversal.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TreeBreadthFirstTraversal.java index 37382012..4bdaa166 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TreeBreadthFirstTraversal.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TreeBreadthFirstTraversal.java @@ -1,35 +1,40 @@ package com.hellokoding.algorithm; import com.hellokoding.datastructure.BSTByLinkedList; +import java.util.*; -import java.util.ArrayDeque; -import java.util.Queue; +public class TreeBreadthFirstTraversal { + List> res = new ArrayList<>(); + public List> levelOrderTraversal(BSTByLinkedList.Node root) { + if (root == null) return res; -public class TreeBreadthFirstTraversal extends BSTByLinkedList { - void breadthFirstTraversal(Node root) { - Queue queue = new ArrayDeque<>(); + Deque queue = new ArrayDeque<>(); queue.offer(root); while (!queue.isEmpty()) { - Node currentNode = queue.poll(); - System.out.println(currentNode); + List level = new ArrayList<>(); - if (currentNode.left != null) - queue.offer(currentNode.left); + int size = queue.size(); + for (int i=0; i> result = new TreeBreadthFirstTraversal().levelOrderTraversal(tree.root); + System.out.println(Arrays.deepToString(result.toArray())); } } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoPointers_2Sum.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoPointers_2Sum.java deleted file mode 100644 index fa4fa07f..00000000 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoPointers_2Sum.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.hellokoding.algorithm; - -import java.util.Arrays; - -public class TwoPointers_2Sum { - boolean contains2(int[] a, int targetSum) { - Arrays.sort(a); - - int i = 0; - int j = a.length - 1; - - while (i < j) { - int sum = a[i] + a[j]; - - if (sum == targetSum) { - return true; - } else if (sum < targetSum) { - i++; - } else { - j--; - } - } - - return false; - } - - public static void main(String[] args) { - int[] a = {4, -9, 0, 11, 6, -20, 1, 7}; - System.out.println(new TwoPointers_2Sum().contains2(a, -14)); - } -} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_BruteForce.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_BruteForce.java new file mode 100644 index 00000000..4ad2468f --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_BruteForce.java @@ -0,0 +1,25 @@ +package com.hellokoding.algorithm; + +import java.util.Arrays; + +public class TwoSum_BruteForce { + static boolean bruteForce(int[] a, int k) { + for (int i = 0; i < a.length ; i++) { + for (int j = i+1; j < a.length; j++) { + if (a[i] + a[j] == k) return true; + } + } + + return false; + } + + public static void main(String[] args) { + int[] a = {1, 3, 7}; + System.out.println(bruteForce(a, 8)); + System.out.println(bruteForce(a, 5)); + + int[] b = {4, -9, 0, 11, 6, -20, 1, 7}; + System.out.println(bruteForce(b, -14)); + System.out.println(bruteForce(b, -15)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_HashTable.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_HashTable.java new file mode 100644 index 00000000..7d6a819a --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_HashTable.java @@ -0,0 +1,27 @@ +package com.hellokoding.algorithm; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +public class TwoSum_HashTable { + static boolean hashTable(int[] a, int k) { + HashSet hashTable = new HashSet<>(); + for (int i = 0; i < a.length ; i++) { + if (hashTable.contains(k - a[i])) return true; + hashTable.add(a[i]); + } + + return false; + } + + public static void main(String[] args) { + int[] a = {1, 3, 7}; + System.out.println(hashTable(a, 8)); + System.out.println(hashTable(a, 5)); + + int[] b = {4, -9, 0, 11, 6, -20, 1, 7}; + System.out.println(hashTable(b, -14)); + System.out.println(hashTable(b, -15)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_TwoPointers.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_TwoPointers.java new file mode 100644 index 00000000..5333d7e4 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/TwoSum_TwoPointers.java @@ -0,0 +1,36 @@ +package com.hellokoding.algorithm; + +import java.util.Arrays; + +public class TwoSum_TwoPointers { + static boolean twoPointers(int[] a, int k) { + Arrays.sort(a); + + int i = 0; + int j = a.length - 1; + + while (i < j) { + int sum = a[i] + a[j]; + + if (sum == k) { + return true; + } else if (sum < k) { + i++; + } else { + j--; + } + } + + return false; + } + + public static void main(String[] args) { + int[] a = {1, 3, 7}; + System.out.println(twoPointers(a, 8)); + System.out.println(twoPointers(a, 5)); + + int[] b = {4, -9, 0, 11, 6, -20, 1, 7}; + System.out.println(twoPointers(b, -14)); + System.out.println(twoPointers(b, -15)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/LinkedList_Add2Numbers.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/LinkedList_Add2Numbers.java new file mode 100644 index 00000000..4c1815f8 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/LinkedList_Add2Numbers.java @@ -0,0 +1,61 @@ +package com.hellokoding.datastructure; + +public class LinkedList_Add2Numbers { + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + ListNode dummyHead = new ListNode(0); + ListNode p = l1, q = l2, curr = dummyHead; + int carry = 0; + while (p != null || q != null) { + int x = (p != null) ? p.val : 0; + int y = (q != null) ? q.val : 0; + int sum = carry + x + y; + carry = sum / 10; + curr.next = new ListNode(sum % 10); + curr = curr.next; + if (p != null) p = p.next; + if (q != null) q = q.next; + } + if (carry > 0) curr.next = new ListNode(carry); + + return dummyHead.next; + } + + public ListNode createListNode(String s) { + ListNode r = new ListNode(getValue(s.charAt(s.length()-1))); + ListNode root = r; + + for(int i=s.length()-2; i>=0; i--) { + int v = getValue(s.charAt(i)); + r.next = new ListNode(v); + r = r.next; + } + + return root; + } + + public int getValue(char c) { + return Integer.parseInt(String.valueOf(c)); + } + + public String getNumber(ListNode l) { + StringBuilder s = new StringBuilder(); + do { + s.append(l.val); + } while((l = l.next) != null); + + return s.reverse().toString(); + } + + public static void main(String[] args) { + LinkedList_Add2Numbers t = new LinkedList_Add2Numbers(); + ListNode result = t.addTwoNumbers(t.createListNode("243"), t.createListNode("64")); + //ListNode result = t.addTwoNumbers(t.createListNode("9"), t.createListNode("9999999991")); + System.out.println(t.getNumber(result)); + } +} + +class ListNode { + int val; + ListNode next; + ListNode(int x) { val = x; } + } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MinHeapByArray.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MinHeapByArray.java index a3f424b5..e5d152c8 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MinHeapByArray.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MinHeapByArray.java @@ -65,6 +65,10 @@ public int peek() { return heap[1]; } + public boolean isEmpty() { + return size == 0; + } + public static void main(String[] args) { MinHeapByArray minHeap = new MinHeapByArray(5); minHeap.push(3); diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyDoublyLinkedList.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyDoublyLinkedList.java index 881e0182..3789e74c 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyDoublyLinkedList.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyDoublyLinkedList.java @@ -55,9 +55,14 @@ public static void main(String[] args) { myLinkedList.addFirst(9); myLinkedList.traversal(); + System.out.println("Traverse all elements"); + myLinkedList.traversal(); + + System.out.println("Remove first node"); myLinkedList.removeFirst(); myLinkedList.traversal(); + System.out.println("Remove last node"); myLinkedList.removeLast(); myLinkedList.traversal(); } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyHashtable.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyHashtable.java new file mode 100644 index 00000000..9e722f73 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyHashtable.java @@ -0,0 +1,164 @@ +package com.hellokoding.datastructure; + +import java.util.Arrays; +import java.util.Objects; + +public class MyHashtable { + private static final int INITIAL_CAPACITY = 16; + private static final float LOAD_FACTOR = 0.75f; + private int size = 0; + + Entry[] entries; + + @SuppressWarnings({"rawtypes", "unchecked"}) + public MyHashtable() { + entries = (Entry[]) new Entry[INITIAL_CAPACITY]; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public MyHashtable(int capacity) { + entries = (Entry[]) new Entry[capacity]; + } + + public V put(K key, V value) { + int index = hashFunction(key); + Entry headEntry = entries[index]; + Entry currentEntry = headEntry; + + // Find key in the chain + while (Objects.nonNull(currentEntry)) { + if (Objects.equals(currentEntry.key, key)) { + // Ignore duplicate key + return currentEntry.value; + } + + currentEntry = currentEntry.next; + } + + // Add first to the chain + Entry newEntry = new Entry<>(key, value); + newEntry.next = headEntry; + entries[index] = newEntry; + + size++; + + resize(); + + return null; + } + + public V get(K key) { + int index = hashFunction(key); + Entry currentEntry = entries[index]; + + // Find key in the chain + while (Objects.nonNull(currentEntry)) { + if (Objects.equals(currentEntry.key, key)) { + return currentEntry.value; + } + + currentEntry = currentEntry.next; + } + + return null; + } + + public V remove(K key) { + int index = hashFunction(key); + Entry currentEntry = entries[index]; + Entry previousEntry = null; + + // Find key in the chain + while (Objects.nonNull(currentEntry)) { + if (Objects.equals(currentEntry.key, key)) { + break; + } + + previousEntry = currentEntry; + currentEntry = currentEntry.next; + } + + if (Objects.isNull(currentEntry)) { + return null; + } + + // Remove + if (Objects.isNull(previousEntry)) { + entries[index] = currentEntry.next; + } else { + previousEntry.next = currentEntry.next; + } + + size--; + + return currentEntry.value; + } + + public int size() { + return size; + } + + private void resize() { + if (size <= LOAD_FACTOR * entries.length) return; + + Entry[] currentEntries = entries; + entries = (Entry[]) new Entry[entries.length*2] ; + + rehash(currentEntries); + } + + private void rehash(Entry[] entries) { + size = 0; + + for (Entry entry : entries) { + while (Objects.nonNull(entry)) { + put(entry.key, entry.value); + entry = entry.next; + } + } + } + + private int hashFunction(K key) { + int hashCode = key.hashCode(); + int index = hashCode % entries.length; + + return index; + } + + public static class Entry { + K key; + V value; + + Entry next; + + Entry(K key, V value) { + this.key = key; + this.value = value; + } + } + + public static void main(String[] args) { + MyHashtable myHashtable = new MyHashtable<>(2); + myHashtable.put("k1", 1); + myHashtable.put("k2", 2); + myHashtable.put("k2", 2); + myHashtable.put("k3", 3); + + Arrays.stream(myHashtable.entries) + .forEach(e -> { + if (Objects.nonNull(e)) { + System.out.printf("%s=%d%s", e.key, e.value, System.lineSeparator()); + } + }); + + System.out.println(myHashtable.size()); + + System.out.println(myHashtable.get("k2")); + + System.out.println(myHashtable.remove("k1")); + + System.out.println(myHashtable.get("k1")); + + System.out.println(myHashtable.size()); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyLinkedList.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyLinkedList.java index b477f5ac..d0824fe2 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyLinkedList.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/MyLinkedList.java @@ -66,11 +66,15 @@ public static void main(String[] args) { myLinkedList.addLast(2); myLinkedList.addLast(6); myLinkedList.addFirst(9); + + System.out.println("Traverse all elements"); myLinkedList.traversal(); + System.out.println("Remove first node"); myLinkedList.removeFirst(); myLinkedList.traversal(); + System.out.println("Remove last node"); myLinkedList.removeLast(); myLinkedList.traversal(); } diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/UndirectedGraphByAdjacencyList.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/UndirectedGraphByAdjacencyList.java deleted file mode 100644 index adb79ee6..00000000 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/UndirectedGraphByAdjacencyList.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.hellokoding.datastructure; - -import java.util.ArrayList; -import java.util.List; - -public class UndirectedGraphByAdjacencyList { - private int V; - private List[] adjacencyList; - - public UndirectedGraphByAdjacencyList(int V) { - this.V = V; - - adjacencyList = new ArrayList[V]; - for (int i = 0; i < V; i++) { - adjacencyList[i] = new ArrayList<>(); - } - } - - public int getV() { - return V; - } - - public List getAdjacencyList(int vertex) { - return adjacencyList[vertex]; - } - - private void print() { - for (int i = 0; i < V; i++) { - System.out.printf("Adjacency list of vertex %d is %s %s", i, adjacencyList[i], System.lineSeparator()); - } - } - - public void addEdge(int source, int dest) { - adjacencyList[source].add(dest); - adjacencyList[dest].add(source); - } - - public static void main(String[] args) { - UndirectedGraphByAdjacencyList graph = new UndirectedGraphByAdjacencyList(3); - graph.addEdge(0, 1); - graph.addEdge(1, 2); - graph.addEdge(2, 0); - graph.print(); - } -} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/BFSByIterative.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/BFSByIterative.java new file mode 100644 index 00000000..017ff8bf --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/BFSByIterative.java @@ -0,0 +1,36 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class BFSByIterative { + public static void bfsByIterative(GraphUndirectedByAdjacencyList g, int v) { + boolean[] visited = new boolean[g.getV()]; + Deque queue = new ArrayDeque<>(); + queue.offer(v); + + while (!queue.isEmpty()) { + v = queue.poll(); + + if (!visited[v]) { + visited[v] = true; + System.out.printf("%d ", v); + + for (Integer w : g.getAdjacencyList().get(v)) { + queue.offer(w); + } + } + } + } + + public static void main(String[] args) { + GraphUndirectedByAdjacencyList g = new GraphUndirectedByAdjacencyList(5); + g.addEdge(0, 1); + g.addEdge(1, 2); + g.addEdge(2, 0); + g.addEdge(1, 3); + g.addEdge(2, 4); + + bfsByIterative(g, 0); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/BFSByIterativeWithColor.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/BFSByIterativeWithColor.java new file mode 100644 index 00000000..f55ac2f3 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/BFSByIterativeWithColor.java @@ -0,0 +1,40 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class BFSByIterativeWithColor { + static final int WHITE = 0, GRAY = 1, BLACK = 2; + + public static void bfsByIterativeWithColor(GraphUndirectedByAdjacencyList g, int v) { + int[] color = new int[g.getV()]; + Deque queue = new ArrayDeque<>(); + queue.offer(v); + + while (!queue.isEmpty()) { + v = queue.poll(); + + if (color[v] == WHITE) { + color[v] = GRAY; + System.out.printf("%d ", v); + + for (Integer w : g.getAdjacencyList().get(v)) { + queue.offer(w); + } + + color[v] = BLACK; + } + } + } + + public static void main(String[] args) { + GraphUndirectedByAdjacencyList g = new GraphUndirectedByAdjacencyList(5); + g.addEdge(0, 1); + g.addEdge(1, 2); + g.addEdge(2, 0); + g.addEdge(1, 3); + g.addEdge(2, 4); + + bfsByIterativeWithColor(g, 0); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByIterative.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByIterative.java new file mode 100644 index 00000000..5db2185a --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByIterative.java @@ -0,0 +1,37 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class DFSByIterative { + static void dfsByIterative(GraphUndirectedByAdjacencyList g, int v) { + boolean[] visited = new boolean[g.getV()]; + + Deque stack = new ArrayDeque<>(); + stack.push(v); + + while (!stack.isEmpty()) { + v = stack.pop(); + + if (!visited[v]) { + visited[v] = true; + System.out.printf("%d ", v); + + for(Integer w : g.getAdjacencyList().get(v)) { + stack.push(w); + } + } + } + } + + public static void main(String[] args) { + GraphUndirectedByAdjacencyList g = new GraphUndirectedByAdjacencyList(5); + g.addEdge(0, 2); + g.addEdge(1, 2); + g.addEdge(1, 0); + g.addEdge(1, 3); + g.addEdge(2, 4); + + dfsByIterative(g, 0); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByIterativeWithColor.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByIterativeWithColor.java new file mode 100644 index 00000000..d7aa7f52 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByIterativeWithColor.java @@ -0,0 +1,41 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class DFSByIterativeWithColor { + static final int WHITE = 0, GRAY = 1, BLACK = 2; + + static void dfsByIterativeWithColor(GraphUndirectedByAdjacencyList g, int v) { + int[] color = new int[g.getV()]; + + Deque stack = new ArrayDeque<>(); + stack.push(v); + + while (!stack.isEmpty()) { + v = stack.pop(); + + if (color[v] == WHITE) { + color[v] = GRAY; + System.out.printf("%d ", v); + + for(Integer w : g.getAdjacencyList().get(v)) { + stack.push(w); + } + + color[v] = BLACK; + } + } + } + + public static void main(String[] args) { + GraphUndirectedByAdjacencyList g = new GraphUndirectedByAdjacencyList(5); + g.addEdge(0, 2); + g.addEdge(1, 2); + g.addEdge(1, 0); + g.addEdge(1, 3); + g.addEdge(2, 4); + + dfsByIterativeWithColor(g, 0); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByRecursive.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByRecursive.java new file mode 100644 index 00000000..2510022c --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByRecursive.java @@ -0,0 +1,36 @@ +package com.hellokoding.datastructure.graph; + +public class DFSByRecursive { + + static void dfs(GraphUndirectedByAdjacencyList g, int v, boolean[] visited) { + visited[v] = true; + System.out.printf("%d ", v); + + for (Integer w : g.getAdjacencyList().get(v)) { + if (!visited[w]) { + dfs(g, w, visited); + } + } + } + + static void traversal(GraphUndirectedByAdjacencyList g) { + boolean[] visited = new boolean[g.getV()]; + + for (int i = 0; i < g.getV(); i++) { + if (!visited[i]) { + dfs(g, i, visited); + } + } + } + + public static void main(String[] args) { + GraphUndirectedByAdjacencyList g = new GraphUndirectedByAdjacencyList(5); + g.addEdge(0, 2); + g.addEdge(1, 2); + g.addEdge(1, 0); + g.addEdge(1, 3); + g.addEdge(2, 4); + + traversal(g); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByRecursiveWithColor.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByRecursiveWithColor.java new file mode 100644 index 00000000..14e4792d --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DFSByRecursiveWithColor.java @@ -0,0 +1,38 @@ +package com.hellokoding.datastructure.graph; + +public class DFSByRecursiveWithColor { + static final int WHITE = 0, GRAY = 1, BLACK = 2; + + static void dfsByRecursiveWithColor(GraphUndirectedByAdjacencyList g, int v, int[] color) { + color[v] = GRAY; + System.out.printf("%d ", v); + + for (Integer w : g.getAdjacencyList().get(v)) { + if (color[w] == WHITE) { + dfsByRecursiveWithColor(g, w, color); + } + } + color[v] = BLACK; + } + + static void traversal(GraphUndirectedByAdjacencyList g) { + int[] color = new int[g.getV()]; + + for (int i = 0; i < g.getV(); i++) { + if (color[i] == WHITE) { + dfsByRecursiveWithColor(g, i, color); + } + } + } + + public static void main(String[] args) { + GraphUndirectedByAdjacencyList g = new GraphUndirectedByAdjacencyList(5); + g.addEdge(0, 1); + g.addEdge(0, 2); + g.addEdge(1, 2); + g.addEdge(1, 3); + g.addEdge(2, 4); + + traversal(g); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSIterative.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSIterative.java new file mode 100644 index 00000000..30f7edb3 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSIterative.java @@ -0,0 +1,56 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class DetectCycleDirectedByDFSIterative { + static boolean dfsByIterative(GraphDirectedByAdjacencyList g) { + boolean[] visited = new boolean[g.getV()]; + boolean[] onStack = new boolean[g.getV()]; + Deque stack = new ArrayDeque<>(); + + for (int i = 0; i < g.getV(); i++) { + if (visited[i]) continue; + stack.push(i); + + while (!stack.isEmpty()) { + int v = stack.peek(); + + if (!visited[v]) { + visited[v] = true; + onStack[v] = true; + } else { + onStack[v] = false; + stack.pop(); + } + + for (Integer w : g.getAdjacencyList().get(v)) { + if (!visited[w]) { + stack.push(w); + } else if (onStack[w]) { + return true; + } + } + } + } + + return false; + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList g1 = new GraphDirectedByAdjacencyList(5); + g1.addEdge(0, 1); + g1.addEdge(1, 2); + g1.addEdge(2, 0); + g1.addEdge(1, 3); + g1.addEdge(2, 4); + System.out.println(dfsByIterative(g1)); + + GraphDirectedByAdjacencyList g2 = new GraphDirectedByAdjacencyList(4); + g2.addEdge(0, 1); + g2.addEdge(2, 0); + g2.addEdge(2, 1); + g2.addEdge(3, 2); + System.out.println(dfsByIterative(g2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSIterativeColor.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSIterativeColor.java new file mode 100644 index 00000000..8803ed61 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSIterativeColor.java @@ -0,0 +1,56 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class DetectCycleDirectedByDFSIterativeColor { + static final int WHITE = 0, GRAY = 1, BLACK = 2; + + static boolean dfsByIterativeWithColor(GraphDirectedByAdjacencyList g) { + int[] color = new int[g.getV()]; + Deque stack = new ArrayDeque<>(g.getV()); + + for (int i = 0; i < g.getV(); i++) { + if (color[i] != WHITE) continue; + stack.push(i); + + while (!stack.isEmpty()) { + int v = stack.peek(); + + if (color[v] == WHITE) { + color[v] = GRAY; + } else { + color[v] = BLACK; + stack.pop(); + } + + for (int w : g.getAdjacencyList().get(v)) { + if (color[w] == GRAY) { + return true; + } else if (color[w] == WHITE) { + stack.push(w); + } + } + } + } + + return false; + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList g1 = new GraphDirectedByAdjacencyList(5); + g1.addEdge(0, 1); + g1.addEdge(1, 2); + g1.addEdge(2, 0); + g1.addEdge(1, 3); + g1.addEdge(2, 4); + System.out.println(dfsByIterativeWithColor(g1)); + + GraphDirectedByAdjacencyList g2 = new GraphDirectedByAdjacencyList(4); + g2.addEdge(0, 1); + g2.addEdge(2, 0); + g2.addEdge(2, 1); + g2.addEdge(3, 2); + System.out.println(dfsByIterativeWithColor(g2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSRecursive.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSRecursive.java new file mode 100644 index 00000000..4316959f --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSRecursive.java @@ -0,0 +1,53 @@ +package com.hellokoding.datastructure.graph; + +import java.util.List; + +public class DetectCycleDirectedByDFSRecursive { + static boolean dfsByRecursive(GraphDirectedByAdjacencyList g, int v, boolean[] visited, boolean[] onStack) { + if (onStack[v]) return true; + if (visited[v]) return false; + + visited[v] = true; + onStack[v] = true; + + List children = g.getAdjacencyList().get(v); + for (Integer c: children) { + if (dfsByRecursive(g, c, visited, onStack)) { + return true; + } + } + + onStack[v] = false; + return false; + } + + static boolean hasCycle(GraphDirectedByAdjacencyList g) { + boolean[] visited = new boolean[g.getV()]; + boolean[] onStack = new boolean[g.getV()]; + + for (int i = 0; i < g.getV(); i++) { + if (dfsByRecursive(g, i, visited, onStack)) { + return true; + } + } + + return false; + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList g1 = new GraphDirectedByAdjacencyList(5); + g1.addEdge(0, 1); + g1.addEdge(1, 2); + g1.addEdge(2, 0); + g1.addEdge(1, 3); + g1.addEdge(2, 4); + System.out.println(hasCycle(g1)); + + GraphDirectedByAdjacencyList g2 = new GraphDirectedByAdjacencyList(4); + g2.addEdge(0, 1); + g2.addEdge(2, 0); + g2.addEdge(2, 1); + g2.addEdge(3, 2); + System.out.println(hasCycle(g2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSRecursiveColor.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSRecursiveColor.java new file mode 100644 index 00000000..5ca44f89 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleDirectedByDFSRecursiveColor.java @@ -0,0 +1,51 @@ +package com.hellokoding.datastructure.graph; + +public class DetectCycleDirectedByDFSRecursiveColor { + static final int WHITE = 0, GRAY = 1, BLACK = 2; + + static boolean dfsByRecursiveWithColor(GraphDirectedByAdjacencyList g, int v, int[] color) { + color[v] = GRAY; + + for (Integer w : g.getAdjacencyList().get(v)) { + if (color[w] == GRAY) { + return true; + } + + if (color[w] == WHITE && dfsByRecursiveWithColor(g, w, color)) { + return true; + } + } + + color[v] = BLACK; + return false; + } + + static boolean hasCycle(GraphDirectedByAdjacencyList g) { + int[] color = new int[g.getV()]; + + for (int i = 0; i < g.getV(); i++) { + if (color[i] == WHITE && dfsByRecursiveWithColor(g, i, color)) { + return true; + } + } + + return false; + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList g1 = new GraphDirectedByAdjacencyList(5); + g1.addEdge(0, 1); + g1.addEdge(1, 2); + g1.addEdge(2, 0); + g1.addEdge(1, 3); + g1.addEdge(2, 4); + System.out.println(hasCycle(g1)); + + GraphDirectedByAdjacencyList g2 = new GraphDirectedByAdjacencyList(4); + g2.addEdge(0, 1); + g2.addEdge(2, 0); + g2.addEdge(2, 1); + g2.addEdge(3, 2); + System.out.println(hasCycle(g2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleUndirected.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleUndirected.java new file mode 100644 index 00000000..54aebed4 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/DetectCycleUndirected.java @@ -0,0 +1,44 @@ +package com.hellokoding.datastructure.graph; + +import java.util.List; + +public class DetectCycleUndirected extends GraphUndirectedByAdjacencyList { + public DetectCycleUndirected(int V) { + super(V); + } + + private boolean dfs(int v, boolean[] visited, int parent) { + visited[v] = true; + + List children = getAdjacencyList().get(v); + for (Integer c: children) { + if (!visited[c] && dfs(c, visited, v)) { + return true; + } else if (v != parent) { + return true; + } + } + + return false; + } + + public boolean hasCycle() { + boolean[] visited = new boolean[getV()]; + + for (int i = 0; i < getV(); i++) { + if (!visited[i] && dfs(i, visited, -1)) { + return true; + } + } + + return false; + } + + public static void main(String[] args) { + DetectCycleUndirected graph = new DetectCycleUndirected(3); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + graph.addEdge(2, 0); + System.out.println(graph.hasCycle()); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphDirectedByAdjacencyList.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphDirectedByAdjacencyList.java new file mode 100644 index 00000000..a8e29309 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphDirectedByAdjacencyList.java @@ -0,0 +1,45 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayList; +import java.util.List; + +public class GraphDirectedByAdjacencyList { + private int V; + private List> adjacencyList; + + public GraphDirectedByAdjacencyList(int V) { + this.V = V; + + adjacencyList = new ArrayList<>(V); + for (int i = 0; i < V; i++) { + adjacencyList.add(new ArrayList<>()); + } + } + + public Integer getV() { + return this.V; + } + + public List> getAdjacencyList() { + return this.adjacencyList; + } + + public void addEdge(int source, int dest) { + adjacencyList.get(source).add(dest); + } + + public void printAdjacencyList() { + for (int i = 0; i < V; i++) { + System.out.printf("Adjacency list of vertex %d is %s %s", i, + adjacencyList.get(i), System.lineSeparator()); + } + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList graph = new GraphDirectedByAdjacencyList(3); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + graph.addEdge(2, 0); + graph.printAdjacencyList(); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphUndirectedByAdjacencyList.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphUndirectedByAdjacencyList.java new file mode 100644 index 00000000..ae72b406 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphUndirectedByAdjacencyList.java @@ -0,0 +1,46 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayList; +import java.util.List; + +public class GraphUndirectedByAdjacencyList { + private int V; + private List> adjacencyList; + + public GraphUndirectedByAdjacencyList(int V) { + this.V = V; + + adjacencyList = new ArrayList<>(V); + for (int i = 0; i < V; i++) { + adjacencyList.add(new ArrayList<>()); + } + } + + public Integer getV() { + return this.V; + } + + public List> getAdjacencyList() { + return this.adjacencyList; + } + + public void addEdge(int source, int dest) { + adjacencyList.get(source).add(dest); + adjacencyList.get(dest).add(source); + } + + public void printAdjacencyList() { + for (int i = 0; i < V; i++) { + System.out.printf("Adjacency list of vertex %d is %s %s", i, + adjacencyList.get(i), System.lineSeparator()); + } + } + + public static void main(String[] args) { + GraphUndirectedByAdjacencyList graph = new GraphUndirectedByAdjacencyList(3); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + graph.addEdge(2, 0); + graph.printAdjacencyList(); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphWeightedByAdjacencyList.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphWeightedByAdjacencyList.java new file mode 100644 index 00000000..d424d14b --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/GraphWeightedByAdjacencyList.java @@ -0,0 +1,63 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayList; +import java.util.List; + +public class GraphWeightedByAdjacencyList { + private int V; + private List> adjacencyList; + + public GraphWeightedByAdjacencyList(int V) { + this.V = V; + + adjacencyList = new ArrayList<>(V); + for (int i = 0; i < V; i++) { + adjacencyList.add(new ArrayList<>()); + } + } + + public Integer getV() { + return this.V; + } + + public List> getAdjacencyList() { + return this.adjacencyList; + } + + public void addEdge(int source, int dest, int weight) { + adjacencyList.get(source).add(new WeightedVertex(dest, weight)); + } + + public void printAdjacencyList() { + for (int i = 0; i < V; i++) { + System.out.printf("Adjacency list of vertex %d is %s %s", i, + adjacencyList.get(i), System.lineSeparator()); + } + } + + static class WeightedVertex { + final Integer vertex, weight; + + public WeightedVertex(int vertex, int weight) { + this.vertex = vertex; + this.weight = weight; + } + + public int getWeight() { + return this.weight; + } + + public String toString() { + return String.format("%d (weight %d)", vertex, weight); + } + } + + public static void main(String[] args) { + GraphWeightedByAdjacencyList g = new GraphWeightedByAdjacencyList(4); + g.addEdge(0, 1, 19); + g.addEdge(2, 0, 15); + g.addEdge(2, 1, 17); + g.addEdge(3, 2, 12); + g.printAdjacencyList(); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByBFS.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByBFS.java new file mode 100644 index 00000000..216633fa --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByBFS.java @@ -0,0 +1,73 @@ +package com.hellokoding.datastructure.graph; + +import java.util.*; + +public class ShortestPathByBFS { + static final int INFINITE = Integer.MAX_VALUE; + static final int UNDEFINED = -1; + + public static Object[] shortestPathByBFS(GraphDirectedByAdjacencyList g, int source) { + int[] distances = new int[g.getV()]; + int[] predecessors = new int[g.getV()]; + + for (int v = 0; v < g.getV(); v++) { + distances[v] = INFINITE; + predecessors[v] = UNDEFINED; + } + distances[source] = 0; + + boolean[] visited = new boolean[g.getV()]; + visited[source] = true; + + Queue queue = new ArrayDeque<>(); + queue.offer(source); + + while (!queue.isEmpty()) { + int u = queue.poll(); + + for (int v : g.getAdjacencyList().get(u)) { + if (!visited[v]) { + visited[v] = true; + distances[v] = distances[u] + 1; + predecessors[v] = u; + queue.offer(v); + } + } + } + + return new Object[]{distances, predecessors}; + } + + static void printResult(Object[] paths) { + int[] distancesFromSource = (int[]) paths[0]; + int[] predecessorsOfSource = (int[]) paths[1]; + + System.out.println("Vertex\tDistance from source\tRoute from source"); + for (int v = 0; v < distancesFromSource.length; v++) { + if (distancesFromSource[v] == INFINITE) { + System.out.printf("%-8s%-24s%s%s", v, "N/A", "N/A", System.lineSeparator()); + continue; + } + + Deque route = new ArrayDeque<>(); + route.push(v); + int u = predecessorsOfSource[v]; + while (u >= 0) { + route.push(u); + u = predecessorsOfSource[u]; + } + + System.out.printf("%-8d%-24d%s%s", v, distancesFromSource[v], route.toString(), System.lineSeparator()); + } + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList g = new GraphDirectedByAdjacencyList(4); + g.addEdge(0, 1); + g.addEdge(2, 0); + g.addEdge(2, 1); + g.addEdge(3, 2); + printResult(shortestPathByBFS(g, 3)); + printResult(shortestPathByBFS(g, 2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByBellmanFord.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByBellmanFord.java new file mode 100644 index 00000000..197b46eb --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByBellmanFord.java @@ -0,0 +1,86 @@ +package com.hellokoding.datastructure.graph; + +import java.util.*; + +public class ShortestPathByBellmanFord { + static final int INFINITE = Integer.MAX_VALUE; + static final int UNDEFINED = -1; + + static Object[] shortestPathByBellmanFord(GraphWeightedByAdjacencyList g, int source) { + int[] distances = new int[g.getV()]; + int[] predecessors = new int[g.getV()]; + + for (int v = 0; v < g.getV(); v++) { + distances[v] = INFINITE; + predecessors[v] = UNDEFINED; + } + distances[source] = 0; + + for (int i = 1; i < g.getV(); i++) { + for (int u = 0; u < g.getV(); u++) { + for (GraphWeightedByAdjacencyList.WeightedVertex v : g.getAdjacencyList().get(u)) { + if (distances[u] == INFINITE) continue; + int alt = distances[u] + v.weight; + if (alt < distances[v.vertex]) { + distances[v.vertex] = alt; + predecessors[v.vertex] = u; + } + } + } + } + + for (int u = 0; u < g.getV(); u++) { + for (GraphWeightedByAdjacencyList.WeightedVertex v : g.getAdjacencyList().get(u)) { + if (distances[u] == INFINITE) continue; + int alt = distances[u] + v.weight; + if (alt < distances[v.vertex]) { + System.out.println("The input graph contains a negative-weight cycle"); + return new Object[]{}; + } + } + } + + return new Object[]{distances, predecessors}; + } + + static void printResult(Object[] paths) { + if (paths.length == 0) return; + + int[] distances = (int[]) paths[0]; + int[] predecessors = (int[]) paths[1]; + + System.out.println("Vertex\tDistance from source\tRoute from source"); + for (int v = 0; v < distances.length; v++) { + if (distances[v] == INFINITE) { + System.out.printf("%-8s%-24s%s%s", v, "N/A", "N/A", System.lineSeparator()); + continue; + } + + Deque route = new ArrayDeque<>(); + route.push(v); + int u = predecessors[v]; + while (u >= 0) { + route.push(u); + u = predecessors[u]; + } + + System.out.printf("%-8d%-24d%s%s", v, distances[v], route.toString(), System.lineSeparator()); + } + } + + public static void main(String[] args) { + GraphWeightedByAdjacencyList g1 = new GraphWeightedByAdjacencyList(4); + g1.addEdge(0, 1, -2); + g1.addEdge(2, 0, 1); + g1.addEdge(2, 1, 3); + g1.addEdge(3, 2, 4); + printResult(shortestPathByBellmanFord(g1, 3)); + + GraphWeightedByAdjacencyList g2 = new GraphWeightedByAdjacencyList(4); + g2.addEdge(0, 1, -5); + g2.addEdge(2, 0, 1); + g2.addEdge(1, 2, 3); + g2.addEdge(3, 2, 4); + printResult(shortestPathByBellmanFord(g2, 3)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByDijkstra.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByDijkstra.java new file mode 100644 index 00000000..5342f39e --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByDijkstra.java @@ -0,0 +1,88 @@ +package com.hellokoding.datastructure.graph; + +import java.util.*; + +import static com.hellokoding.datastructure.graph.GraphWeightedByAdjacencyList.WeightedVertex; + +public class ShortestPathByDijkstra { + static final int INFINITE = Integer.MAX_VALUE; + static final int UNDEFINED = -1; + + static int minVertex(Queue queue, int[] distance) { + int minDistance = INFINITE; + int minVertex = UNDEFINED; + + for (Integer v : queue) { + if (minDistance > distance[v]) { + minDistance = distance[v]; + minVertex = v; + } + } + + return minVertex; + } + + static Object[] shortestPathByDijkstra(GraphWeightedByAdjacencyList g, int source) { + int[] distances = new int[g.getV()]; + int[] predecessors = new int[g.getV()]; + Queue queue = new ArrayDeque<>(); + + for (int v = 0; v < g.getV(); v++) { + distances[v] = INFINITE; + predecessors[v] = UNDEFINED; + queue.offer(v); + } + distances[source] = 0; + + while (!queue.isEmpty()) { + int u = minVertex(queue, distances); + if (u == UNDEFINED) break; + queue.remove(u); + + for (WeightedVertex v : g.getAdjacencyList().get(u)) { + if (distances[u] == INFINITE) continue; + + int alt = distances[u] + v.weight; + if (alt < distances[v.vertex]) { + distances[v.vertex] = alt; + predecessors[v.vertex] = u; + } + } + } + + return new Object[]{distances, predecessors}; + } + + static void printResult(Object[] paths) { + int[] distances = (int[]) paths[0]; + int[] predecessors = (int[]) paths[1]; + + System.out.println("Vertex\tDistance from source\tRoute from source"); + for (int v = 0; v < distances.length; v++) { + if (distances[v] == INFINITE) { + System.out.printf("%-8s%-24s%s%s", v, "N/A", "N/A", System.lineSeparator()); + continue; + } + + Deque route = new ArrayDeque<>(); + route.push(v); + int u = predecessors[v]; + while (u >= 0) { + route.push(u); + u = predecessors[u]; + } + + System.out.printf("%-8d%-24d%s%s", v, distances[v], route.toString(), System.lineSeparator()); + } + } + + public static void main(String[] args) { + GraphWeightedByAdjacencyList g = new GraphWeightedByAdjacencyList(4); + g.addEdge(0, 1, 2); + g.addEdge(2, 0, 1); + g.addEdge(2, 1, 5); + g.addEdge(3, 2, 4); + printResult(shortestPathByDijkstra(g, 3)); + printResult(shortestPathByDijkstra(g, 2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByDijkstraWithMinHeap.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByDijkstraWithMinHeap.java new file mode 100644 index 00000000..bceffb4b --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByDijkstraWithMinHeap.java @@ -0,0 +1,72 @@ +package com.hellokoding.datastructure.graph; + +import java.util.*; + +import static com.hellokoding.datastructure.graph.GraphWeightedByAdjacencyList.WeightedVertex; + +public class ShortestPathByDijkstraWithMinHeap { + static final int INFINITE = Integer.MAX_VALUE; + static final int UNDEFINED = -1; + + static Object[] shortestPathByDijkstra(GraphWeightedByAdjacencyList g, int source) { + int[] distances = new int[g.getV()]; + int[] predecessors = new int[g.getV()]; + PriorityQueue minHeap = new PriorityQueue<>(g.getV(), Comparator.comparingInt(WeightedVertex::getWeight)); + + for (int v = 0; v < g.getV(); v++) { + distances[v] = INFINITE; + predecessors[v] = UNDEFINED; + minHeap.add(new WeightedVertex(v, distances[v])); + } + distances[source] = 0; + + while (!minHeap.isEmpty()) { + WeightedVertex u = minHeap.poll(); + + for (WeightedVertex v : g.getAdjacencyList().get(u.vertex)) { + if (distances[u.vertex] == INFINITE) continue; + + int alt = distances[u.vertex] + v.weight; + if (alt < distances[v.vertex]) { + distances[v.vertex] = alt; + predecessors[v.vertex] = u.vertex; + } + } + } + + return new Object[]{distances, predecessors}; + } + + static void printResult(Object[] paths) { + int[] distances = (int[]) paths[0]; + int[] predecessors = (int[]) paths[1]; + + System.out.println("Vertex\tDistance from source\tRoute from source"); + for (int v = 0; v < distances.length; v++) { + if (distances[v] == INFINITE) { + System.out.printf("%-8s%-24s%s%s", v, "N/A", "N/A", System.lineSeparator()); + continue; + } + + Deque route = new ArrayDeque<>(); + route.push(v); + int u = predecessors[v]; + while (u >= 0) { + route.push(u); + u = predecessors[u]; + } + + System.out.printf("%-8d%-24d%s%s", v, distances[v], route.toString(), System.lineSeparator()); + } + } + + public static void main(String[] args) { + GraphWeightedByAdjacencyList g = new GraphWeightedByAdjacencyList(4); + g.addEdge(0, 1, 2); + g.addEdge(2, 0, 1); + g.addEdge(2, 1, 5); + g.addEdge(3, 2, 4); + printResult(shortestPathByDijkstra(g, 3)); + printResult(shortestPathByDijkstra(g, 2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByFloydWarshall.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByFloydWarshall.java new file mode 100644 index 00000000..172eb2d2 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/ShortestPathByFloydWarshall.java @@ -0,0 +1,57 @@ +package com.hellokoding.datastructure.graph; + +import java.util.Arrays; + +public class ShortestPathByFloydWarshall { + static final int I = Integer.MAX_VALUE; + + public static int[][] shortestPathByBFSFloydWarshall(int[][] g) { + int V = g.length; + int[][] distances = Arrays.copyOf(g, V); + + for (int k = 0; k < V; k++) { + for (int i = 0; i < V; i++) { + for (int j = 0; j < V; j++) { + if (distances[i][k] == I || distances[k][j] == I) continue; + distances[i][j] = Math.min(distances[i][j], distances[i][k] + distances[k][j]); + } + + if (distances[i][i] < 0) { + System.out.println("The input graph contains a negative-weight cycle"); + return new int[][]{}; + } + } + } + + return distances; + } + + static void printResult(int[][] distances) { + int V = distances.length; + for (int i = 0; i < V; i++) { + for (int j = 0; j < V; j++) { + System.out.printf("%-4s", distances[i][j] == I ? "I" : distances[i][j]); + } + System.out.println(); + } + System.out.println(); + } + + public static void main(String[] args) { + int[][] g1 = new int[][] { + {0, -2, I, I}, + {I, 0, I, I}, + {1, 3, 0, I}, + {I, I, 4, 0} + }; + printResult(shortestPathByBFSFloydWarshall(g1)); + + int[][] g2 = new int[][] { + {0, -5, I, I}, + {I, 0, 3, I}, + {1, I, 0, I}, + {I, I, 4, 0} + }; + printResult(shortestPathByBFSFloydWarshall(g2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByDFSIterativeColor.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByDFSIterativeColor.java new file mode 100644 index 00000000..da35081d --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByDFSIterativeColor.java @@ -0,0 +1,69 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +public class TopologicalSortByDFSIterativeColor { + static final int WHITE = 0, GRAY = 1, BLACK = 2; + + static List topologicalSort(GraphDirectedByAdjacencyList g) { + int[] color = new int[g.getV()]; + Deque stack = new ArrayDeque<>(g.getV()); + Deque sorted = new ArrayDeque<>(g.getV()); + + for (int i = 0; i < g.getV(); i++) { + if (color[i] != WHITE) continue; + stack.push(i); + + while (!stack.isEmpty()) { + int v = stack.peek(); + + if (color[v] == WHITE) { + color[v] = GRAY; + } else { + color[v] = BLACK; + sorted.push(stack.pop()); + } + + for (int w : g.getAdjacencyList().get(v)) { + if (color[w] == GRAY) { + // Found a cycle + return new ArrayList<>(); + } else if (color[w] == WHITE) { + stack.push(w); + } + } + } + } + + return new ArrayList<>(sorted); + } + + static void printResult(List sorted) { + if (sorted.isEmpty()) { + System.out.println("There are no topological orderings as the input graph is cyclic"); + } else { + sorted.forEach((x) -> System.out.printf("%d ", x)); + System.out.println(); + } + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList g1 = new GraphDirectedByAdjacencyList(5); + g1.addEdge(0, 1); + g1.addEdge(1, 2); + g1.addEdge(2, 0); + g1.addEdge(1, 3); + g1.addEdge(2, 4); + printResult(topologicalSort(g1)); + + GraphDirectedByAdjacencyList g2 = new GraphDirectedByAdjacencyList(4); + g2.addEdge(0, 1); + g2.addEdge(2, 0); + g2.addEdge(2, 1); + g2.addEdge(3, 2); + printResult(topologicalSort(g2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByDFSRecursiveColor.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByDFSRecursiveColor.java new file mode 100644 index 00000000..b5abb50a --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByDFSRecursiveColor.java @@ -0,0 +1,72 @@ +package com.hellokoding.datastructure.graph; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +public class TopologicalSortByDFSRecursiveColor { + static final int WHITE = 0, GRAY = 1, BLACK = 2; + static boolean hasCycle = false; + + static void topologicalSortByDFSRecursive(GraphDirectedByAdjacencyList g, int v, int[] color, Deque stack) { + if (hasCycle) { + return; + } + + color[v] = GRAY; + + for (Integer w : g.getAdjacencyList().get(v)) { + if (color[w] == GRAY) { + hasCycle = true; + } + + if (color[w] == WHITE) { + topologicalSortByDFSRecursive(g, w, color, stack); + } + } + + color[v] = BLACK; + stack.push(v); + } + + static List topologicalSort(GraphDirectedByAdjacencyList g) { + int[] color = new int[g.getV()]; + Deque stack = new ArrayDeque<>(); + hasCycle = false; + + for (int i = 0; i < g.getV(); i++) { + if (color[i] == WHITE) { + topologicalSortByDFSRecursive(g, i, color, stack); + } + } + + return hasCycle ? new ArrayList<>() : new ArrayList<>(stack); + } + + static void printResult(List sorted) { + if (sorted.isEmpty()) { + System.out.println("There are no topological orderings as the input graph is cyclic"); + } else { + sorted.forEach((x) -> System.out.printf("%d ", x)); + System.out.println(); + } + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList g1 = new GraphDirectedByAdjacencyList(5); + g1.addEdge(0, 1); + g1.addEdge(1, 2); + g1.addEdge(2, 0); + g1.addEdge(1, 3); + g1.addEdge(2, 4); + printResult(topologicalSort(g1)); + + GraphDirectedByAdjacencyList g2 = new GraphDirectedByAdjacencyList(4); + g2.addEdge(0, 1); + g2.addEdge(2, 0); + g2.addEdge(2, 1); + g2.addEdge(3, 2); + printResult(topologicalSort(g2)); + } +} diff --git a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByInDegree.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByInDegree.java new file mode 100644 index 00000000..9f99ac37 --- /dev/null +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/datastructure/graph/TopologicalSortByInDegree.java @@ -0,0 +1,64 @@ +package com.hellokoding.datastructure.graph; + +import java.util.*; + +public class TopologicalSortByInDegree { + static List topologicalSortByInDegree(GraphDirectedByAdjacencyList g) { + List sorted = new ArrayList<>(); + + int[] inDegree = new int[g.getV()]; + for (int v = 0; v < g.getV(); v++) { + for(int w : g.getAdjacencyList().get(v)) { + inDegree[w] += 1; + } + } + + Queue queue = new ArrayDeque<>(); + for (int v = 0; v < g.getV(); v++) { + if (inDegree[v] == 0) { + queue.offer(v); + } + } + + while (!queue.isEmpty()) { + int v = queue.poll(); + sorted.add(v); + + for (int w : g.getAdjacencyList().get(v)) { + inDegree[w]--; + + if (inDegree[w] == 0) { + queue.offer(w); + } + } + } + + return sorted.size() == g.getV() ? sorted : new ArrayList<>(); + } + + static void printResult(List sorted) { + if (sorted.isEmpty()) { + System.out.println("There are no topological orderings as the input graph is cyclic"); + } else { + sorted.forEach((x) -> System.out.printf("%d ", x)); + System.out.println(); + } + } + + public static void main(String[] args) { + GraphDirectedByAdjacencyList g1 = new GraphDirectedByAdjacencyList(5); + g1.addEdge(0, 1); + g1.addEdge(1, 2); + g1.addEdge(2, 0); + g1.addEdge(1, 3); + g1.addEdge(2, 4); + printResult(topologicalSortByInDegree(g1)); + + GraphDirectedByAdjacencyList g2 = new GraphDirectedByAdjacencyList(4); + g2.addEdge(0, 1); + g2.addEdge(2, 0); + g2.addEdge(2, 1); + g2.addEdge(3, 2); + printResult(topologicalSortByInDegree(g2)); + } +} diff --git a/golang-examples/golang-core/atoi.go b/golang-examples/golang-core/atoi.go deleted file mode 100644 index c8a800d6..00000000 --- a/golang-examples/golang-core/atoi.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "fmt" - "strconv" -) - -func main() { - str := "1" - - n, err := strconv.Atoi(str) - - if err != nil { - fmt.Println(err) - } else { - fmt.Println(n) - } -} diff --git a/golang-examples/golang-core/channel.go b/golang-examples/golang-core/channel.go deleted file mode 100644 index b23269a9..00000000 --- a/golang-examples/golang-core/channel.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "fmt" -) - -func f(c chan string) { - c <- "hello, channel!" -} - -func main() { - c := make(chan string) - - go f(c) - s := <- c - - fmt.Println(s) -} \ No newline at end of file diff --git a/golang-examples/golang-core/formatint.go b/golang-examples/golang-core/formatint.go deleted file mode 100644 index d2dc9020..00000000 --- a/golang-examples/golang-core/formatint.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "fmt" - "strconv" -) - -func main() { - var i int64 = 9 - - s := strconv.FormatInt(i, 2) - - fmt.Println(s) -} diff --git a/golang-examples/golang-core/go.mod b/golang-examples/golang-core/go.mod deleted file mode 100644 index 9495c43a..00000000 --- a/golang-examples/golang-core/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module golang-core - -go 1.13 diff --git a/golang-examples/golang-core/goroutines.go b/golang-examples/golang-core/goroutines.go deleted file mode 100644 index 5a55aa87..00000000 --- a/golang-examples/golang-core/goroutines.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "fmt" - "time" -) - -func f(s string) { - fmt.Println(s) -} - -func main() { - f("i'm sync!") - - go f("i'm async!") - - go func(s string) { - fmt.Println(s) - }("i'm async too!") - - time.Sleep(time.Second) - fmt.Println("done!") -} diff --git a/golang-examples/golang-core/itoa.go b/golang-examples/golang-core/itoa.go deleted file mode 100644 index 754931bd..00000000 --- a/golang-examples/golang-core/itoa.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "fmt" - "strconv" -) - -func main() { - i := 1 - - s := strconv.Itoa(i) - - fmt.Println(s) -} diff --git a/golang-examples/golang-core/parseint.go b/golang-examples/golang-core/parseint.go deleted file mode 100644 index 5c4da6a9..00000000 --- a/golang-examples/golang-core/parseint.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "fmt" - "strconv" -) - -func main() { - str := "1001" - - n, err := strconv.ParseInt(str, 2, 32) - - if err != nil { - fmt.Println(err) - } else { - fmt.Println(n) - } -} diff --git a/golang-examples/golang-core/waitgroup.go b/golang-examples/golang-core/waitgroup.go deleted file mode 100644 index 83525fc3..00000000 --- a/golang-examples/golang-core/waitgroup.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "fmt" - "sync" -) - -func f(s string, waitGroup *sync.WaitGroup) { - defer waitGroup.Done() - fmt.Println(s) -} - -func main() { - var waitGroup sync.WaitGroup - waitGroup.Add(1) - go f("i'm async!", &waitGroup) - - waitGroup.Add(1) - go func(s string) { - defer waitGroup.Done() - fmt.Println(s) - }("i'm async too!") - - waitGroup.Wait() - - fmt.Println("done!") -} diff --git a/golang-examples/golang-core/waitgroup_timeout.go b/golang-examples/golang-core/waitgroup_timeout.go deleted file mode 100644 index 1dfb0183..00000000 --- a/golang-examples/golang-core/waitgroup_timeout.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "sync" - "time" - "fmt" -) - -type MyWaitGroup struct { - sync.WaitGroup -} - -func (wg *MyWaitGroup) waitTimeout(timeout time.Duration) bool { - done := make(chan struct{}) - - go func() { - defer close(done) - wg.Wait() - }() - - select { - case <-done: - return false - - case <-time.After(timeout): - return true - } -} - -func f(s string, waitGroup *MyWaitGroup) { - defer waitGroup.Done() - fmt.Println(s) -} - -func main() { - var waitGroup MyWaitGroup - - waitGroup.Add(1) - go f("i'm async!", &waitGroup) - - waitGroup.Add(1) - go func(s string) { - defer waitGroup.Done() - time.Sleep(2*time.Second) - fmt.Println(s) - }("i'm async too!") - - r := waitGroup.waitTimeout(time.Second) - if r == true { - fmt.Println("Wait timeout!") - } else { - fmt.Println("done!") - } -} \ No newline at end of file diff --git a/golang-examples/rest-gin-gorm/go.mod b/golang-examples/rest-gin-gorm/go.mod deleted file mode 100644 index 7e915eb5..00000000 --- a/golang-examples/rest-gin-gorm/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module rest-gin-gorm - -go 1.13 - -require ( - github.com/gin-gonic/gin v1.4.0 - github.com/google/wire v0.3.0 - github.com/jinzhu/gorm v1.9.10 -) diff --git a/golang-examples/rest-gin-gorm/go.sum b/golang-examples/rest-gin-gorm/go.sum deleted file mode 100644 index 436a638d..00000000 --- a/golang-examples/rest-gin-gorm/go.sum +++ /dev/null @@ -1,168 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= -cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k= -github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/wire v0.3.0 h1:imGQZGEVEHpje5056+K+cgdO72p0LQv2xIIFXNGUf60= -github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jinzhu/gorm v1.9.10 h1:HvrsqdhCW78xpJF67g1hMxS6eCToo9PZH4LDB8WKPac= -github.com/jinzhu/gorm v1.9.10/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= -github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= -github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s= -golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/golang-examples/rest-gin-gorm/main.go b/golang-examples/rest-gin-gorm/main.go deleted file mode 100644 index 93951da0..00000000 --- a/golang-examples/rest-gin-gorm/main.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "os" - - "github.com/jinzhu/gorm" - _ "github.com/jinzhu/gorm/dialects/mysql" - "github.com/gin-gonic/gin" - - "rest-gin-gorm/product" -) - -func initDB() *gorm.DB{ - db, err := gorm.Open("mysql", os.Getenv("DB_URL")) - if err != nil { - panic(err) - } - - db.AutoMigrate(&product.Product{}) - - return db -} - -func main() { - db := initDB() - defer db.Close() - - productAPI := InitProductAPI(db) - - r := gin.Default() - - r.GET("/products", productAPI.FindAll) - r.GET("/products/:id", productAPI.FindByID) - r.POST("/products", productAPI.Create) - r.PUT("/products/:id", productAPI.Update) - r.DELETE("/products/:id", productAPI.Delete) - - err := r.Run() - if err != nil { - panic(err) - } -} diff --git a/golang-examples/rest-gin-gorm/product/product.go b/golang-examples/rest-gin-gorm/product/product.go deleted file mode 100644 index 34838ad0..00000000 --- a/golang-examples/rest-gin-gorm/product/product.go +++ /dev/null @@ -1,9 +0,0 @@ -package product - -import "github.com/jinzhu/gorm" - -type Product struct { - gorm.Model - Code string - Price uint -} diff --git a/golang-examples/rest-gin-gorm/product/product_api.go b/golang-examples/rest-gin-gorm/product/product_api.go deleted file mode 100644 index 90966ba9..00000000 --- a/golang-examples/rest-gin-gorm/product/product_api.go +++ /dev/null @@ -1,79 +0,0 @@ -package product - -import ( - "strconv" - "log" - "net/http" - "github.com/gin-gonic/gin" -) - -type ProductAPI struct { - ProductService ProductService -} - -func ProvideProductAPI(p ProductService) ProductAPI { - return ProductAPI{ProductService: p} -} - -func (p *ProductAPI) FindAll(c *gin.Context) { - products := p.ProductService.FindAll() - - c.JSON(http.StatusOK, gin.H{"products": ToProductDTOs(products)}) -} - -func (p *ProductAPI) FindByID(c *gin.Context) { - id, _ := strconv.Atoi(c.Param("id")) - product := p.ProductService.FindByID(uint(id)) - - c.JSON(http.StatusOK, gin.H{"product": ToProductDTO(product)}) -} - -func (p *ProductAPI) Create(c *gin.Context) { - var productDTO ProductDTO - err := c.BindJSON(&productDTO) - if err != nil { - log.Fatalln(err) - c.Status(http.StatusBadRequest) - return - } - - createdProduct := p.ProductService.Save(ToProduct(productDTO)) - - c.JSON(http.StatusOK, gin.H{"product": ToProductDTO(createdProduct)}) -} - -func (p *ProductAPI) Update(c *gin.Context) { - var productDTO ProductDTO - err := c.BindJSON(&productDTO) - if err != nil { - log.Fatalln(err) - c.Status(http.StatusBadRequest) - return - } - - id, _ := strconv.Atoi(c.Param("id")) - product := p.ProductService.FindByID(uint(id)) - if product == (Product{}) { - c.Status(http.StatusBadRequest) - return - } - - product.Code = productDTO.Code - product.Price = productDTO.Price - p.ProductService.Save(product) - - c.Status(http.StatusOK) -} - -func (p *ProductAPI) Delete(c *gin.Context) { - id, _ := strconv.Atoi(c.Param("id")) - product := p.ProductService.FindByID(uint(id)) - if product == (Product{}) { - c.Status(http.StatusBadRequest) - return - } - - p.ProductService.Delete(product) - - c.Status(http.StatusOK) -} diff --git a/golang-examples/rest-gin-gorm/product/product_dto.go b/golang-examples/rest-gin-gorm/product/product_dto.go deleted file mode 100644 index f8981f65..00000000 --- a/golang-examples/rest-gin-gorm/product/product_dto.go +++ /dev/null @@ -1,7 +0,0 @@ -package product - -type ProductDTO struct { - ID uint `json:"id,string,omitempty"` - Code string `json:"code"` - Price uint `json:"price,string"` -} diff --git a/golang-examples/rest-gin-gorm/product/product_mapper.go b/golang-examples/rest-gin-gorm/product/product_mapper.go deleted file mode 100644 index c9add2c7..00000000 --- a/golang-examples/rest-gin-gorm/product/product_mapper.go +++ /dev/null @@ -1,19 +0,0 @@ -package product - -func ToProduct(productDTO ProductDTO) Product { - return Product{Code: productDTO.Code, Price: productDTO.Price} -} - -func ToProductDTO(product Product) ProductDTO { - return ProductDTO{ID: product.ID, Code: product.Code, Price: product.Price} -} - -func ToProductDTOs(products []Product) []ProductDTO { - productdtos := make([]ProductDTO, len(products)) - - for i, itm := range products { - productdtos[i] = ToProductDTO(itm) - } - - return productdtos -} diff --git a/golang-examples/rest-gin-gorm/product/product_repository.go b/golang-examples/rest-gin-gorm/product/product_repository.go deleted file mode 100644 index 3924d6ed..00000000 --- a/golang-examples/rest-gin-gorm/product/product_repository.go +++ /dev/null @@ -1,38 +0,0 @@ -package product - -import ( - "github.com/jinzhu/gorm" - _ "github.com/jinzhu/gorm/dialects/mysql" -) - -type ProductRepository struct { - DB *gorm.DB -} - -func ProvideProductRepostiory(DB *gorm.DB) ProductRepository { - return ProductRepository{DB: DB} -} - -func (p *ProductRepository) FindAll() []Product { - var products []Product - p.DB.Find(&products) - - return products -} - -func (p *ProductRepository) FindByID(id uint) Product { - var product Product - p.DB.First(&product, id) - - return product -} - -func (p *ProductRepository) Save(product Product) Product { - p.DB.Save(&product) - - return product -} - -func (p *ProductRepository) Delete(product Product) { - p.DB.Delete(&product) -} diff --git a/golang-examples/rest-gin-gorm/product/product_service.go b/golang-examples/rest-gin-gorm/product/product_service.go deleted file mode 100644 index c4d8e8f0..00000000 --- a/golang-examples/rest-gin-gorm/product/product_service.go +++ /dev/null @@ -1,27 +0,0 @@ -package product - -type ProductService struct { - ProductRepository ProductRepository -} - -func ProvideProductService(p ProductRepository) ProductService { - return ProductService{ProductRepository: p} -} - -func (p *ProductService) FindAll() []Product { - return p.ProductRepository.FindAll() -} - -func (p *ProductService) FindByID(id uint) Product { - return p.ProductRepository.FindByID(id) -} - -func (p *ProductService) Save(product Product) Product { - p.ProductRepository.Save(product) - - return product -} - -func (p *ProductService) Delete(product Product) { - p.ProductRepository.Delete(product) -} diff --git a/golang-examples/rest-gin-gorm/wire.go b/golang-examples/rest-gin-gorm/wire.go deleted file mode 100644 index 7ff00ade..00000000 --- a/golang-examples/rest-gin-gorm/wire.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import ( - "github.com/jinzhu/gorm" - "github.com/google/wire" - "rest-gin-gorm/product" -) - -func initProductAPI(db *gorm.DB) product.ProductAPI { - wire.Build(product.ProvideProductRepostiory, product.ProvideProductService, product.ProvideProductAPI) - - return product.ProductAPI{} -} diff --git a/golang-examples/rest-gin-gorm/wire_gen.go b/golang-examples/rest-gin-gorm/wire_gen.go deleted file mode 100644 index 4ae2dc41..00000000 --- a/golang-examples/rest-gin-gorm/wire_gen.go +++ /dev/null @@ -1,24 +0,0 @@ -// Code generated by Wire. DO NOT EDIT. - -//go:generate wire -//+build !wireinject - -package main - -import ( - "github.com/jinzhu/gorm" - "rest-gin-gorm/product" -) - -import ( - _ "github.com/jinzhu/gorm/dialects/mysql" -) - -// Injectors from wire.go: - -func InitProductAPI(db *gorm.DB) product.ProductAPI { - productRepository := product.ProvideProductRepostiory(db) - productService := product.ProvideProductService(productRepository) - productAPI := product.ProvideProductAPI(productService) - return productAPI -} diff --git a/java-examples/java-core/pom.xml b/java-examples/java-core/pom.xml index d7558690..1848fec6 100644 --- a/java-examples/java-core/pom.xml +++ b/java-examples/java-core/pom.xml @@ -9,8 +9,8 @@ 1.0-SNAPSHOT - 11 - 11 + 14 + 14 @@ -29,7 +29,13 @@ org.assertj assertj-core 3.12.2 + test + + + com.github.tomakehurst + wiremock-jre8 + 2.26.3 + test - \ No newline at end of file diff --git a/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListIterationExample.java b/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListIterationExample.java new file mode 100644 index 00000000..d407719e --- /dev/null +++ b/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListIterationExample.java @@ -0,0 +1,51 @@ +package com.hellokoding.java.collections; + +import java.util.*; + +public class ArrayListIterationExample { + public static void main(String[] args) { + List arrayList1 = new ArrayList<>(List.of(1, 2, 3)); + + // Iterate forward with for-index loop + for(int i=0; i=0; i--){ + int ele = arrayList1.get(i); + System.out.printf("%d ", ele); // 3 2 1 + } + + System.out.println(); + + // Iterate forward with for-each loop + for(int ele : arrayList1){ + System.out.printf("%d ", ele); // 1 2 3 + } + + System.out.println(); + + // Iterate forward with forEach(Consumer) method + arrayList1.forEach(e -> System.out.printf("%d ", e)); // 1 2 3 + + System.out.println(); + + // Iterate forward with iterator() method + Iterator iterator = arrayList1.iterator(); + while (iterator.hasNext()){ + System.out.printf("%d ", iterator.next()); // 1 2 3 + } + + System.out.println(); + + // Iterate backward with listIterator() method + ListIterator listIterator = arrayList1.listIterator(arrayList1.size()); + while (listIterator.hasPrevious()){ + System.out.printf("%d ", listIterator.previous()); // 3 2 1 + } + } +} diff --git a/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListSortExample.java b/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListSortExample.java new file mode 100644 index 00000000..0757a999 --- /dev/null +++ b/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListSortExample.java @@ -0,0 +1,79 @@ +package com.hellokoding.java.collections; + +import java.time.LocalDate; +import java.util.*; + +public class ArrayListSortExample { + public static void main(String[] args) { + List arrayList1 = new ArrayList<>(List.of(3, 1, 2)); + + // sort an ArrayList of numbers in ascending order with Collections.sort() + Collections.sort(arrayList1); + System.out.println(arrayList1); // [1, 2, 3] + + // sort an ArrayList of numbers in descending order with Collections.sort() + Collections.sort(arrayList1, Comparator.reverseOrder()); + System.out.println(arrayList1); // [3, 2, 1] + + List arrayList2 = new ArrayList<>(List.of("c", "a", "b")); + + // sort an ArrayList of Strings in alphabetical order with sort() + arrayList2.sort(Comparator.naturalOrder()); + System.out.println(arrayList2); // [a, b, c] + + // sort an ArrayList of Strings in reverse alphabetical order with sort() + arrayList2.sort(Comparator.reverseOrder()); + System.out.println(arrayList2); // [c, b, a] + + LocalDate ld1 = LocalDate.of(2019, 3, 1); + LocalDate ld2 = LocalDate.of(2019, 1, 1); + LocalDate ld3 = LocalDate.of(2019, 2, 1); + List arrayList3 = new ArrayList<>(List.of(ld1, ld2, ld3)); + + // sort an ArrayList of Dates in chronological order + arrayList3.sort(Comparator.naturalOrder()); + System.out.println(arrayList3); // [2019-01-01, 2019-02-01, 2019-03-01] + + // sort an ArrayList of Dates in reverse chronological order + arrayList3.sort(Comparator.reverseOrder()); + System.out.println(arrayList3); // [2019-03-01, 2019-02-01, 2019-01-01] + } + + static class Employee implements Comparable { + private final Integer id; + private final String name; + + public Employee(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Employee employee = (Employee) o; + return Objects.equals(id, employee.id) && + Objects.equals(name, employee.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public int compareTo(Employee o) { + return Comparator.comparing(Employee::getName) + .compare(this, o); + } + } +} diff --git a/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListUserDefinedObjectsExample.java b/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListUserDefinedObjectsExample.java new file mode 100644 index 00000000..51955bbf --- /dev/null +++ b/java-examples/java-core/src/main/java/com/hellokoding/java/collections/ArrayListUserDefinedObjectsExample.java @@ -0,0 +1,50 @@ +package com.hellokoding.java.collections; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ArrayListUserDefinedObjectsExample { + public static void main(String[] args) { + Book book1 = new Book(1, "Spring Boot In Practice"); + Book book2 = new Book(2, "Algorithms Bootcamp"); + List books = new ArrayList<>(); + books.add(book1); + books.add(book2); + + System.out.println(books.contains(book1)); // true + System.out.println(books.contains(book2)); // true + } + + static class Book { + private final int id; + private final String title; + + public Book(int id, String title) { + this.id = id; + this.title = title; + } + + public int getId() { + return id; + } + + public String getTitle() { + return title; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Book book = (Book) o; + return id == book.id && + Objects.equals(title, book.title); + } + + @Override + public int hashCode() { + return Objects.hash(id, title); + } + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/ArrayListInitializeTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/ArrayListInitializeTest.java index e8238ec8..52580338 100644 --- a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/ArrayListInitializeTest.java +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/ArrayListInitializeTest.java @@ -53,4 +53,15 @@ public void initWithDoubleBrace() { assertThat(arrayList).contains(1, 2, 3); } + + @Test + public void initWithCapacity() { + List arrayList = new ArrayList<>(1000); + + for (int i = 0; i < 1000; i++) { + arrayList.add(i); + } + + assertThat(arrayList).hasSize(1000); + } } diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/ArrayListTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/ArrayListTest.java index b6c950d9..901e5085 100644 --- a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/ArrayListTest.java +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/ArrayListTest.java @@ -160,6 +160,8 @@ int getId() { String getTitle() { return title; } + + } @Test @@ -175,29 +177,38 @@ public void objectsComparingFixed(){ } static class BookFixed { - int id; - String title; + private int id; + private String title; - BookFixed(int id, String title) { + public BookFixed(int id, String title) { this.id = id; this.title = title; } - int getId() { + public int getId() { return id; } - String getTitle() { + public String getTitle() { return title; } + public void setTitle(String title) { + this.title = title; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BookFixed bookFixed = (BookFixed) o; return id == bookFixed.id && - Objects.equals(title, bookFixed.title); + Objects.equals(title, bookFixed.title); + } + + @Override + public int hashCode() { + return Objects.hash(id, title); } } } diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapInitializeTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapInitializeTest.java index 4ef38686..fe97770b 100644 --- a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapInitializeTest.java +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapInitializeTest.java @@ -104,4 +104,17 @@ public void initializeWithPutAll() { } + @Test + public void initializeWithInitialCapacityAndLoadFactor() { + // init with 16 and 0.75 as default initial capacity and default load factor + Map map1 = new HashMap<>(); + + int initialCapacity = 2; + // init with 2 and 0.75 as initial capacity and default load factor + Map map2 = new HashMap<>(initialCapacity); + + float loadFactor = 1; + // init with 16 and 1 as default initial capacity and load factor + Map map3 = new HashMap<>(initialCapacity, loadFactor); + } } diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapModifyTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapModifyTest.java new file mode 100644 index 00000000..8d0e04de --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapModifyTest.java @@ -0,0 +1,141 @@ +package com.hellokoding.java.collections; + +import org.junit.Test; + +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HashMapModifyTest { + @Test + public void put() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", 2); + assertThat(map.get("k2")).isEqualTo(2); + + map.put("k2", 20); + assertThat(map.get("k2")).isEqualTo(20); + } + + @Test + public void putIfAbsent() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", 2); + map.put("k3", null); + + map.putIfAbsent("k4", 4); + assertThat(map.get("k4")).isEqualTo(4); + + map.putIfAbsent("k2", 20); + assertThat(map.get("k2")).isEqualTo(2); + + map.putIfAbsent("k3", 3); + assertThat(map.get("k3")).isEqualTo(3); + } + + @Test + public void putAll() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", null); + + Map map2 = new HashMap<>(); + map2.put("k3", 3); + map2.putAll(map); + + assertThat(map2.size()).isEqualTo(3); + } + + @Test + public void increaseValueWithCompute() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", null); + + map.compute("k2", (k, v) -> v == null ? 1 : v++); + assertThat(map.get("k2")).isEqualTo(1); + } + + @Test + public void replace() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", null); + + map.replace("k2", 2); + assertThat(map.get("k2")).isEqualTo(2); + + map.replace("k3", 3); + assertThat(map.get("k3")).isNull(); + } + + @Test + public void replaceIfAssociated() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", null); + + map.replace("k1", 1, 10); + assertThat(map.get("k1")).isEqualTo(10); + + map.replace("k2", 2, 20); + assertThat(map.get("k2")).isNull(); + } + + @Test + public void replaceAll() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", null); + + map.replaceAll((k, v) -> 100); + assertThat(map.get("k1")).isEqualTo(100); + assertThat(map.get("k2")).isEqualTo(100); + } + + @Test + public void removeByKey() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", null); + + Integer v = map.remove("k1"); + assertThat(v).isEqualTo(1); + assertThat(map).hasSize(1); + + v = map.remove("k3"); + assertThat(v).isNull(); + assertThat(map).hasSize(1); + } + + @Test + public void removeByKeyValue() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", null); + + boolean r = map.remove("k1", 10); + assertThat(r).isFalse(); + assertThat(map).hasSize(2); + + r = map.remove("k2", null); + assertThat(r).isTrue(); + assertThat(map).hasSize(1); + + r = map.remove("k3", 3); + assertThat(r).isFalse(); + assertThat(map).hasSize(1); + } + + @Test + public void clear() { + Map map = new HashMap<>(); + map.put("k1", 1); + map.put("k2", null); + + map.clear(); + assertThat(map).hasSize(0); + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapSortingTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapSortingTest.java index 90dc3265..bb30a36f 100644 --- a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapSortingTest.java +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapSortingTest.java @@ -4,6 +4,8 @@ import java.util.*; +import static java.util.Map.Entry.comparingByKey; +import static java.util.Map.Entry.comparingByValue; import static org.assertj.core.api.Assertions.assertThat; public class HashMapSortingTest { @@ -25,28 +27,28 @@ public void sortByKeys_WithTreeMapAndComparable() { @Test public void sortByKeys_WithTreeMapAndComparator() { Map.Entry e1 = Map.entry("k1", 1); - Map.Entry e2 = Map.entry("k2", 2); + Map.Entry e2 = Map.entry("k02", 2); Map.Entry e3 = Map.entry("k3", 3); Map map = new HashMap<>(Map.ofEntries(e3, e1, e2)); NavigableMap treeMap1 = new TreeMap<>(Comparator.reverseOrder()); treeMap1.putAll(map); - assertThat(treeMap1).containsExactly(e3, e2, e1); + assertThat(treeMap1).containsExactly(e3, e1, e2); NavigableMap treeMap2 = treeMap1.descendingMap(); - assertThat(treeMap2).containsExactly(e1, e2, e3); + assertThat(treeMap2).containsExactly(e2, e1, e3); } @Test public void sortByKeys_WithTreeMapAndCustomComparator() { Category c1 = new Category(1, "c1"); - Category c2 = new Category(2, "c2"); + Category c2 = new Category(20, "c2"); Category c3 = new Category(3, "c2"); Book b1 = new Book(1, "b1"); - Book b2 = new Book(2, "b2"); - Book b3 = new Book(3, "b3"); + Book b2 = new Book(20, "b2"); + Book b3 = new Book(3, "b2"); Map.Entry cb1 = Map.entry(c1, b1); Map.Entry cb2 = Map.entry(c2, b2); @@ -60,35 +62,127 @@ public void sortByKeys_WithTreeMapAndCustomComparator() { NavigableMap treeMap1 = new TreeMap<>(c); treeMap1.putAll(map); - assertThat(treeMap1).containsExactly(cb3, cb2, cb1); + assertThat(treeMap1).containsExactly(cb2, cb3, cb1); NavigableMap treeMap2 = treeMap1.descendingMap(); - assertThat(treeMap2).containsExactly(cb1, cb2, cb3); + assertThat(treeMap2).containsExactly(cb1, cb3, cb2); } @Test - public void sortByKeysOrValues_WithTreeSet() { + public void sortByKeysAndByValues_WithTreeSetAndComparable() { Map.Entry e1 = Map.entry("k1", 1); - Map.Entry e2 = Map.entry("k2", 2); + Map.Entry e2 = Map.entry("k2", 20); Map.Entry e3 = Map.entry("k3", 3); Map map = new HashMap<>(Map.ofEntries(e3, e1, e2)); - NavigableSet> treeSet1 = new TreeSet<>(Map.Entry.comparingByKey()); + NavigableSet> treeSet1 = new TreeSet<>(comparingByKey()); treeSet1.addAll(map.entrySet()); assertThat(treeSet1).containsExactly(e1, e2, e3); - Map linkedHashMap = new LinkedHashMap<>(); - treeSet1.forEach(e -> linkedHashMap.put(e.getKey(), e.getValue())); - assertThat(linkedHashMap).containsExactly(e1, e2, e3); + NavigableSet> treeSet2 = new TreeSet<>(comparingByValue()); + treeSet2.addAll(map.entrySet()); + assertThat(treeSet2).containsExactly(e1, e3, e2); + } + + @Test + public void sortByKeysAndByValues_WithTreeSetAndComparator() { + Map.Entry e1 = Map.entry("k1", 1); + Map.Entry e2 = Map.entry("k2", 20); + Map.Entry e3 = Map.entry("k3", 3); + + Map map = new HashMap<>(Map.ofEntries(e3, e1, e2)); + + NavigableSet> treeSet1 = new TreeSet<>(comparingByKey(Comparator.reverseOrder())); + treeSet1.addAll(map.entrySet()); + assertThat(treeSet1).containsExactly(e3, e2, e1); + + NavigableSet> treeSet2 = new TreeSet<>(comparingByValue(Comparator.reverseOrder())); + treeSet2.addAll(map.entrySet()); + assertThat(treeSet2).containsExactly(e2, e3, e1); + } + + @Test + public void sortByKeysAndByValues_WithTreeSetAndCustomComparator() { + Category c1 = new Category(1, "c1"); + Category c2 = new Category(20, "c2"); + Category c3 = new Category(3, "c2"); + + Book b1 = new Book(1, "b1"); + Book b2 = new Book(20, "b2"); + Book b3 = new Book(3, "b2"); + + Map.Entry cb1 = Map.entry(c1, b1); + Map.Entry cb2 = Map.entry(c2, b2); + Map.Entry cb3 = Map.entry(c3, b3); + + Map map = new HashMap<>(Map.ofEntries(cb3, cb1, cb2)); + + Comparator categoryComparator = Comparator + .comparing(Category::getName, Comparator.reverseOrder()) + .thenComparing(Category::getId, Comparator.reverseOrder()); + + NavigableSet> treeSet1 = new TreeSet<>(comparingByKey(categoryComparator)); + treeSet1.addAll(map.entrySet()); + assertThat(treeSet1).containsExactly(cb2, cb3, cb1); + + Comparator bookComparator = Comparator + .comparing(Book::getTitle, Comparator.reverseOrder()) + .thenComparing(Book::getId, Comparator.reverseOrder()); - NavigableSet> treeSet2 = new TreeSet<>(Map.Entry.comparingByValue()); + NavigableSet> treeSet2 = new TreeSet<>(comparingByValue(bookComparator)); treeSet2.addAll(map.entrySet()); - assertThat(treeSet2).containsExactly(e1, e2, e3); + assertThat(treeSet2).containsExactly(cb2, cb3, cb1); + } + + @Test + public void sortByKeysAndByValues_WithArrayListAndComparator() { + Map.Entry e1 = Map.entry("k1", 1); + Map.Entry e2 = Map.entry("k2", 20); + Map.Entry e3 = Map.entry("k3", 3); + + Map map = new HashMap<>(Map.ofEntries(e3, e1, e2)); + + List> arrayList1 = new ArrayList<>(map.entrySet()); + arrayList1.sort(comparingByKey(Comparator.naturalOrder())); + assertThat(arrayList1).containsExactly(e1, e2, e3); + + List> arrayList2 = new ArrayList<>(map.entrySet()); + arrayList2.sort(comparingByValue(Comparator.reverseOrder())); + assertThat(arrayList2).containsExactly(e2, e3, e1); + } + + @Test + public void sortByKeysAndByValues_WithArrayListAndCustomComparator() { + Category c1 = new Category(1, "c1"); + Category c2 = new Category(20, "c2"); + Category c3 = new Category(3, "c2"); + + Book b1 = new Book(1, "b1"); + Book b2 = new Book(20, "b2"); + Book b3 = new Book(3, "b2"); + + Map.Entry cb1 = Map.entry(c1, b1); + Map.Entry cb2 = Map.entry(c2, b2); + Map.Entry cb3 = Map.entry(c3, b3); + + Map map = new HashMap<>(Map.ofEntries(cb3, cb1, cb2)); + + Comparator categoryComparator = Comparator + .comparing(Category::getName, Comparator.reverseOrder()) + .thenComparing(Category::getId, Comparator.reverseOrder()); + + List> arrayList1 = new ArrayList<>(map.entrySet()); + arrayList1.sort(comparingByKey(categoryComparator)); + assertThat(arrayList1).containsExactly(cb2, cb3, cb1); + + Comparator bookComparator = Comparator + .comparing(Book::getTitle, Comparator.reverseOrder()) + .thenComparing(Book::getId, Comparator.reverseOrder()); - Map linkedHashMap2 = new LinkedHashMap<>(); - treeSet2.forEach(e -> linkedHashMap2.put(e.getKey(), e.getValue())); - assertThat(linkedHashMap2).containsExactly(e1, e2, e3); + List> arrayList2 = new ArrayList<>(map.entrySet()); + arrayList2.sort(comparingByValue(bookComparator)); + assertThat(arrayList2).containsExactly(cb2, cb3, cb1); } static class Category { diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapTest.java index b9203d43..024fa91f 100644 --- a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapTest.java +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/HashMapTest.java @@ -2,10 +2,10 @@ import org.junit.Test; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import static java.util.Map.Entry.comparingByKey; +import static java.util.Map.Entry.comparingByValue; import static org.assertj.core.api.Assertions.assertThat; public class HashMapTest { @@ -62,6 +62,20 @@ public void iterateOverKeySet() { map.values().forEach(k -> System.out.printf("%s ", k)); } + @Test + public void retrieve() { + Map hashMap = new HashMap<>(Map.of("k1", 1, "k2", 2)); + + Set> entrySet = hashMap.entrySet(); + assertThat(entrySet).contains(Map.entry("k1", 1), Map.entry("k2", 2)); + + Set keySet = hashMap.keySet(); + assertThat(keySet).contains("k1", "k2"); + + Collection values = hashMap.values(); + assertThat(values).contains(1, 2); + } + @Test public void getValueByKey() { Map map = new HashMap<>(Map.of("k1", 1, "k2", 2)); @@ -78,15 +92,23 @@ public void filter() { } @Test - public void replace() { - Map map = new HashMap<>(); - map.put("k1", 1); - map.put("k2", 2); + public void containsPutReplaceRemove() { + Map map = new HashMap<>(Map.of("k1", 1, "k2", 2)); - map.replace("k2", 20); - map.replace("k3", 3); + boolean containedKey = map.containsKey("k1"); + assertThat(containedKey).isTrue(); - assertThat(map).contains(Map.entry("k1", 1), Map.entry("k2", 20)); + boolean containedValue = map.containsValue(2); + assertThat(containedValue).isTrue(); + + map.put("k3", 3); + assertThat(map).hasSize(3); + + map.replace("k1", 10); + assertThat(map).contains(Map.entry("k1", 10), Map.entry("k2", 2), Map.entry("k3", 3)); + + map.remove("k3"); + assertThat(map).contains(Map.entry("k1", 10), Map.entry("k2", 2)); } @Test @@ -200,4 +222,21 @@ public int hashCode() { return Objects.hash(id, title); } } + + @Test + public void sortByKeysAndByValues_WithArrayListAndComparator() { + Map.Entry e1 = Map.entry("k1", 1); + Map.Entry e2 = Map.entry("k2", 20); + Map.Entry e3 = Map.entry("k3", 3); + + Map map = new HashMap<>(Map.ofEntries(e3, e1, e2)); + + List> arrayList1 = new ArrayList<>(map.entrySet()); + arrayList1.sort(comparingByKey(Comparator.naturalOrder())); + assertThat(arrayList1).containsExactly(e1, e2, e3); + + List> arrayList2 = new ArrayList<>(map.entrySet()); + arrayList2.sort(comparingByValue(Comparator.reverseOrder())); + assertThat(arrayList2).containsExactly(e2, e3, e1); + } } diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/LinkedHashMapTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/LinkedHashMapTest.java new file mode 100644 index 00000000..69d30942 --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/LinkedHashMapTest.java @@ -0,0 +1,244 @@ +package com.hellokoding.java.collections; + +import org.junit.Test; + +import java.util.*; + +import static java.util.Map.Entry.comparingByKey; +import static java.util.Map.Entry.comparingByValue; +import static org.assertj.core.api.Assertions.assertThat; + +public class LinkedHashMapTest { + @Test + public void declare() { + Map linkedHashMap1 = new LinkedHashMap<>(); + assertThat(linkedHashMap1).isInstanceOf(LinkedHashMap.class); + + LinkedHashMap linkedHashMap2 = new LinkedHashMap<>(); + } + + @Test + public void initInOneLineWithFactoryMethods() { + // create and initialize a LinkedHashMap from Java 9+ Map.of + Map linkedHashMap1 = new LinkedHashMap<>((Map.of("k1", 1, "k3", 2, "k2", 3))); + assertThat(linkedHashMap1).hasSize(3); + + // create and initialize a LinkedHashMap from Java 9+ Map.ofEntries + Map linkedHashMap2 = new LinkedHashMap<>(Map.ofEntries(Map.entry("k4", 4), Map.entry("k5", 5))); + assertThat(linkedHashMap2).hasSize(2); + } + + @Test + public void initializeWithPutIfAbsent() { + // Create a new LinkedHashMap + Map linkedHashMap = new LinkedHashMap<>(); + + // Add elements to LinkedHashMap + linkedHashMap.putIfAbsent("k1", 1); + linkedHashMap.putIfAbsent("k2", 2); + linkedHashMap.putIfAbsent("k3", 3); + + // Can add null key and value + linkedHashMap.putIfAbsent(null, 4); + linkedHashMap.putIfAbsent("k4", null); + + // Duplicate key will be ignored + linkedHashMap.putIfAbsent("k1", 10); + assertThat(linkedHashMap).hasSize(5); + + // The output ordering is predictable as LinkedHashMap is reserved the insertion order + System.out.println(linkedHashMap); + } + + @Test + public void iterateOverKeyValuePairs() { + Map linkedHashMap = new LinkedHashMap<>(Map.of("k1", 1, "k2", 2)); + linkedHashMap.forEach((k, v) -> System.out.printf("%s=%d ", k, v)); + } + + @Test + public void iterateOverEntrySetKeySetAndValues() { + Map linkedHashMap = new LinkedHashMap<>(Map.of("k1", 1, "k2", 2)); + linkedHashMap.entrySet().forEach(e -> System.out.printf("%s ", e)); + linkedHashMap.keySet().forEach(k -> System.out.printf("%s ", k)); + linkedHashMap.values().forEach(v -> System.out.printf("%s ", v)); + } + + @Test + public void retrieve() { + Map linkedHashMap = new LinkedHashMap<>(Map.of("k1", 1, "k2", 2)); + + Set> entrySet = linkedHashMap.entrySet(); + assertThat(entrySet).contains(Map.entry("k1", 1), Map.entry("k2", 2)); + + Set keySet = linkedHashMap.keySet(); + assertThat(keySet).contains("k1", "k2"); + + Collection values = linkedHashMap.values(); + assertThat(values).contains(1, 2); + } + + @Test + public void getValueByKey() { + Map linkedHashMap = new LinkedHashMap<>(Map.of("k1", 1, "k2", 2)); + int value = linkedHashMap.get("k1"); + + assertThat(value).isEqualTo(1); + } + + @Test + public void filter() { + Map linkedHashMap = new LinkedHashMap<>(Map.of("k1", 1, "k2", 2)); + Integer[] arr = linkedHashMap.values().stream().filter(v -> v > 1).toArray(Integer[]::new); + assertThat(arr).contains(2); + } + + @Test + public void containsPutReplaceRemove() { + Map linkedHashMap = new LinkedHashMap<>(Map.of("k1", 1, "k2", 2)); + + boolean containedKey = linkedHashMap.containsKey("k1"); + assertThat(containedKey).isTrue(); + + boolean containedValue = linkedHashMap.containsValue(2); + assertThat(containedValue).isTrue(); + + linkedHashMap.put("k3", 3); + assertThat(linkedHashMap).hasSize(3); + + linkedHashMap.replace("k1", 10); + assertThat(linkedHashMap).contains(Map.entry("k1", 10), Map.entry("k2", 2), Map.entry("k3", 3)); + + linkedHashMap.remove("k3"); + assertThat(linkedHashMap).contains(Map.entry("k1", 10), Map.entry("k2", 2)); + } + + @Test + public void objectsComparingProblem(){ + Map linkedHashMap = new LinkedHashMap<>(); + + linkedHashMap.put(new Category(1, "c1"), new Book(1, "b1")); + + boolean containedKey = linkedHashMap.containsKey(new Category(1, "c1")); + assertThat(containedKey).isFalse(); + + boolean containedValue = linkedHashMap.containsValue(new Book(1, "b1")); + assertThat(containedValue).isFalse(); + + linkedHashMap.put(new Category(1, "c1"), new Book(1, "b1")); + assertThat(linkedHashMap).hasSize(2); + + Book previousValue = linkedHashMap.replace(new Category(1, "c1"), new Book(2, "b1")); + assertThat(previousValue).isNull(); + + linkedHashMap.remove(new Category(1, "c1")); + assertThat(linkedHashMap).hasSize(2); + } + + static class Category { + int id; + String name; + + Category(int id, String name) { + this.id = id; + this.name = name; + } + } + + static class Book { + int id; + String title; + + Book(int id, String title) { + this.id = id; + this.title = title; + } + } + + @Test + public void objectsComparingFixed(){ + Map linkedHashMap = new LinkedHashMap<>(); + + linkedHashMap.put(new CategoryFixed(1, "c1"), new BookFixed(1, "b1")); + + boolean containedKey = linkedHashMap.containsKey(new CategoryFixed(1, "c1")); + assertThat(containedKey).isTrue(); + + boolean containedValue = linkedHashMap.containsValue(new BookFixed(1, "b1")); + assertThat(containedValue).isTrue(); + + linkedHashMap.put(new CategoryFixed(1, "c1"), new BookFixed(1, "b1")); + assertThat(linkedHashMap).hasSize(1); + + BookFixed previousValue = linkedHashMap.replace(new CategoryFixed(1, "c1"), new BookFixed(2, "b1")); + assertThat(previousValue).isNotNull(); + + linkedHashMap.remove(new CategoryFixed(1, "c1")); + assertThat(linkedHashMap).hasSize(0); + } + + static class CategoryFixed { + int id; + String name; + + CategoryFixed(int id, String name) { + this.id = id; + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CategoryFixed that = (CategoryFixed) o; + return id == that.id && + Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + } + + static class BookFixed { + int id; + String title; + + BookFixed(int id, String title) { + this.id = id; + this.title = title; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BookFixed bookFixed = (BookFixed) o; + return id == bookFixed.id && + Objects.equals(title, bookFixed.title); + } + + @Override + public int hashCode() { + return Objects.hash(id, title); + } + } + + @Test + public void sortByKeysAndByValues_WithArrayListAndComparator() { + Map.Entry e1 = Map.entry("k1", 1); + Map.Entry e2 = Map.entry("k2", 20); + Map.Entry e3 = Map.entry("k3", 3); + + Map linkedHashMap = new LinkedHashMap<>(Map.ofEntries(e3, e1, e2)); + + List> arrayList1 = new ArrayList<>(linkedHashMap.entrySet()); + arrayList1.sort(comparingByKey(Comparator.naturalOrder())); + assertThat(arrayList1).containsExactly(e1, e2, e3); + + List> arrayList2 = new ArrayList<>(linkedHashMap.entrySet()); + arrayList2.sort(comparingByValue(Comparator.reverseOrder())); + assertThat(arrayList2).containsExactly(e2, e3, e1); + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/collections/TreeMapTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/TreeMapTest.java new file mode 100644 index 00000000..16990e5f --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/collections/TreeMapTest.java @@ -0,0 +1,278 @@ +package com.hellokoding.java.collections; + +import org.junit.Test; + +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TreeMapTest { + @Test + public void declare() { + Map treeMap1 = new TreeMap<>(); + assertThat(treeMap1).isInstanceOf(TreeMap.class); + + TreeMap treeMap2 = new TreeMap<>(); + } + + @Test + public void initWithComparable() { + Map.Entry e1 = Map.entry("k1", 1); + Map.Entry e2 = Map.entry("k2", 20); + Map.Entry e3 = Map.entry("k3", 3); + + // create and initialize a TreeMap with Java 9+ Map.ofEntries + // keys are ordered based on their Comparable implementation + NavigableMap treeMap1 = new TreeMap<>(Map.ofEntries(e3, e1, e2)); + + // the iteration order is based on Comparable of the object key + assertThat(treeMap1).containsExactly(e1, e2, e3); + + // create and initialize a TreeMap with keys ordering based on Comparable + NavigableMap treeMap2 = new TreeMap<>(); + + // value is overrode with put method when duplicate key + treeMap2.put(e1.getKey(), e1.getValue()); + + // duplicate key is ignored with putIfAbsent + treeMap2.putIfAbsent(e1.getKey(), e1.getValue()); + + // import all mappings from an existing Map + treeMap2.putAll(treeMap1); + + // the iteration order is based on Comparable of the object key + assertThat(treeMap2).containsExactly(e1, e2, e3); + } + + @Test + public void initWithComparator() { + Map.Entry e1 = Map.entry("k1", 1); + Map.Entry e2 = Map.entry("k2", 20); + Map.Entry e3 = Map.entry("k3", 3); + + NavigableMap treeMap = new TreeMap<>(Comparator.reverseOrder()); + treeMap.putAll(Map.ofEntries(e3, e1, e2)); + + // the iteration order is in + // reverse order of the object key Comparable implementation + assertThat(treeMap).containsExactly(e3, e2, e1); + } + + @Test + public void iterateOverKeyValuePairs() { + NavigableMap treeMap = new TreeMap<>(Map.of("k1", 1, "k2", 2)); + treeMap.forEach((k, v) -> System.out.printf("%s=%d ", k, v)); + } + + @Test + public void iterateOverEntrySetKeySetAndValues() { + NavigableMap treeMap = new TreeMap<>(Map.of("k1", 1, "k2", 2)); + treeMap.entrySet().forEach(e -> System.out.printf("%s ", e)); + treeMap.keySet().forEach(k -> System.out.printf("%s ", k)); + treeMap.values().forEach(v -> System.out.printf("%s ", v)); + } + + @Test + public void retrieve() { + NavigableMap treeMap = new TreeMap<>(Map.of("k1", 1, "k2", 2)); + + Set> entrySet = treeMap.entrySet(); + assertThat(entrySet).contains(Map.entry("k1", 1), Map.entry("k2", 2)); + + Set keySet = treeMap.keySet(); + assertThat(keySet).contains("k1", "k2"); + + Collection values = treeMap.values(); + assertThat(values).contains(1, 2); + } + + @Test + public void getValueByKey() { + NavigableMap treeMap = new TreeMap<>(Map.of("k1", 1, "k2", 2)); + int value = treeMap.get("k1"); + + assertThat(value).isEqualTo(1); + } + + @Test + public void filter() { + NavigableMap treeMap = new TreeMap<>(Map.of("k1", 1, "k2", 2)); + Integer[] arr = treeMap.values().stream().filter(v -> v > 1).toArray(Integer[]::new); + assertThat(arr).contains(2); + } + + @Test + public void containsPutReplaceRemove() { + NavigableMap treeMap = new TreeMap<>(Map.of("k1", 1, "k2", 2)); + + boolean containedKey = treeMap.containsKey("k1"); + assertThat(containedKey).isTrue(); + + boolean containedValue = treeMap.containsValue(2); + assertThat(containedValue).isTrue(); + + treeMap.put("k3", 3); + assertThat(treeMap).hasSize(3); + + treeMap.replace("k1", 10); + assertThat(treeMap).contains(Map.entry("k1", 10), Map.entry("k2", 2), Map.entry("k3", 3)); + + treeMap.remove("k3"); + assertThat(treeMap).contains(Map.entry("k1", 10), Map.entry("k2", 2)); + } + + @Test + public void objectsComparingProblem(){ + NavigableMap treeMap = new TreeMap<>(); + + treeMap.put(new Category(1, "c1"), new Book(1, "b1")); + + boolean containedKey = treeMap.containsKey(new Category(2, "c1")); + assertThat(containedKey).isTrue(); + + boolean containedValue = treeMap.containsValue(new Book(2, "b1")); + assertThat(containedValue).isTrue(); + + treeMap.put(new Category(2, "c1"), new Book(2, "b1")); + assertThat(treeMap).hasSize(1); + + Book previousValue = treeMap.replace(new Category(2, "c1"), new Book(2, "b1")); + assertThat(previousValue).isNotNull(); + + treeMap.remove(new Category(2, "c1")); + assertThat(treeMap).hasSize(0); + } + + static class Category implements Comparable { + int id; + String name; + + Category(int id, String name) { + this.id = id; + this.name = name; + } + + @Override + public int compareTo(Category o) { + return CharSequence.compare(this.name, o.name); + } + } + + static class Book implements Comparable { + int id; + String title; + + Book(int id, String title) { + this.id = id; + this.title = title; + } + + int getId() { + return id; + } + + String getTitle() { + return title; + } + + @Override + public int compareTo(Book o) { + return CharSequence.compare(this.title, o.title); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Book book = (Book) o; + return Objects.equals(title, book.title); + } + } + + @Test + public void objectsComparingFixed(){ + NavigableMap treeMap = new TreeMap<>(); + + treeMap.put(new CategoryFixed(1, "c1"), new BookFixed(1, "b1")); + + boolean containedKey = treeMap.containsKey(new CategoryFixed(2, "c1")); + assertThat(containedKey).isFalse(); + + boolean containedValue = treeMap.containsValue(new BookFixed(2, "b1")); + assertThat(containedValue).isFalse(); + + treeMap.put(new CategoryFixed(2, "c1"), new BookFixed(2, "b1")); + assertThat(treeMap).hasSize(2); + + BookFixed previousValue = treeMap.replace(new CategoryFixed(2, "c1"), new BookFixed(2, "b1")); + assertThat(previousValue).isNotNull(); + + treeMap.remove(new CategoryFixed(2, "c1")); + assertThat(treeMap).hasSize(1); + } + + static class CategoryFixed implements Comparable{ + int id; + String name; + + CategoryFixed(int id, String name) { + this.id = id; + this.name = name; + } + + int getId() { + return id; + } + + String getName() { + return name; + } + + @Override + public int compareTo(CategoryFixed o) { + return Comparator.comparing(CategoryFixed::getName) + .thenComparing(CategoryFixed::getId) + .compare(this, o); + } + } + + static class BookFixed implements Comparable { + int id; + String title; + + BookFixed(int id, String title) { + this.id = id; + this.title = title; + } + + int getId() { + return id; + } + + String getTitle() { + return title; + } + + + @Override + public int compareTo(BookFixed o) { + return Comparator.comparing(BookFixed::getTitle) + .thenComparing(BookFixed::getId) + .compare(this, o); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BookFixed bookFixed = (BookFixed) o; + return id == bookFixed.id && + Objects.equals(title, bookFixed.title); + } + + @Override + public int hashCode() { + return Objects.hash(id, title); + } + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/concurrent/CompletableFutureExceptionHandlingTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/concurrent/CompletableFutureExceptionHandlingTest.java new file mode 100644 index 00000000..4b5a56c6 --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/concurrent/CompletableFutureExceptionHandlingTest.java @@ -0,0 +1,79 @@ +package com.hellokoding.java.concurrent; + +import org.junit.Test; + +import java.util.concurrent.CompletableFuture; + +public class CompletableFutureExceptionHandlingTest { + @Test + public void handleWithException() { + CompletableFuture completableFuture = CompletableFuture + .supplyAsync(() -> Integer.parseInt("s")) + .handleAsync((result, e) -> { + if (e != null) { + System.out.println(e.getMessage()); + return "Error!"; + } else { + return result + 1; + } + }) + .thenAcceptAsync(System.out::println); + } + + @Test + public void handleWithoutExceptions() { + CompletableFuture completableFuture = CompletableFuture + .supplyAsync(() -> 1) + .handleAsync((result, e) -> { + if (e != null) { + System.out.println(e.getMessage()); + return "Error!"; + } else { + return result + 1; + } + }) + .thenAcceptAsync(System.out::println); + } + + @Test + public void exceptionallyWithException() { + CompletableFuture completableFuture = CompletableFuture + .supplyAsync(() -> Integer.parseInt("s")) + .exceptionallyAsync((e) -> { + System.out.println(e.getMessage()); + return 0; + }) + .thenAcceptAsync(System.out::println); + } + + @Test + public void exceptionallyWithoutExceptions() { + CompletableFuture completableFuture = CompletableFuture + .supplyAsync(() -> 1) + .exceptionallyAsync((e) -> { + System.out.println(e.getMessage()); + return 0; + }) + .thenAcceptAsync(System.out::println); + } + + @Test + public void whenCompleteWithException() { + CompletableFuture completableFuture = CompletableFuture + .supplyAsync(() -> Integer.parseInt("s")) + .whenCompleteAsync((result, e) -> { + if (e != null) System.out.println(e.getMessage()); + }) + .thenAcceptAsync(System.out::println); + } + + @Test + public void whenCompleteWithoutExceptions() { + CompletableFuture completableFuture = CompletableFuture + .supplyAsync(() -> 1) + .whenCompleteAsync((result, e) -> { + if (e != null) System.out.println(e.getMessage()); + }) + .thenAcceptAsync(System.out::println); + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/concurrent/CompletableFutureGetVsJoinTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/concurrent/CompletableFutureGetVsJoinTest.java new file mode 100644 index 00000000..cc735588 --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/concurrent/CompletableFutureGetVsJoinTest.java @@ -0,0 +1,14 @@ +package com.hellokoding.java.concurrent; + +import org.junit.Test; + +import java.util.concurrent.CompletableFuture; + +public class CompletableFutureGetVsJoinTest { + @Test + public void handleWithException() { + CompletableFuture completableFuture = CompletableFuture + .supplyAsync(() -> Integer.parseInt("s")) + .thenAcceptAsync(System.out::println); + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/java11/HttpClientExamples.java b/java-examples/java-core/src/test/java/com/hellokoding/java/java11/HttpClientExamples.java new file mode 100644 index 00000000..bc1ca7dd --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/java11/HttpClientExamples.java @@ -0,0 +1,229 @@ +package com.hellokoding.java.java11; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.common.ConsoleNotifier; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; +import com.github.tomakehurst.wiremock.junit.WireMockClassRule; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import java.io.IOException; +import java.net.*; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.util.Base64; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.assertj.core.api.Assertions.assertThat; + +public class HttpClientExamples { + private ObjectMapper objectMapper = new ObjectMapper(); + + @ClassRule + public static final WireMockClassRule wireMockRule = new WireMockClassRule( + WireMockConfiguration.options() + .port(8081) + .notifier(new ConsoleNotifier(true)) + .extensions(new ResponseTemplateTransformer(true))); + + @BeforeClass + public static void setUp() { + stubFor(any(urlEqualTo("/test/resource")) + .withHeader("Accept", equalTo("application/json")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"id\":\"1\",\"title\":\"Java 11 HttpClient in practice\"}"))); + + stubFor(any(urlEqualTo("/test/secure")) + .withBasicAuth("user", "pass") + .willReturn(ok())); + + stubFor(any(urlEqualTo("/test/set-cookie")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Set-Cookie", "SID=Any; Path=/; Domain=localhost"))); + } + + @Test + public void createAnHTTPClient() throws NoSuchAlgorithmException { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .proxy(ProxySelector.getDefault()) + .followRedirects(HttpClient.Redirect.NEVER) + .authenticator(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("user", "pass".toCharArray()); + } + }) + .cookieHandler(new CookieManager()) + .executor(Executors.newFixedThreadPool(2)) + .priority(1) + .sslContext(SSLContext.getDefault()) + .sslParameters(new SSLParameters()) + .connectTimeout(Duration.ofSeconds(1)) + .build(); + + assertThat(client.connectTimeout()).get().isEqualTo(Duration.ofSeconds(1)); + } + + @Test + public void createADefaultHTTPClient() { + HttpClient client = HttpClient.newHttpClient(); + + assertThat(client.version()).isEqualTo(HttpClient.Version.HTTP_2); + assertThat(client.followRedirects()).isEqualTo(HttpClient.Redirect.NEVER); + assertThat(client.proxy()).isEmpty(); + assertThat(client.connectTimeout()).isEmpty(); + assertThat(client.cookieHandler()).isEmpty(); + assertThat(client.authenticator()).isEmpty(); + assertThat(client.executor()).isEmpty(); + } + + @Test + public void buildAnHTTPRequest() { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8081/test/resource")) + .POST(HttpRequest.BodyPublishers.noBody()) + .version(HttpClient.Version.HTTP_2) + .header("Accept", "application/json") + .timeout(Duration.ofMillis(500)) + .build(); + + assertThat(request.timeout().get()).isEqualTo(Duration.ofMillis(500)); + } + + @Test + public void getSync() throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8081/test/resource")) + .header("Accept", "application/json") + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(200); + } + + @Test + public void getAsync() { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8081/test/resource")) + .header("Accept", "application/json") + .build(); + + int statusCode = client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApplyAsync(HttpResponse::statusCode) + .join(); + + assertThat(statusCode).isEqualTo(200); + } + + @Test + public void postAsync() { + HttpClient client = HttpClient.newHttpClient(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8081/test/resource")) + .header("Accept", "application/json") + .POST(HttpRequest.BodyPublishers.ofString("ping!")) + .build(); + + CompletableFuture> completableFuture = + client.sendAsync(request, HttpResponse.BodyHandlers.ofString()); + completableFuture + .thenApplyAsync(HttpResponse::headers) + .thenAcceptAsync(System.out::println); + HttpResponse response = completableFuture.join(); + + assertThat(response.statusCode()).isEqualTo(200); + } + + @Test + public void postJson() throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + + Book book = new Book(1, "Java HttpClient in practice"); + String body = objectMapper.writeValueAsString(book); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8081/test/resource")) + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(objectMapper.readValue(response.body(), Book.class).id).isEqualTo(1); + } + + @Test + public void basicAuthentication() throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + + String encodedAuth = Base64.getEncoder() + .encodeToString(("user" + ":" + "pass").getBytes(StandardCharsets.UTF_8)); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8081/test/secure")) + .header("Authorization", "Basic " + encodedAuth) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertThat(response.statusCode()).isEqualTo(200); + } + + @Test + public void cookie() throws IOException, InterruptedException { + HttpClient client = HttpClient.newBuilder() + .cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ALL)) + .build(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8081/test/set-cookie")) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String cookie = response.headers().firstValue("Set-Cookie").get(); + assertThat(HttpCookie.parse(cookie).get(0).getName()).isEqualTo("SID"); + + request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8081/test/resource")) + .header("Accept", "application/json") + .build(); + response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertThat(response.statusCode()).isEqualTo(200); + } + + static class Book { + public int id; + public String title; + + public Book() { + + } + + public Book(int id, String title) { + this.id = id; + this.title = title; + } + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/java14/HelpfulNullPointerException.java b/java-examples/java-core/src/test/java/com/hellokoding/java/java14/HelpfulNullPointerException.java new file mode 100644 index 00000000..9cab5a04 --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/java14/HelpfulNullPointerException.java @@ -0,0 +1,18 @@ +package com.hellokoding.java.java14; + +import org.junit.Test; + +public class HelpfulNullPointerException { + public void nullString(Pojo pojo) { + pojo.field1 = "a"; + } + + @Test + public void testNullString() { + nullString(null); + } +} + +class Pojo { + String field1; +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/java14/PatternMatchingForInstanceOf.java b/java-examples/java-core/src/test/java/com/hellokoding/java/java14/PatternMatchingForInstanceOf.java new file mode 100644 index 00000000..a1b76c5b --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/java14/PatternMatchingForInstanceOf.java @@ -0,0 +1,34 @@ +package com.hellokoding.java.java14; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PatternMatchingForInstanceOf { + public String instanceOf(Object o) { + if (o instanceof Book) { + Book b = (Book) o; + if (b.id() == 1) { + return b.title(); + } + } + + return ""; + } + + public String instanceOfWithPatternMatching(Object o) { + if (o instanceof Book b && b.id() == 1) { + return b.title(); + } else { + return ""; + } + } + + @Test + public void testInstanceOf() { + Book b = new Book(1, "Java 14"); + String s = instanceOfWithPatternMatching(b); + + assertThat(s).isEqualTo("Java 14"); + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/java14/Records.java b/java-examples/java-core/src/test/java/com/hellokoding/java/java14/Records.java new file mode 100644 index 00000000..e0cecbe6 --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/java14/Records.java @@ -0,0 +1,20 @@ +package com.hellokoding.java.java14; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class Records { + @Test + public void testRecord() { + Book b = new Book(1, "Java 14"); + int id = b.id(); + String title = b.title(); + + assertThat(id).isEqualTo(1); + assertThat(title).isEqualTo("Java 14"); + } +} + +record Book(int id, String title) { +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/java14/SwitchExpressions.java b/java-examples/java-core/src/test/java/com/hellokoding/java/java14/SwitchExpressions.java new file mode 100644 index 00000000..747b371f --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/java14/SwitchExpressions.java @@ -0,0 +1,49 @@ +package com.hellokoding.java.java14; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SwitchExpressions { + public String switchStatementWithBreak(int val) { + var str = ""; + switch (val) { + case 0: + str = "zero"; + break; + case 1: + str = "one"; + break; + default: + System.out.println("default"); + str = "many"; + break; + } + + return str; + } + + public String toWord(int val) { + var str = switch (val) { + case 0 -> "zero"; + case 1 -> "one"; + default -> { + System.out.println("default"); + yield "many"; + } + }; + + return str; + } + + @Test + public void testSwitchExpressions() { + var str1 = toWord (0); + var str2 = toWord (1); + var str3 = toWord (2); + + assertThat(str1).isEqualTo("zero"); + assertThat(str2).isEqualTo("one"); + assertThat(str3).isEqualTo("many"); + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/lang/CheckedVsUnCheckedExceptionTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/lang/CheckedVsUnCheckedExceptionTest.java new file mode 100644 index 00000000..bc3bd696 --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/lang/CheckedVsUnCheckedExceptionTest.java @@ -0,0 +1,37 @@ +package com.hellokoding.java.lang; + +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class CheckedVsUnCheckedExceptionTest { + @Test + public void tryCatchException() { + try { + Files.readAllLines(Path.of("test.txt")); + } catch (IOException e) { + System.out.println(e); + } + } + + @Test + public void throwsException() throws IOException { + Files.readAllLines(Path.of("test.txt")); + } + + @Test + public void tryCatchRuntimeException() { + try { + Integer.parseInt("s"); + } catch (NumberFormatException e) { + System.out.println(e); + } + } + + @Test + public void throwsRuntimeException() throws NumberFormatException { + Integer.parseInt("s"); + } +} diff --git a/java-examples/java-core/src/test/java/com/hellokoding/java/lang/LambdaExceptionTest.java b/java-examples/java-core/src/test/java/com/hellokoding/java/lang/LambdaExceptionTest.java new file mode 100644 index 00000000..ea82bb84 --- /dev/null +++ b/java-examples/java-core/src/test/java/com/hellokoding/java/lang/LambdaExceptionTest.java @@ -0,0 +1,60 @@ +package com.hellokoding.java.lang; + +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.function.Consumer; +import static com.hellokoding.java.lang.ThrowingConsumer.wrapper; + +public class LambdaExceptionTest { + @Test + public void tryCatchInLambda() { + List.of("test.txt", "test2.txt").forEach(item -> { + try { + Files.readAllLines(Path.of(item)); + } catch (IOException e) { + System.out.println(e); + throw new RuntimeException(e); + } + }); + } + + @Test + public void wrapperMethod() { + List.of("test.txt", "test2.txt") + .forEach(item -> readFile(item)); + } + + void readFile(String fileName) throws RuntimeException{ + try { + Files.readAllLines(Path.of(fileName)); + } catch (IOException e) { + System.out.println(e); + throw new RuntimeException(e); + } + } + + @Test + public void genericWrapperMethod() { + List.of("test.txt", "test2.txt") + .forEach(wrapper(item -> Files.readAllLines(Path.of(item)))); + } +} + +@FunctionalInterface +interface ThrowingConsumer { + void accept(T t) throws E; + + static Consumer wrapper(ThrowingConsumer t) { + return arg -> { + try { + t.accept(arg); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-circular-reference-jackson/pom.xml b/jpa-hibernate-examples/jpa-hibernate-circular-reference-jackson/pom.xml index 8514cb7b..c07da1f2 100644 --- a/jpa-hibernate-examples/jpa-hibernate-circular-reference-jackson/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-circular-reference-jackson/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.3.4.RELEASE diff --git a/jpa-hibernate-examples/jpa-hibernate-circular-reference-jackson/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-circular-reference-jackson/src/main/resources/application.properties index 4067a1ea..293dc021 100644 --- a/jpa-hibernate-examples/jpa-hibernate-circular-reference-jackson/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-circular-reference-jackson/src/main/resources/application.properties @@ -1,9 +1,11 @@ -spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=hellokoding -spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-auto=create spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect spring.jpa.generate-ddl=true spring.jpa.show-sql=true + +server.port=8080 diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/pom.xml b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/pom.xml index 7ebe2a8c..70cb1bc0 100644 --- a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.3.4.RELEASE diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/Application.java b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/Application.java new file mode 100644 index 00000000..c7f4b911 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/Application.java @@ -0,0 +1,26 @@ +package com.hellokoding.jpa; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@SpringBootApplication +class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Bean + public CommandLineRunner runner(EmployeeRepository employeeRepository) { + return r -> { + employeeRepository.save(new Employee("tom", Stream.of( + new EmployeePhone("012", true), + new EmployeePhone("013", false) + ).collect(Collectors.toSet()))); + }; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/Employee.java b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/Employee.java new file mode 100644 index 00000000..64432194 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/Employee.java @@ -0,0 +1,28 @@ +package com.hellokoding.jpa; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.util.Set; + +@Getter @Setter +@Entity +public class Employee { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + private String name; + + @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL) + private Set employeePhones; + + public Employee(String name, Set employeePhones) { + this.name = name; + this.employeePhones = employeePhones; + for (EmployeePhone employeePhone: employeePhones) { + employeePhone.setEmployee(this); + } + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeePhone.java b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeePhone.java new file mode 100644 index 00000000..0e33386c --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeePhone.java @@ -0,0 +1,25 @@ +package com.hellokoding.jpa; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Getter @Setter +@Entity +@IdClass(EmployeePhoneId.class) +public class EmployeePhone { + @ManyToOne + @PrimaryKeyJoinColumn + private Employee employee; + + @Id + private String phone; + + private Boolean isPrimary; + + public EmployeePhone(String phone, Boolean isPrimary) { + this.phone = phone; + this.isPrimary = isPrimary; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeePhoneId.java b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeePhoneId.java new file mode 100644 index 00000000..627eca5b --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeePhoneId.java @@ -0,0 +1,15 @@ +package com.hellokoding.jpa; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class EmployeePhoneId implements Serializable { + private Employee employee; + private String phone; + + public EmployeePhoneId() { + + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeeRepository.java b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeeRepository.java new file mode 100644 index 00000000..e8ddff4d --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/EmployeeRepository.java @@ -0,0 +1,6 @@ +package com.hellokoding.jpa; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface EmployeeRepository extends JpaRepository{ +} diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java deleted file mode 100644 index 703c7024..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.hellokoding.jpa; - -import com.hellokoding.jpa.book.Book; -import com.hellokoding.jpa.book.BookRepository; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; - -import java.util.Arrays; -import java.util.Date; - -@Slf4j - -@SpringBootApplication -class JpaApplication { - public static void main(String[] args) { - SpringApplication.run(JpaApplication.class, args); - } - - @Bean - public CommandLineRunner runner(BookRepository bookRepository) { - return r -> { - // Create a couple of Book - bookRepository.saveAll(Arrays.asList(new Book("Hello Koding 1", new Date()), new Book("Hello Koding 2", new Date()))); - - // Fetch all - log.info("My books: " + bookRepository.findAll()); - }; - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/Book.java b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/Book.java deleted file mode 100644 index 697d7c95..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/Book.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.hellokoding.jpa.book; - -import lombok.*; - -import javax.persistence.*; -import java.io.Serializable; -import java.util.Date; - -@Data -@NoArgsConstructor - -@Entity -@IdClass(Book.IdClass.class) -public class Book { - @Id - private String name; - - @Id - private Date publishedDate; - - private Date updatedDate = new Date(); - - public Book(String name, Date publishedDate) { - this.name = name; - this.publishedDate = publishedDate; - } - - @Data - static class IdClass implements Serializable { - private String name; - private Date publishedDate; - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/resources/application.properties index 4067a1ea..d0959338 100644 --- a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/resources/application.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=hellokoding spring.datasource.driver-class-name=com.mysql.jdbc.Driver diff --git a/jpa-hibernate-examples/jpa-hibernate-crud-deleting-entity/pom.xml b/jpa-hibernate-examples/jpa-hibernate-crud-deleting-entity/pom.xml index f33dc96d..62b6ff23 100644 --- a/jpa-hibernate-examples/jpa-hibernate-crud-deleting-entity/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-crud-deleting-entity/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.4.RELEASE + 2.3.4.RELEASE diff --git a/jpa-hibernate-examples/jpa-hibernate-crud-deleting-entity/src/test/java/com/hellokoding/jpa/book/DeletingDataTest.java b/jpa-hibernate-examples/jpa-hibernate-crud-deleting-entity/src/test/java/com/hellokoding/jpa/book/DeletingDataTest.java index c60b3a0a..e5105078 100644 --- a/jpa-hibernate-examples/jpa-hibernate-crud-deleting-entity/src/test/java/com/hellokoding/jpa/book/DeletingDataTest.java +++ b/jpa-hibernate-examples/jpa-hibernate-crud-deleting-entity/src/test/java/com/hellokoding/jpa/book/DeletingDataTest.java @@ -31,16 +31,17 @@ public void setUp(){ } @Test - public void whenDeleteByJPQL_thenSuccess() { + public void whenDeleteAssociatedEntitiesByCascadeType_thenSuccess() { // when - bookRepository.deleteByCategoryId(givenCategoryId); + Category category = categoryRepository.findById(givenCategoryId).get(); + categoryRepository.delete(category); // then assertThat(bookRepository.findByCategoryId(givenCategoryId)).hasSize(0); } @Test - public void whenDeleteByOrphanRemoval_thenSuccess() { + public void whenDeleteAssociatedEntitiesByOrphanRemoval_thenSuccess() { // when Category category = categoryRepository.findById(givenCategoryId).get(); Book book = category.getBooks().iterator().next(); @@ -53,10 +54,9 @@ public void whenDeleteByOrphanRemoval_thenSuccess() { } @Test - public void whenDeleteByCascadeType_thenSuccess() { + public void whenDeleteAssociatedEntitiesByJPQL_thenSuccess() { // when - Category category = categoryRepository.findById(givenCategoryId).get(); - categoryRepository.delete(category); + bookRepository.deleteByCategoryId(givenCategoryId); // then assertThat(bookRepository.findByCategoryId(givenCategoryId)).hasSize(0); diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/Dockerfile b/jpa-hibernate-examples/jpa-hibernate-deleting-data/Dockerfile new file mode 100644 index 00000000..6ae65e4e --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/Dockerfile @@ -0,0 +1 @@ +FROM maven:3.5-jdk-8 \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/pom.xml b/jpa-hibernate-examples/jpa-hibernate-deleting-data/pom.xml new file mode 100644 index 00000000..4f6147eb --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + com.hellokoding.jpa + jpa-hibernate-deleting-data + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + + + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + + + mysql + mysql-connector-java + runtime + + + org.projectlombok + lombok + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/JpaApplication.java b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/JpaApplication.java new file mode 100644 index 00000000..3e211a14 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/JpaApplication.java @@ -0,0 +1,75 @@ +package com.hellokoding.jpa; + +import com.hellokoding.jpa.customer.Card; +import com.hellokoding.jpa.customer.CardService; +import com.hellokoding.jpa.customer.Customer; +import com.hellokoding.jpa.customer.CustomerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import javax.transaction.Transactional; +import java.util.List; + +import static com.hellokoding.jpa.customer.Card.MASTER; +import static com.hellokoding.jpa.customer.Card.VISA; + +@Slf4j +@SpringBootApplication +public class JpaApplication { + public static void main(String[] args) { + SpringApplication.run(JpaApplication.class, args); + } + + @Bean + public CommandLineRunner runner(CustomerService customerService, CardService cardService) { + return r -> { + log.info("Soft deleting manually..."); + Customer custA = customerService.save(new Customer("A", + new Card(123, VISA), + new Card(213, MASTER))); + customerService.softDelete(custA.getId()); + + log.info("Deleting data with CascadeType..."); + Customer custB = customerService.save(new Customer("B", + new Card(124, VISA), + new Card(214, MASTER))); + customerService.delete(custB); + + Customer custC = customerService.save(new Customer("C", + new Card(125, VISA), + new Card(215, MASTER))); + customerService.deleteById(custC.getId()); + + log.info("Deleting data with orphanRemoval..."); + Customer custD = customerService.save(new Customer("D", + new Card(126, VISA), + new Card(216, MASTER))); + custD.getCards().removeIf(c -> VISA.equals(c.getType())); + customerService.save(custD); + + log.info("Deleting data with JPQL"); + Customer custE = customerService.save(new Customer("E", + new Card(127, VISA), + new Card(217, MASTER))); + cardService.deleteByCustomerId(custE.getId()); + customerService.deleteByIdWithJPQL(custE.getId()); + + customerService.save(new Customer("F", + new Card(128, VISA), + new Card(218, MASTER))); + + log.info("Find top 10 customers"); + List customers = customerService.findTop10(); + for (Customer customer : customers) { + log.info(customer.toString()); + + for (Card card : customer.getCards()) { + log.info(card.toString()); + } + } + }; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/Card.java b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/Card.java new file mode 100644 index 00000000..9cfc6df5 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/Card.java @@ -0,0 +1,62 @@ +package com.hellokoding.jpa.customer; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.UpdateTimestamp; +import org.hibernate.annotations.Where; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import java.time.LocalDateTime; +import java.util.Objects; + +@Getter @Setter @NoArgsConstructor @ToString(exclude = "customer") +@SQLDelete(sql = "UPDATE Card SET deleted = 1 WHERE card_number = ?") +@Where(clause = "deleted = 0") +@Entity +public class Card { + public static final String VISA = "VISA"; + public static final String MASTER = "MASTER"; + + @Id + private Integer cardNumber; + + private String type; + + @ManyToOne + @JoinColumn + private Customer customer; + + private Boolean deleted; + + @CreationTimestamp + private LocalDateTime createdAt; + + @UpdateTimestamp + private LocalDateTime updatedAt; + + public Card(Integer cardNumber, String type) { + this.cardNumber = cardNumber; + this.type = type; + this.deleted = false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Card)) return false; + Card card = (Card) o; + return Objects.equals(cardNumber, card.cardNumber); + } + + @Override + public int hashCode() { + return Objects.hash(cardNumber); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CardRepository.java b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CardRepository.java new file mode 100644 index 00000000..4f52b2fb --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CardRepository.java @@ -0,0 +1,13 @@ +package com.hellokoding.jpa.customer; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface CardRepository extends JpaRepository { + @Modifying + @Query("DELETE FROM Card c WHERE c.customer.id = ?1") + void deleteByCustomerId(int customerId); +} diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CardService.java b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CardService.java new file mode 100644 index 00000000..6f12f1cf --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CardService.java @@ -0,0 +1,17 @@ +package com.hellokoding.jpa.customer; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; + +@Service +@Transactional +public class CardService { + @Autowired + private CardRepository cardRepository; + + public void deleteByCustomerId(Integer customerId) { + cardRepository.deleteByCustomerId(customerId); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/Customer.java b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/Customer.java new file mode 100644 index 00000000..a5c8274f --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/Customer.java @@ -0,0 +1,61 @@ +package com.hellokoding.jpa.customer; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.UpdateTimestamp; +import org.hibernate.annotations.Where; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Getter @Setter @NoArgsConstructor @ToString(exclude = "cards") +@SQLDelete(sql = "UPDATE Customer SET deleted = 1 WHERE id = ?") +@Where(clause = "deleted = 0") +@Entity +public class Customer { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + private String name; + + @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true) + private Set cards; + + private Boolean deleted; + + @CreationTimestamp + private LocalDateTime createdAt; + + @UpdateTimestamp + private LocalDateTime updatedAt; + + public Customer(String name, Card... card) { + this.name = name; + this.cards = Stream.of(card).collect(Collectors.toSet()); + this.cards.forEach(x -> x.setCustomer(this)); + this.deleted = false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Customer)) return false; + Customer category = (Customer) o; + return Objects.equals(id, category.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} + diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CustomerRepository.java b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CustomerRepository.java new file mode 100644 index 00000000..3e27ff3d --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CustomerRepository.java @@ -0,0 +1,21 @@ +package com.hellokoding.jpa.customer; + +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface CustomerRepository extends JpaRepository { + @Modifying + @Query("DELETE FROM Customer c WHERE c.id = ?1") + void deleteByIdWithJPQL(int id); + + List findTop10ByDeletedFalse(); + + List findTop10By(); + + @EntityGraph(attributePaths = "cards", type = EntityGraph.EntityGraphType.LOAD) + List findAllByIdIsIn(List ids); +} diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CustomerService.java b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CustomerService.java new file mode 100644 index 00000000..3ba8c2ea --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/java/com/hellokoding/jpa/customer/CustomerService.java @@ -0,0 +1,50 @@ +package com.hellokoding.jpa.customer; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Transactional +public class CustomerService { + @Autowired + private CustomerRepository customerRepository; + + public Customer save(Customer customer) { + return customerRepository.save(customer); + } + + public void softDelete(Integer id) { + Customer customer = customerRepository.findById(id).get(); + customer.setDeleted(true); + + for(Card card : customer.getCards()) { + card.setDeleted(true); + } + + customerRepository.save(customer); + } + + public void delete(Customer customer) { + customerRepository.delete(customer); + } + + public void deleteById(Integer id) { + customerRepository.deleteById(id); + } + + public void deleteByIdWithJPQL(Integer id) { + customerRepository.deleteByIdWithJPQL(id); + } + + public List findTop10() { + List customerIds = customerRepository.findTop10By(); + + return customerRepository.findAllByIdIsIn( + customerIds.stream().map(Customer::getId).collect(Collectors.toList()) + ); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/resources/application.properties new file mode 100644 index 00000000..047b482b --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-deleting-data/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true +spring.datasource.username=root +spring.datasource.password=hellokoding + +spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect +spring.jpa.hibernate.ddl-auto=create +spring.jpa.show-sql=true diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/pom.xml b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/pom.xml index f07f1045..7e7402e9 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 2.1.4.RELEASE + 2.3.4.RELEASE UTF-8 - 1.7 + 1.8 @@ -27,10 +27,14 @@ spring-boot-starter-data-jpa - org.hsqldb - hsqldb + mysql + mysql-connector-java runtime + + org.projectlombok + lombok + diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/Application.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/Application.java index e1c148ca..9b8f6a29 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/Application.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/Application.java @@ -3,27 +3,24 @@ import com.hellokoding.jpa.model.Book; import com.hellokoding.jpa.model.BookPublisher; import com.hellokoding.jpa.model.Publisher; +import com.hellokoding.jpa.repository.BookPublisherRepository; import com.hellokoding.jpa.repository.BookRepository; import com.hellokoding.jpa.repository.PublisherRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.transaction.Transactional; +import java.util.Arrays; import java.util.Date; +@RequiredArgsConstructor @SpringBootApplication public class Application implements CommandLineRunner { - private static final Logger logger = LoggerFactory.getLogger(Application.class); - - @Autowired - private BookRepository bookRepository; - - @Autowired - private PublisherRepository publisherRepository; + private final BookRepository bookRepository; + private final PublisherRepository publisherRepository; + private final BookPublisherRepository bookPublisherRepository; public static void main(String[] args) { SpringApplication.run(Application.class, args); @@ -32,28 +29,18 @@ public static void main(String[] args) { @Override @Transactional public void run(String... strings) throws Exception { - // create new - Book bookA = new Book("Book A"); - Publisher publisherA = new Publisher("Publisher A"); - - BookPublisher bookPublisher = new BookPublisher(); - bookPublisher.setBook(bookA); - bookPublisher.setPublisher(publisherA); - bookPublisher.setPublishedDate(new Date()); - - bookA.getBookPublishers().add(bookPublisher); - - publisherRepository.save(publisherA); - bookRepository.save(bookA); - - // test - System.out.println(bookA.getBookPublishers().size()); - - // update - bookA.getBookPublishers().remove(bookPublisher); - bookRepository.save(bookA); - - // test - System.out.println(bookA.getBookPublishers().size()); + Book b1 = new Book("Spring Boot"); + Book b2 = new Book("Spring Data JPA"); + bookRepository.saveAll(Arrays.asList(b1, b2)); + + Publisher p1 = new Publisher("HelloKoding 1"); + Publisher p2 = new Publisher("HelloKoding 2"); + publisherRepository.saveAll(Arrays.asList(p1, p2)); + + BookPublisher bp1 = new BookPublisher(b1, p1, new Date()); + BookPublisher bp2 = new BookPublisher(b1, p2, new Date()); + BookPublisher bp3 = new BookPublisher(b2, p1, new Date()); + BookPublisher bp4 = new BookPublisher(b2, p2, new Date()); + bookPublisherRepository.saveAll(Arrays.asList(bp1, bp2, bp3, bp4)); } } diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/Book.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/Book.java index 93b2daa6..387f8aa0 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/Book.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/Book.java @@ -1,49 +1,24 @@ package com.hellokoding.jpa.model; +import lombok.Data; + import javax.persistence.*; import java.util.HashSet; import java.util.Set; +@Data @Entity public class Book{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private int id; + private Integer id; private String name; @OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true) - private Set bookPublishers; - - public Book() { - } + private Set bookPublishers = new HashSet<>(); public Book(String name) { this.name = name; - bookPublishers = new HashSet<>(); - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Set getBookPublishers() { - return bookPublishers; - } - - public void setBookPublishers(Set bookPublishers) { - this.bookPublishers = bookPublishers; } } diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisher.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisher.java index 1f59cacd..2d3fc52b 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisher.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisher.java @@ -1,47 +1,36 @@ package com.hellokoding.jpa.model; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + import javax.persistence.*; -import java.io.Serializable; import java.util.Date; +@Getter @Setter @NoArgsConstructor @Entity @Table(name = "book_publisher") -public class BookPublisher implements Serializable{ - @Id +public class BookPublisher { + @EmbeddedId + private BookPublisherId id; + @ManyToOne + @MapsId("bookId") @JoinColumn(name = "book_id") private Book book; - @Id @ManyToOne + @MapsId("publisherId") @JoinColumn(name = "publisher_id") private Publisher publisher; @Column(name = "published_date") private Date publishedDate; - public Book getBook() { - return book; - } - - public void setBook(Book book) { + public BookPublisher(Book book, Publisher publisher, Date publishedDate) { + this.id = new BookPublisherId(book.getId(), publisher.getId()); this.book = book; - } - - - public Publisher getPublisher() { - return publisher; - } - - public void setPublisher(Publisher publisher) { this.publisher = publisher; - } - - public Date getPublishedDate() { - return publishedDate; - } - - public void setPublishedDate(Date publishedDate) { this.publishedDate = publishedDate; } } diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisherId.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisherId.java new file mode 100644 index 00000000..880bb5f4 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisherId.java @@ -0,0 +1,19 @@ +package com.hellokoding.jpa.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import java.io.Serializable; + +@Data @AllArgsConstructor @NoArgsConstructor +@Embeddable +public class BookPublisherId implements Serializable { + @Column(name = "book_id") + private Integer bookId; + + @Column(name = "publisher_id") + private Integer publisherId; +} diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/Publisher.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/Publisher.java index 5944f685..7436683a 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/Publisher.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/model/Publisher.java @@ -1,48 +1,24 @@ package com.hellokoding.jpa.model; +import lombok.Data; + import javax.persistence.*; +import java.util.HashSet; import java.util.Set; +@Data @Entity public class Publisher { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private int id; + private Integer id; private String name; @OneToMany(mappedBy = "publisher") - private Set bookPublishers; - - public Publisher(){ - - } + private Set bookPublishers = new HashSet<>(); public Publisher(String name){ this.name = name; } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Set getBookPublishers() { - return bookPublishers; - } - - public void setBookPublishers(Set bookPublishers) { - this.bookPublishers = bookPublishers; - } } diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/repository/BookPublisherRepository.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/repository/BookPublisherRepository.java new file mode 100644 index 00000000..ffb48897 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/java/com/hellokoding/jpa/repository/BookPublisherRepository.java @@ -0,0 +1,8 @@ +package com.hellokoding.jpa.repository; + +import com.hellokoding.jpa.model.BookPublisher; +import com.hellokoding.jpa.model.BookPublisherId; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BookPublisherRepository extends JpaRepository { +} diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/resources/application.properties index 9fb9708e..18a5333b 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-hsql/src/main/resources/application.properties @@ -1,2 +1,9 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false +spring.datasource.username=root +spring.datasource.password=hellokoding +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + spring.jpa.hibernate.ddl-auto=create -spring.jpa.show-sql=true \ No newline at end of file +spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect +spring.jpa.generate-ddl=true +spring.jpa.show-sql=true diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/Dockerfile b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/Dockerfile new file mode 100644 index 00000000..6ae65e4e --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/Dockerfile @@ -0,0 +1 @@ +FROM maven:3.5-jdk-8 \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/docker-compose.yml b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/docker-compose.yml new file mode 100644 index 00000000..feebb764 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' +services: + hk-mysql: + container_name: hk-mysql + image: mysql/mysql-server:5.7 + environment: + MYSQL_DATABASE: test + MYSQL_ROOT_PASSWORD: hellokoding + MYSQL_ROOT_HOST: '%' + ports: + - "3306:3306" + restart: always + + app: + build: . + volumes: + - .:/app + - ~/.m2:/root/.m2 + working_dir: /app + command: mvn clean spring-boot:run + depends_on: + - hk-mysql diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/pom.xml b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/pom.xml new file mode 100644 index 00000000..c0550d93 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + com.hellokoding.jpa + jpa-hibernate-many-to-many-extra-columns-mysql + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + + + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + runtime + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/JpaApplication.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/JpaApplication.java new file mode 100644 index 00000000..4c6455c9 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/JpaApplication.java @@ -0,0 +1,11 @@ +package com.hellokoding.jpa; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaApplication { + public static void main(String[] args) { + SpringApplication.run(JpaApplication.class, args); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/Book.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/Book.java similarity index 50% rename from jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/Book.java rename to jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/Book.java index 2a09b344..c5af36ee 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/Book.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/Book.java @@ -1,6 +1,6 @@ package com.hellokoding.jpa.book; -import lombok.*; +import lombok.Data; import javax.persistence.*; @@ -12,14 +12,11 @@ public class Book { @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; + @Column(name = "name") private String name; - @OneToOne(mappedBy = "book", cascade = CascadeType.ALL) - private BookDetail bookDetail; - - public Book(String name, BookDetail bookDetail) { + public Book(String name, BookPublisher... bookPublishers) { this.name = name; - this.bookDetail = bookDetail; - this.bookDetail.setBook(this); + for(BookPublisher bookPublisher : bookPublishers) bookPublisher.setBook(this); } } diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisher.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisher.java new file mode 100644 index 00000000..b43b2388 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisher.java @@ -0,0 +1,49 @@ +package com.hellokoding.jpa.book; + +import lombok.*; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; +import java.util.Objects; + +@Getter +@Setter +@NoArgsConstructor + +@Entity +public class BookPublisher implements Serializable { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @ManyToOne + @JoinColumn + private Book book; + + @ManyToOne + @JoinColumn + private Publisher publisher; + + private Date publishedDate; + + public BookPublisher(Publisher publisher, Date publishedDate) { + this.publisher = publisher; + this.publishedDate = publishedDate; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BookPublisher)) return false; + BookPublisher that = (BookPublisher) o; + return Objects.equals(book.getName(), that.book.getName()) && + Objects.equals(publisher.getName(), that.publisher.getName()) && + Objects.equals(publishedDate, that.publishedDate); + } + + @Override + public int hashCode() { + return Objects.hash(book.getName(), publisher.getName(), publishedDate); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisherController.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisherController.java new file mode 100644 index 00000000..989ce964 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisherController.java @@ -0,0 +1,28 @@ +package com.hellokoding.jpa.book; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/bookPublishers") +@RequiredArgsConstructor +public class BookPublisherController { + private final BookPublisherRepository bookPublisherRepository; + + @PostMapping + public ResponseEntity create(@RequestBody BookPublisher bookPublisher) { + BookPublisher savedBookPublisher = bookPublisherRepository.save(bookPublisher); + + return ResponseEntity.status(HttpStatus.CREATED).body(savedBookPublisher); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisherRepository.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisherRepository.java new file mode 100644 index 00000000..2dc859ea --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookPublisherRepository.java @@ -0,0 +1,9 @@ +package com.hellokoding.jpa.book; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface BookPublisherRepository extends JpaRepository{ + List findByPublisherId(Integer publisherId); +} diff --git a/jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookRepository.java similarity index 100% rename from jpa-hibernate-examples/jpa-hibernate-composite-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java rename to jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/BookRepository.java diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/Publisher.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/Publisher.java new file mode 100644 index 00000000..a7ea8713 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/Publisher.java @@ -0,0 +1,22 @@ +package com.hellokoding.jpa.book; + +import lombok.*; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + +@Data + +@Entity +public class Publisher { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + private String name; + + public Publisher(String name) { + this.name = name; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/AddressRepository.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/PublisherRepository.java similarity index 55% rename from jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/AddressRepository.java rename to jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/PublisherRepository.java index 56bbb828..6d198936 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/AddressRepository.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/java/com/hellokoding/jpa/book/PublisherRepository.java @@ -2,5 +2,5 @@ import org.springframework.data.jpa.repository.JpaRepository; -public interface AddressRepository extends JpaRepository{ +public interface PublisherRepository extends JpaRepository{ } diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/resources/application.properties new file mode 100644 index 00000000..d0959338 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql-rest/src/main/resources/application.properties @@ -0,0 +1,9 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false +spring.datasource.username=root +spring.datasource.password=hellokoding +spring.datasource.driver-class-name=com.mysql.jdbc.Driver + +spring.jpa.hibernate.ddl-auto=create +spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect +spring.jpa.generate-ddl=true +spring.jpa.show-sql=true diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java index e9278f99..c564e5fe 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java @@ -1,7 +1,6 @@ package com.hellokoding.jpa; import com.hellokoding.jpa.book.*; -import com.hellokoding.springboot.jpa.book.*; import lombok.RequiredArgsConstructor; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/java/com/hellokoding/jpa/book/BookPublisher.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/java/com/hellokoding/jpa/book/BookPublisher.java index 69de5be1..ec50b418 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/java/com/hellokoding/jpa/book/BookPublisher.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/java/com/hellokoding/jpa/book/BookPublisher.java @@ -12,15 +12,17 @@ @NoArgsConstructor @Entity -public class BookPublisher implements Serializable { +public class BookPublisher { @Id - @ManyToOne - @JoinColumn + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "book_id") private Book book; - @Id - @ManyToOne - @JoinColumn + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "publisher_id") private Publisher publisher; private Date publishedDate; diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/resources/application.properties index 4067a1ea..d0959338 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-extra-columns-mysql/src/main/resources/application.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=hellokoding spring.datasource.driver-class-name=com.mysql.jdbc.Driver diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/pom.xml b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/pom.xml index ee7ecf9a..beee7baf 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/pom.xml @@ -25,10 +25,14 @@ spring-boot-starter-data-jpa - org.hsqldb - hsqldb + mysql + mysql-connector-java runtime + + org.projectlombok + lombok + diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/Application.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/Application.java deleted file mode 100644 index f5dbfb71..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/Application.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.hellokoding.jpa; - -import com.hellokoding.jpa.model.Book; -import com.hellokoding.jpa.model.Publisher; -import com.hellokoding.jpa.repository.BookRepository; -import com.hellokoding.jpa.repository.PublisherRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import javax.transaction.Transactional; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -@SpringBootApplication -public class Application implements CommandLineRunner { - private static final Logger logger = LoggerFactory.getLogger(Application.class); - - @Autowired - private BookRepository bookRepository; - - @Autowired - private PublisherRepository publisherRepository; - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } - - @Override - @Transactional - public void run(String... strings) throws Exception { - // save a couple of books - Publisher publisherA = new Publisher("Publisher A"); - Publisher publisherB = new Publisher("Publisher B"); - Publisher publisherC = new Publisher("Publisher C"); - Book bookA = new Book("Book A", new HashSet<>(Arrays.asList(publisherA, publisherB))); - Book bookB = new Book("Book B", new HashSet<>(Arrays.asList(publisherA, publisherC))); - bookRepository.saveAll(Arrays.asList(bookA, bookB)); - - // fetch all books - for(Book book : bookRepository.findAll()) { - logger.info(book.toString()); - } - - // save a couple of publishers - Book bookD = new Book("Book D"); - Book bookE = new Book("Book E"); - Book bookF = new Book("Book F"); - Publisher publisherD = new Publisher("Publisher D", new HashSet(Arrays.asList(bookD, bookE))); - Publisher publisherE = new Publisher("Publisher E", new HashSet(Arrays.asList(bookE, bookF))); - publisherRepository.saveAll(Arrays.asList(publisherD, publisherE)); - - // fetch all publishers - for(Publisher publisher : publisherRepository.findAll()) { - logger.info(publisher.toString()); - } - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/JpaApplication.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/JpaApplication.java new file mode 100644 index 00000000..73f31f7f --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/JpaApplication.java @@ -0,0 +1,44 @@ +package com.hellokoding.jpa; + +import com.hellokoding.jpa.model.Book; +import com.hellokoding.jpa.model.BookPublisher; +import com.hellokoding.jpa.model.Publisher; +import com.hellokoding.jpa.repository.BookPublisherRepository; +import com.hellokoding.jpa.repository.BookRepository; +import com.hellokoding.jpa.repository.PublisherRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.util.Arrays; +import java.util.Date; + +@RequiredArgsConstructor +@SpringBootApplication +public class JpaApplication implements CommandLineRunner { + private final BookRepository bookRepository; + private final PublisherRepository publisherRepository; + private final BookPublisherRepository bookPublisherRepository; + + public static void main(String[] args) { + SpringApplication.run(JpaApplication.class, args); + } + + @Override + public void run(String... args) { + Book b1 = new Book("Spring Boot"); + Book b2 = new Book("Spring Data JPA"); + bookRepository.saveAll(Arrays.asList(b1, b2)); + + Publisher p1 = new Publisher("HelloKoding 1"); + Publisher p2 = new Publisher("HelloKoding 2"); + publisherRepository.saveAll(Arrays.asList(p1, p2)); + + BookPublisher bp1 = new BookPublisher(b1, p1, new Date()); + BookPublisher bp2 = new BookPublisher(b1, p2, new Date()); + BookPublisher bp3 = new BookPublisher(b2, p1, new Date()); + BookPublisher bp4 = new BookPublisher(b2, p2, new Date()); + bookPublisherRepository.saveAll(Arrays.asList(bp1, bp2, bp3, bp4)); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/Book.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/Book.java index 9a106a5e..389e73bd 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/Book.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/Book.java @@ -1,70 +1,20 @@ package com.hellokoding.jpa.model; +import lombok.Data; + import javax.persistence.*; -import java.util.Set; +@Data @Entity -public class Book{ +public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; + @Column(name = "name") private String name; - @ManyToMany(cascade = CascadeType.ALL) - @JoinTable(name = "book_publisher", joinColumns = @JoinColumn(name = "book_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "publisher_id", referencedColumnName = "id")) - private Set publishers; - - public Book() { - - } - public Book(String name) { this.name = name; } - - public Book(String name, Set publishers){ - this.name = name; - this.publishers = publishers; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Set getPublishers() { - return publishers; - } - - public void setPublishers(Set publishers) { - this.publishers = publishers; - } - - @Override - public String toString() { - String result = String.format( - "Book [id=%d, name='%s']%n", - id, name); - if (publishers != null) { - for(Publisher publisher : publishers) { - result += String.format( - "Publisher[id=%d, name='%s']%n", - publisher.getId(), publisher.getName()); - } - } - - return result; - } } diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisher.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisher.java new file mode 100644 index 00000000..8dd15982 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/BookPublisher.java @@ -0,0 +1,48 @@ +package com.hellokoding.jpa.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; +import java.util.Date; +import java.util.Objects; + +@Getter @Setter @NoArgsConstructor +@Entity +public class BookPublisher { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "book_id") + private Book book; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "publisher_id") + private Publisher publisher; + + private Date publishedDate; + + public BookPublisher(Book book, Publisher publisher, Date publishedDate) { + this.book = book; + this.publisher = publisher; + this.publishedDate = publishedDate; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BookPublisher)) return false; + BookPublisher that = (BookPublisher) o; + return Objects.equals(book.getName(), that.book.getName()) && + Objects.equals(publisher.getName(), that.publisher.getName()) && + Objects.equals(publishedDate, that.publishedDate); + } + + @Override + public int hashCode() { + return Objects.hash(book.getName(), publisher.getName(), publishedDate); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/Publisher.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/Publisher.java index 8ab38566..7f292dba 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/Publisher.java +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/model/Publisher.java @@ -1,8 +1,10 @@ package com.hellokoding.jpa.model; +import lombok.Data; + import javax.persistence.*; -import java.util.Set; +@Data @Entity public class Publisher { @Id @@ -11,59 +13,7 @@ public class Publisher { private String name; - @ManyToMany(mappedBy = "publishers") - private Set books; - - public Publisher(){ - - } - - public Publisher(String name){ - this.name = name; - } - - public Publisher(String name, Set books){ - this.name = name; - this.books = books; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { + public Publisher(String name) { this.name = name; } - - public Set getBooks() { - return books; - } - - public void setBooks(Set books) { - this.books = books; - } - - @Override - public String toString() { - String result = String.format( - "Publisher [id=%d, name='%s']%n", - id, name); - if (books != null) { - for(Book book : books) { - result += String.format( - "Book[id=%d, name='%s']%n", - book.getId(), book.getName()); - } - } - - return result; - } } diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/repository/BookPublisherRepository.java b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/repository/BookPublisherRepository.java new file mode 100644 index 00000000..bfaef2bd --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/java/com/hellokoding/jpa/repository/BookPublisherRepository.java @@ -0,0 +1,7 @@ +package com.hellokoding.jpa.repository; + +import com.hellokoding.jpa.model.BookPublisher; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BookPublisherRepository extends JpaRepository { +} diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/resources/application.properties index e50c4d7d..d0959338 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-hsql/src/main/resources/application.properties @@ -1,2 +1,9 @@ -spring.jpa.hibernate.ddl-auto=create-drop -spring.jpa.show-sql=true \ No newline at end of file +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false +spring.datasource.username=root +spring.datasource.password=hellokoding +spring.datasource.driver-class-name=com.mysql.jdbc.Driver + +spring.jpa.hibernate.ddl-auto=create +spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect +spring.jpa.generate-ddl=true +spring.jpa.show-sql=true diff --git a/jpa-hibernate-examples/jpa-hibernate-many-to-many-mysql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-many-to-many-mysql/src/main/resources/application.properties index 4067a1ea..d0959338 100644 --- a/jpa-hibernate-examples/jpa-hibernate-many-to-many-mysql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-many-to-many-mysql/src/main/resources/application.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=hellokoding spring.datasource.driver-class-name=com.mysql.jdbc.Driver diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-hsql/pom.xml b/jpa-hibernate-examples/jpa-hibernate-one-to-many-hsql/pom.xml index 703dc66b..f112ae6a 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-many-hsql/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-hsql/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 2.1.4.RELEASE + 2.3.4.RELEASE UTF-8 - 1.7 + 1.8 diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/Dockerfile b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/Dockerfile new file mode 100644 index 00000000..6ae65e4e --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/Dockerfile @@ -0,0 +1 @@ +FROM maven:3.5-jdk-8 \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/docker-compose.yml b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/docker-compose.yml new file mode 100644 index 00000000..2d76d303 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' +services: + hk-mysql: + container_name: hk-mysql + image: mysql/mysql-server:8.0 + environment: + MYSQL_DATABASE: test + MYSQL_ROOT_PASSWORD: hellokoding + MYSQL_ROOT_HOST: '%' + ports: + - "3306:3306" + restart: always + + app: + build: . + volumes: + - .:/app + - ~/.m2:/root/.m2 + working_dir: /app + command: mvn clean spring-boot:run + depends_on: + - hk-mysql diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/pom.xml b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/pom.xml new file mode 100644 index 00000000..73b92482 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + com.hellokoding.jpa + jpa-hibernate-one-to-many-mysql + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + runtime + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Application.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Application.java new file mode 100644 index 00000000..579e06f5 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Application.java @@ -0,0 +1,11 @@ +package com.hellokoding.jpa.bidirectional; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Book.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Book.java new file mode 100644 index 00000000..9f241d2a --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Book.java @@ -0,0 +1,46 @@ +package com.hellokoding.jpa.bidirectional; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +@Entity +public class Book { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + @Column(name = "name") + private String name; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "library_id") + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Library library; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Library getLibrary() { + return library; + } + + public void setLibrary(Library library) { + this.library = library; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/BookController.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/BookController.java new file mode 100644 index 00000000..631dd460 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/BookController.java @@ -0,0 +1,87 @@ +package com.hellokoding.jpa.bidirectional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.validation.Valid; +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/books") +public class BookController { + private final BookRepository bookRepository; + private final LibraryRepository libraryRepository; + + @Autowired + public BookController(BookRepository bookRepository, LibraryRepository libraryRepository) { + this.bookRepository = bookRepository; + this.libraryRepository = libraryRepository; + } + + @PostMapping + public ResponseEntity create(@RequestBody @Valid Book book) { + Optional optionalLibrary = libraryRepository.findById(book.getLibrary().getId()); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + book.setLibrary(optionalLibrary.get()); + + Book savedBook = bookRepository.save(book); + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") + .buildAndExpand(savedBook.getId()).toUri(); + + return ResponseEntity.created(location).body(savedBook); + } + + @PutMapping("/{id}") + public ResponseEntity update(@RequestBody @Valid Book book, @PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(book.getLibrary().getId()); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + book.setLibrary(optionalLibrary.get()); + book.setId(optionalBook.get().getId()); + bookRepository.save(book); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + bookRepository.delete(optionalBook.get()); + + return ResponseEntity.noContent().build(); + } + + @GetMapping + public ResponseEntity> getAll(Pageable pageable) { + return ResponseEntity.ok(bookRepository.findAll(pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + return ResponseEntity.ok(optionalBook.get()); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/BookRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/BookRepository.java similarity index 63% rename from jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/BookRepository.java rename to jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/BookRepository.java index daac871f..1cab1362 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/BookRepository.java +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/BookRepository.java @@ -1,6 +1,5 @@ -package com.hellokoding.jpa.repository; +package com.hellokoding.jpa.bidirectional; -import com.hellokoding.jpa.model.Book; import org.springframework.data.jpa.repository.JpaRepository; public interface BookRepository extends JpaRepository{ diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Library.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Library.java new file mode 100644 index 00000000..4605b9c6 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/Library.java @@ -0,0 +1,47 @@ +package com.hellokoding.jpa.bidirectional; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.util.HashSet; +import java.util.Set; + +@Entity +public class Library { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + private String name; + + @OneToMany(mappedBy = "library", cascade = CascadeType.ALL) + private Set books = new HashSet<>(); + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getBooks() { + return books; + } + + public void setBooks(Set books) { + this.books = books; + + for(Book b : books) { + b.setLibrary(this); + } + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/LibraryController.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/LibraryController.java new file mode 100644 index 00000000..39fc5d38 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/LibraryController.java @@ -0,0 +1,74 @@ +package com.hellokoding.jpa.bidirectional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.validation.Valid; +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/libraries") +public class LibraryController { + private final LibraryRepository libraryRepository; + private final BookRepository bookRepository; + + @Autowired + public LibraryController(LibraryRepository libraryRepository, BookRepository bookRepository) { + this.libraryRepository = libraryRepository; + this.bookRepository = bookRepository; + } + + @PostMapping + public ResponseEntity create(@Valid @RequestBody Library library) { + Library savedLibrary = libraryRepository.save(library); + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") + .buildAndExpand(savedLibrary.getId()).toUri(); + + return ResponseEntity.created(location).body(savedLibrary); + } + + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Integer id, @Valid @RequestBody Library library) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + library.setId(optionalLibrary.get().getId()); + libraryRepository.save(library); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + libraryRepository.delete(optionalLibrary.get()); + + return ResponseEntity.noContent().build(); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + return ResponseEntity.ok(optionalLibrary.get()); + } + + @GetMapping + public ResponseEntity> getAll(Pageable pageable) { + return ResponseEntity.ok(libraryRepository.findAll(pageable)); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/LibraryRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/LibraryRepository.java new file mode 100644 index 00000000..8a30d478 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/bidirectional/LibraryRepository.java @@ -0,0 +1,6 @@ +package com.hellokoding.jpa.bidirectional; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface LibraryRepository extends JpaRepository{ +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Application.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Application.java new file mode 100644 index 00000000..48827bd5 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Application.java @@ -0,0 +1,11 @@ +package com.hellokoding.jpa.unidirectional; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Book.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Book.java new file mode 100644 index 00000000..adc0ef36 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Book.java @@ -0,0 +1,45 @@ +package com.hellokoding.jpa.unidirectional; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +@Entity +public class Book { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + private String name; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Library library; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Library getLibrary() { + return library; + } + + public void setLibrary(Library library) { + this.library = library; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/BookController.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/BookController.java new file mode 100644 index 00000000..abb407df --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/BookController.java @@ -0,0 +1,92 @@ +package com.hellokoding.jpa.unidirectional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.validation.Valid; +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/books") +public class BookController { + private final BookRepository bookRepository; + private final LibraryRepository libraryRepository; + + @Autowired + public BookController(BookRepository bookRepository, LibraryRepository libraryRepository) { + this.bookRepository = bookRepository; + this.libraryRepository = libraryRepository; + } + + @PostMapping + public ResponseEntity create(@RequestBody @Valid Book book) { + Optional optionalLibrary = libraryRepository.findById(book.getLibrary().getId()); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + book.setLibrary(optionalLibrary.get()); + + Book savedBook = bookRepository.save(book); + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") + .buildAndExpand(savedBook.getId()).toUri(); + + return ResponseEntity.created(location).body(savedBook); + } + + @PutMapping("/{id}") + public ResponseEntity update(@RequestBody @Valid Book book, @PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(book.getLibrary().getId()); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + book.setLibrary(optionalLibrary.get()); + book.setId(optionalBook.get().getId()); + bookRepository.save(book); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + bookRepository.delete(optionalBook.get()); + + return ResponseEntity.noContent().build(); + } + + @GetMapping + public ResponseEntity> getAll(Pageable pageable) { + return ResponseEntity.ok(bookRepository.findAll(pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + return ResponseEntity.ok(optionalBook.get()); + } + + @GetMapping("/library/{libraryId}") + public ResponseEntity> getByLibraryId(@PathVariable Integer libraryId, Pageable pageable) { + return ResponseEntity.ok(bookRepository.findByLibraryId(libraryId, pageable)); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/BookRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/BookRepository.java new file mode 100644 index 00000000..36822017 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/BookRepository.java @@ -0,0 +1,18 @@ +package com.hellokoding.jpa.unidirectional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +import javax.transaction.Transactional; + +public interface BookRepository extends JpaRepository{ + Page findByLibraryId(Integer libraryId, Pageable pageable); + + @Modifying + @Transactional + @Query("DELETE FROM Book b WHERE b.library.id = ?1") + void deleteByLibraryId(Integer libraryId); +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Library.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Library.java new file mode 100644 index 00000000..bb260488 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/Library.java @@ -0,0 +1,33 @@ +package com.hellokoding.jpa.unidirectional; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.validation.constraints.NotNull; + +@Entity +public class Library { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/LibraryController.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/LibraryController.java new file mode 100644 index 00000000..934d82e0 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/LibraryController.java @@ -0,0 +1,81 @@ +package com.hellokoding.jpa.unidirectional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.transaction.Transactional; +import javax.validation.Valid; +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/libraries") +public class LibraryController { + private final LibraryRepository libraryRepository; + private final BookRepository bookRepository; + + @Autowired + public LibraryController(LibraryRepository libraryRepository, BookRepository bookRepository) { + this.libraryRepository = libraryRepository; + this.bookRepository = bookRepository; + } + + @PostMapping("/") + public ResponseEntity create(@Valid @RequestBody Library library) { + Library savedLibrary = libraryRepository.save(library); + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") + .buildAndExpand(savedLibrary.getId()).toUri(); + + return ResponseEntity.created(location).body(savedLibrary); + } + + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Integer id, @Valid @RequestBody Library library) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + library.setId(optionalLibrary.get().getId()); + libraryRepository.save(library); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + deleteLibraryInTransaction(optionalLibrary.get()); + + return ResponseEntity.noContent().build(); + } + + @Transactional + public void deleteLibraryInTransaction(Library library) { + bookRepository.deleteByLibraryId(library.getId()); + libraryRepository.delete(library); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + return ResponseEntity.ok(optionalLibrary.get()); + } + + @GetMapping("/") + public ResponseEntity> getAll(Pageable pageable) { + return ResponseEntity.ok(libraryRepository.findAll(pageable)); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/LibraryRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/LibraryRepository.java new file mode 100644 index 00000000..5289209c --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/java/com/hellokoding/jpa/unidirectional/LibraryRepository.java @@ -0,0 +1,6 @@ +package com.hellokoding.jpa.unidirectional; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface LibraryRepository extends JpaRepository{ +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/resources/application.properties new file mode 100644 index 00000000..047b482b --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql-rest/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true +spring.datasource.username=root +spring.datasource.password=hellokoding + +spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect +spring.jpa.hibernate.ddl-auto=create +spring.jpa.show-sql=true diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/docker-compose.yml b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/docker-compose.yml index feebb764..2d76d303 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/docker-compose.yml +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: hk-mysql: container_name: hk-mysql - image: mysql/mysql-server:5.7 + image: mysql/mysql-server:8.0 environment: MYSQL_DATABASE: test MYSQL_ROOT_PASSWORD: hellokoding diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/pom.xml b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/pom.xml index e839224c..546d7e4b 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.2.6.RELEASE @@ -29,10 +29,6 @@ mysql-connector-java runtime - - org.projectlombok - lombok - diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java index 564170b8..98b35447 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java @@ -1,25 +1,73 @@ package com.hellokoding.jpa; -import com.hellokoding.jpa.book.Book; -import com.hellokoding.jpa.book.BookCategory; -import com.hellokoding.jpa.book.BookCategoryRepository; -import org.springframework.beans.factory.annotation.Autowired; +import com.hellokoding.jpa.book.BookService; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import javax.persistence.*; + @SpringBootApplication public class JpaApplication implements CommandLineRunner { - @Autowired - private BookCategoryRepository bookCategoryRepository; + private final BookService bookService; - public static void main(String[] args) { - SpringApplication.run(JpaApplication.class, args); + public JpaApplication(BookService bookService) { + this.bookService = bookService; } @Override public void run(String... args) { - // Create a couple of Book and BookCategory - bookCategoryRepository.save(new BookCategory("Category 1", new Book("Hello Koding 1"), new Book("Hello Koding 2"))); + bookService.create(); + bookService.read(); + bookService.delete(); + } + + public static void main(String[] args) { + SpringApplication.run(JpaApplication.class, args); + } + + @Entity + public static class Book { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + private String name; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "book_category_id", referencedColumnName = "id") + private BookCategory bookCategory; + + public Book() { + } + + public Book(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BookCategory getBookCategory() { + return bookCategory; + } + + public void setBookCategory(BookCategory bookCategory) { + this.bookCategory = bookCategory; + bookCategory.getBooks().add(this); + } } } diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/Book.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/Book.java index c44570d2..a18af999 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/Book.java +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/Book.java @@ -1,11 +1,7 @@ package com.hellokoding.jpa.book; -import lombok.*; - import javax.persistence.*; -@Data - @Entity public class Book { @Id @@ -14,11 +10,39 @@ public class Book { private String name; - @ManyToOne - @JoinColumn + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "book_category_id", referencedColumnName = "id") private BookCategory bookCategory; + public Book() { + } + public Book(String name) { this.name = name; } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BookCategory getBookCategory() { + return bookCategory; + } + + public void setBookCategory(BookCategory bookCategory) { + this.bookCategory = bookCategory; + bookCategory.getBooks().add(this); + } } diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookCategory.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookCategory.java index 4580e999..cca9a415 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookCategory.java +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookCategory.java @@ -1,30 +1,59 @@ package com.hellokoding.jpa.book; -import lombok.Data; -import lombok.EqualsAndHashCode; - import javax.persistence.*; +import java.util.HashSet; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@Data -@EqualsAndHashCode(exclude = "books") -@Entity +@Entity(name = "BookCategory") +@Table(name = "book_category") public class BookCategory { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; + @Column(name = "category_name") private String name; - @OneToMany(mappedBy = "bookCategory", cascade = CascadeType.ALL) + @OneToMany( + mappedBy = "bookCategory", + cascade = CascadeType.PERSIST, + fetch = FetchType.LAZY + ) private Set books; - public BookCategory(String name, Book... books) { + public BookCategory() { + books = new HashSet<>(); + } + + public BookCategory(String name) { this.name = name; - this.books = Stream.of(books).collect(Collectors.toSet()); - this.books.forEach(x -> x.setBookCategory(this)); + books = new HashSet<>(); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getBooks() { + return books; + } + + public void setBooks(Set books) { + this.books = books; + for (Book book : books) { + book.setBookCategory(this); + } } } diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java index e2e14a22..dece3f2c 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java @@ -1,6 +1,23 @@ package com.hellokoding.jpa.book; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; +import java.util.List; + +@Transactional(readOnly = true) public interface BookRepository extends JpaRepository{ + @EntityGraph(attributePaths = "bookCategory") + List findFirst10ByOrderByNameAsc(); + + @Modifying + @Transactional + @Query("DELETE FROM Book b WHERE b.bookCategory.id = ?1") + void deleteInBulkByCategoryId(int categoryId); + + @Transactional + void deleteByBookCategoryId(int categoryId); } diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookService.java b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookService.java new file mode 100644 index 00000000..36a43183 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/java/com/hellokoding/jpa/book/BookService.java @@ -0,0 +1,43 @@ +package com.hellokoding.jpa.book; + +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class BookService { + private final BookCategoryRepository bookCategoryRepository; + private final BookRepository bookRepository; + + public BookService(BookCategoryRepository bookCategoryRepository, + BookRepository bookRepository) { + this.bookCategoryRepository = bookCategoryRepository; + this.bookRepository = bookRepository; + } + + public void create() { + List bookCategories = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + BookCategory bookCategory = new BookCategory("Category " + (i+1)); + for (int j = 0; j < 2; j++) { + Book book = new Book(String.format("Book %s.%s", (i+1), (j+1))); + book.setBookCategory(bookCategory); + } + bookCategories.add(bookCategory); + } + + bookCategoryRepository.saveAll(bookCategories); + } + + public void read() { + List books = bookRepository.findFirst10ByOrderByNameAsc(); + books.forEach(b -> System.out.println( + String.format("%s, %s", b.getName(), b.getBookCategory().getName()) + )); + } + + public void delete() { + bookRepository.deleteInBulkByCategoryId(1); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/resources/application.properties index 4067a1ea..18a932ed 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-many-mysql/src/main/resources/application.properties @@ -1,9 +1,8 @@ -spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true spring.datasource.username=root spring.datasource.password=hellokoding -spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.hibernate.ddl-auto=create -spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect -spring.jpa.generate-ddl=true spring.jpa.show-sql=true +spring.jpa.properties.hibernate.generate_statistics=true \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/pom.xml b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/pom.xml index 6b67b7a0..fe8cc5f4 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/pom.xml @@ -27,10 +27,15 @@ - org.hsqldb - hsqldb + mysql + mysql-connector-java runtime + + + org.projectlombok + lombok + diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/Application.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/Application.java index 22bcb4e0..fca4faa8 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/Application.java +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/Application.java @@ -1,41 +1,37 @@ package com.hellokoding.jpa; -import com.hellokoding.jpa.model.Book; -import com.hellokoding.jpa.model.BookDetail; -import com.hellokoding.jpa.repository.BookRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import com.hellokoding.jpa.model.IDCard; +import com.hellokoding.jpa.model.Person; +import com.hellokoding.jpa.repository.IDCardRepository; +import com.hellokoding.jpa.repository.PersonRepository; +import lombok.RequiredArgsConstructor; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import java.util.ArrayList; -import java.util.List; +import javax.transaction.Transactional; +import java.util.Arrays; +@RequiredArgsConstructor @SpringBootApplication public class Application implements CommandLineRunner { - private static final Logger logger = LoggerFactory.getLogger(Application.class); - - @Autowired - private BookRepository bookRepository; + private final IDCardRepository idCardRepository; + private final PersonRepository personRepository; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override - public void run(String... strings) throws Exception { - // save a couple of books - List books = new ArrayList<>(); - books.add(new Book("Book A", new BookDetail(49))); - books.add(new Book("Book B", new BookDetail(59))); - books.add(new Book("Book C", new BookDetail(69))); - bookRepository.saveAll(books); + @Transactional + public void run(String... strings) { + Person p1 = new Person("Tom"); + Person p2 = new Person("Daisy"); + Person p3 = new Person("Alex"); + personRepository.saveAll(Arrays.asList(p1, p2, p3)); - // fetch all books - for (Book book : bookRepository.findAll()) { - logger.info(book.toString()); - } + idCardRepository.save(new IDCard(p1)); + idCardRepository.save(new IDCard(p2)); + idCardRepository.save(new IDCard(p3)); } } diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/Book.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/Book.java deleted file mode 100644 index af38277b..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/Book.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hellokoding.jpa.model; - -import javax.persistence.*; - -@Entity -public class Book { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private int id; - - private String name; - - @OneToOne(cascade = CascadeType.ALL) - @JoinColumn(name = "book_detail_id") - private BookDetail bookDetail; - - public Book(){ - - } - - public Book(String name){ - this.name = name; - } - - public Book(String name, BookDetail bookDetail){ - this.name = name; - this.bookDetail = bookDetail; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public BookDetail getBookDetail() { - return bookDetail; - } - - public void setBookDetail(BookDetail bookDetail) { - this.bookDetail = bookDetail; - } - - @Override - public String toString() { - return String.format( - "Book[id=%d, name='%s', number of pages='%d']", - id, name, bookDetail.getNumberOfPages()); - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/BookDetail.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/BookDetail.java deleted file mode 100644 index 67ff45aa..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/BookDetail.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.hellokoding.jpa.model; - -import javax.persistence.*; - -@Entity -@Table(name = "book_detail") -public class BookDetail { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - @Column(name = "number_of_pages") - private Integer numberOfPages; - - @OneToOne(mappedBy = "bookDetail") - private Book book; - - public BookDetail(){ - - } - - public BookDetail(Integer numberOfPages){ - this.numberOfPages = numberOfPages; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public Integer getNumberOfPages() { - return numberOfPages; - } - - public void setNumberOfPages(Integer numberOfPages) { - this.numberOfPages = numberOfPages; - } - - public Book getBook() { - return book; - } - - public void setBook(Book book) { - this.book = book; - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/IDCard.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/IDCard.java new file mode 100644 index 00000000..c47135fe --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/IDCard.java @@ -0,0 +1,26 @@ +package com.hellokoding.jpa.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.util.UUID; + +@Getter @Setter +@Entity +public class IDCard { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int personId; + + @OneToOne(cascade = CascadeType.ALL, optional = false) + @PrimaryKeyJoinColumn(name = "person_id", referencedColumnName = "id") + private Person person; + + @Column(unique = true, nullable = false) + private String code = UUID.randomUUID().toString(); + + public IDCard(Person person) { + this.person = person; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/Person.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/Person.java new file mode 100644 index 00000000..b135cbe4 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/model/Person.java @@ -0,0 +1,20 @@ +package com.hellokoding.jpa.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Getter @Setter +@Entity +public class Person { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + private String name; + + public Person(String name) { + this.name = name; + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/IDCardRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/IDCardRepository.java new file mode 100644 index 00000000..4c34e3a2 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/IDCardRepository.java @@ -0,0 +1,7 @@ +package com.hellokoding.jpa.repository; + +import com.hellokoding.jpa.model.IDCard; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface IDCardRepository extends JpaRepository{ +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java new file mode 100644 index 00000000..20a7a505 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java @@ -0,0 +1,7 @@ +package com.hellokoding.jpa.repository; + +import com.hellokoding.jpa.model.Person; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PersonRepository extends JpaRepository{ +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/resources/application.properties index e50c4d7d..18a5333b 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-hsql/src/main/resources/application.properties @@ -1,2 +1,9 @@ -spring.jpa.hibernate.ddl-auto=create-drop -spring.jpa.show-sql=true \ No newline at end of file +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false +spring.datasource.username=root +spring.datasource.password=hellokoding +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + +spring.jpa.hibernate.ddl-auto=create +spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect +spring.jpa.generate-ddl=true +spring.jpa.show-sql=true diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/pom.xml b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/pom.xml index c66534be..b295daec 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.3.4.RELEASE diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/Application.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/Application.java index da761588..8b5ad80f 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/Application.java +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/Application.java @@ -1,26 +1,32 @@ package com.hellokoding.jpa; -import com.hellokoding.jpa.book.Address; -import com.hellokoding.jpa.book.Library; -import com.hellokoding.jpa.book.LibraryRepository; -import lombok.RequiredArgsConstructor; +import com.hellokoding.jpa.model.IDCard; +import com.hellokoding.jpa.model.Person; +import com.hellokoding.jpa.repository.PersonRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -@RequiredArgsConstructor +import java.util.ArrayList; +import java.util.List; + @SpringBootApplication public class Application implements CommandLineRunner { - private final LibraryRepository libraryRepository; + @Autowired + private PersonRepository personRepository; public static void main(String[] args) { - SpringApplication.run(Application.class, args); + SpringApplication.run(com.hellokoding.jpa.Application.class, args); } @Override - public void run(String... args) { - // Create a couple of Library and Address - libraryRepository.save(new Library("Library 1", new Address("Street 1", "Zip 1"))); - libraryRepository.save(new Library("Library 2", new Address("Street 2", "Zip 2"))); + public void run(String... strings) throws Exception { + // save idCard along with persons + List persons = new ArrayList<>(); + persons.add(new Person("Tom", new IDCard())); + persons.add(new Person("Daisy", new IDCard())); + persons.add(new Person("Alex", new IDCard())); + personRepository.saveAll(persons); } } diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/Address.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/Address.java deleted file mode 100644 index b4b2a149..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/Address.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.hellokoding.jpa.book; - -import lombok.Data; - -import javax.persistence.*; - -@Data - -@Entity -public class Address { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - private String street; - - private String zipCode; - - @OneToOne(mappedBy = "address") - private Library library; - - public Address(String street, String zipCode) { - this.street = street; - this.zipCode = zipCode; - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/Library.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/Library.java deleted file mode 100644 index 47d019e5..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/Library.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.hellokoding.jpa.book; - -import lombok.Data; - -import javax.persistence.*; - -@Data - -@Entity -public class Library { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; - - private String name; - - @OneToOne(cascade = CascadeType.ALL) - @JoinColumn(unique = true) - private Address address; - - public Library(String name, Address address) { - this.name = name; - this.address = address; - this.address.setLibrary(this); - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/model/IDCard.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/model/IDCard.java new file mode 100644 index 00000000..2f118e62 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/model/IDCard.java @@ -0,0 +1,21 @@ +package com.hellokoding.jpa.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.util.UUID; + +@Getter @Setter +@Entity +public class IDCard { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(unique = true, nullable = false) + private String code = UUID.randomUUID().toString(); + + @OneToOne(mappedBy = "idCard", optional = false) + private Person person; +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/model/Person.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/model/Person.java new file mode 100644 index 00000000..f874b9f3 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/model/Person.java @@ -0,0 +1,27 @@ +package com.hellokoding.jpa.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +public class Person { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + private String name; + + @OneToOne(cascade = CascadeType.ALL, optional = false) + @JoinColumn(name = "id_card_id") + private IDCard idCard; + + public Person(String name, IDCard idCard) { + this.name = name; + this.idCard = idCard; + this.idCard.setPerson(this); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java new file mode 100644 index 00000000..20a7a505 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java @@ -0,0 +1,7 @@ +package com.hellokoding.jpa.repository; + +import com.hellokoding.jpa.model.Person; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PersonRepository extends JpaRepository{ +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/resources/application.properties index 4d22fe2a..18a5333b 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/resources/application.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=hellokoding spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/pom.xml b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/pom.xml index af5f36bb..553f0065 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/pom.xml +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.2.6.RELEASE diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/Application.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/Application.java new file mode 100644 index 00000000..c9dacd93 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/Application.java @@ -0,0 +1,32 @@ +package com.hellokoding.jpa; + +import com.hellokoding.jpa.model.IDCard; +import com.hellokoding.jpa.model.Person; +import com.hellokoding.jpa.repository.PersonRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.util.ArrayList; +import java.util.List; + +@SpringBootApplication +public class Application implements CommandLineRunner { + @Autowired + private PersonRepository personRepository; + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Override + public void run(String... strings) throws Exception { + // save idCard along with persons + List persons = new ArrayList<>(); + persons.add(new Person("Tom", new IDCard())); + persons.add(new Person("Daisy", new IDCard())); + persons.add(new Person("Alex", new IDCard())); + personRepository.saveAll(persons); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java deleted file mode 100644 index 4ae5d460..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/JpaApplication.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.hellokoding.jpa; - -import com.hellokoding.jpa.book.Book; -import com.hellokoding.jpa.book.BookDetail; -import com.hellokoding.jpa.book.BookRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@RequiredArgsConstructor -@SpringBootApplication -public class JpaApplication implements CommandLineRunner { - private final BookRepository bookRepository; - - public static void main(String[] args) { - SpringApplication.run(JpaApplication.class, args); - } - - @Override - public void run(String... args) { - // Create a couple of Book and BookDetail - bookRepository.save(new Book("Hello Koding 1", new BookDetail(101))); - bookRepository.save(new Book("Hello Koding 2", new BookDetail(102))); - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookDetail.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookDetail.java deleted file mode 100644 index 2fc4649e..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookDetail.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.hellokoding.jpa.book; - -import lombok.Data; - -import javax.persistence.*; - -@Data - -@Entity -public class BookDetail { - @Id - private Integer id; - - @OneToOne - @JoinColumn - @MapsId - private Book book; - - private int numberOfPages; - - public BookDetail(int numberOfPages) { - this.numberOfPages = numberOfPages; - } -} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookDetailRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookDetailRepository.java deleted file mode 100644 index 00306937..00000000 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookDetailRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hellokoding.jpa.book; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface BookDetailRepository extends JpaRepository{ -} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/model/IDCard.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/model/IDCard.java new file mode 100644 index 00000000..dbf3cb1d --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/model/IDCard.java @@ -0,0 +1,23 @@ +package com.hellokoding.jpa.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.util.UUID; + +@Getter @Setter +@Entity +public class IDCard { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @OneToOne(optional = false) + @JoinColumn(name = "person_id") + @MapsId + private Person person; + + @Column(unique = true, nullable = false) + private String code = UUID.randomUUID().toString(); +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/model/Person.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/model/Person.java new file mode 100644 index 00000000..fa9a1066 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/model/Person.java @@ -0,0 +1,26 @@ +package com.hellokoding.jpa.model; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@Entity +public class Person { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + private String name; + + @OneToOne(cascade = CascadeType.ALL, mappedBy = "person") + private IDCard idCard; + + public Person(String name, IDCard idCard) { + this.name = name; + this.idCard = idCard; + this.idCard.setPerson(this); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java new file mode 100644 index 00000000..20a7a505 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/repository/PersonRepository.java @@ -0,0 +1,7 @@ +package com.hellokoding.jpa.repository; + +import com.hellokoding.jpa.model.Person; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PersonRepository extends JpaRepository{ +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/resources/application.properties index 4067a1ea..d0959338 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/resources/application.properties +++ b/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/resources/application.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=hellokoding spring.datasource.driver-class-name=com.mysql.jdbc.Driver diff --git a/jpa-hibernate-examples/jpa-hibernate-problems/Dockerfile b/jpa-hibernate-examples/jpa-hibernate-problems/Dockerfile new file mode 100644 index 00000000..6ae65e4e --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-problems/Dockerfile @@ -0,0 +1 @@ +FROM maven:3.5-jdk-8 \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-problems/docker-compose.yml b/jpa-hibernate-examples/jpa-hibernate-problems/docker-compose.yml new file mode 100644 index 00000000..2d76d303 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-problems/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' +services: + hk-mysql: + container_name: hk-mysql + image: mysql/mysql-server:8.0 + environment: + MYSQL_DATABASE: test + MYSQL_ROOT_PASSWORD: hellokoding + MYSQL_ROOT_HOST: '%' + ports: + - "3306:3306" + restart: always + + app: + build: . + volumes: + - .:/app + - ~/.m2:/root/.m2 + working_dir: /app + command: mvn clean spring-boot:run + depends_on: + - hk-mysql diff --git a/jpa-hibernate-examples/jpa-hibernate-problems/pom.xml b/jpa-hibernate-examples/jpa-hibernate-problems/pom.xml new file mode 100644 index 00000000..ffc370af --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-problems/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + com.hellokoding.jpa + jpa-hibernate-problems + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + runtime + + + org.projectlombok + lombok + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/BookApplication.java b/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/BookApplication.java new file mode 100644 index 00000000..d118a922 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/BookApplication.java @@ -0,0 +1,115 @@ +package com.hellokoding.jpa; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static javax.persistence.CascadeType.*; +import static javax.persistence.FetchType.*; + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class Book { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + String name; + @ManyToOne(fetch = EAGER) BookCategory cat; + //@OneToOne(cascade = PERSIST) BookDetail detail; +} + +//@Entity @Builder @AllArgsConstructor @NoArgsConstructor +//class BookDetail { +// @Id @MapsId("book_id") int id; +// int numberOfPages; +//} + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class BookCategory { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + String name; + @OneToMany(mappedBy = "cat", cascade = PERSIST, fetch = EAGER) + @Builder.Default Set books = new HashSet<>(); +} + +interface BookCategoryRepository extends JpaRepository { + List findFirst10ByOrderByName(); +} + +interface BookRepository extends JpaRepository{ + //@EntityGraph(attributePaths = "bookCategory") + List findFirst10ByOrderByNameAsc(); +} + +@Service @RequiredArgsConstructor +class BookService { + private final BookCategoryRepository bookCategoryRepository; + private final BookRepository bookRepository; + + public void create() { + Stream bookCategories = IntStream.range(1, 11).mapToObj(i -> { + BookCategory bookCategory = BookCategory.builder().name("Cat " + i).build(); + IntStream.range(1, 3).forEach(j -> { + Book book = Book.builder() + .name(String.format("Book %d.%d", i, j)) + .cat(bookCategory) + //.detail(BookDetail.builder().numberOfPages(i*100).build()) + .build(); + + bookCategory.books.add(book); + }); + + return bookCategory; + }); + + bookCategoryRepository.saveAll(bookCategories.collect(Collectors.toList())); + } + + public void findCategories() { + bookCategoryRepository.findFirst10ByOrderByName().forEach(c -> { + System.out.println(c.name); + c.books.forEach(b -> { + System.out.println(b.name + " " + b.cat.name); + }); + }); + } + + public List findBookNames() { + List books = bookRepository.findFirst10ByOrderByNameAsc(); + + books.forEach(b -> { + System.out.println(b.name + " " + b.cat.name); + }); + + return books.stream() + .map(b -> b.name) + .collect(Collectors.toList()); + } +} + +@SpringBootApplication @RequiredArgsConstructor +public class BookApplication implements CommandLineRunner { + private final BookService bookService; + + @Override + public void run(String... args) { + bookService.create(); + bookService.findCategories(); + //bookService.findBookNames().forEach(System.out::println); + } + + public static void main(String[] args) { + SpringApplication.run(BookApplication.class, args); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/CompanyApplication.java b/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/CompanyApplication.java new file mode 100644 index 00000000..560898b6 --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/CompanyApplication.java @@ -0,0 +1,252 @@ +package com.hellokoding.jpa; + +import lombok.*; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.CascadeType.PERSIST; +import static javax.persistence.FetchType.EAGER; +import static javax.persistence.FetchType.LAZY; + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class Company { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + + String name; + + @OneToMany(fetch = LAZY, mappedBy = "company", cascade = ALL) + @Builder.Default Set employees = new HashSet<>(); + + @OneToMany(fetch = LAZY, mappedBy = "company", cascade = ALL) + @Builder.Default Set companyOwners = new HashSet<>(); + + @OneToMany(fetch = LAZY, mappedBy = "company", cascade = ALL) + @Builder.Default Set departments = new HashSet<>(); + + @OneToOne(fetch = LAZY, cascade = PERSIST) Address address; +} + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class Address { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + + String address; + + @OneToOne(mappedBy = "address", fetch = EAGER) Company company; +} + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class Employee { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + + String name; + + @ManyToOne Company company; +} + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class Owner { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + + String name; + + @OneToMany(mappedBy = "owner", cascade = ALL) + @Builder.Default Set companyOwners = new HashSet<>(); +} + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class CompanyOwner implements Serializable { + @Id @ManyToOne @JoinColumn Company company; + + @Id @ManyToOne @JoinColumn Owner owner; + + int noOfStocks; +} + +@Value +@Builder @AllArgsConstructor +class CompanyIdName { + Integer id; + String name; + List employees; + + static CompanyIdName of(Company company) { + return CompanyIdName.builder() + .id(company.id) + .name(company.name) + .employees(company.employees.stream() + .map(EmployeeIdName::of) + .collect(Collectors.toList())) + .build(); + } +} + +@Builder @AllArgsConstructor +class EmployeeIdName { + Integer id; + String name; + + static EmployeeIdName of(Employee employee) { + return EmployeeIdName.builder() + .id(employee.id) + .name(employee.name) + .build(); + } +} + +interface CompanyProjection { + int getId(); + String getName(); + List getEmployees(); +} + +interface EmployeeProjection { + int getId(); + String getName(); +} + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class Department { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + + String name; + + @ManyToOne Company company; +} + +interface CompanyRepository extends JpaRepository { + List findFirst10ByOrderByNameAsc(); + + @EntityGraph(attributePaths = "employees") + List findFirst10ByOrderByNameDesc(); + + @Query(value="FROM Company c LEFT JOIN FETCH c.employees") + List findWithEmployeesAndJoinFetch(Pageable pageable); + + @EntityGraph(attributePaths = "employees") + List findFirst10ByOrderByIdDesc(); + + @EntityGraph(attributePaths = "employees") + List findFirst10ByOrderByIdAsc(); +} + +interface EmployeeRepository extends JpaRepository { + @Modifying + @Transactional + @Query("DELETE FROM Employee e WHERE e.company.id = ?1") + void deleteInBulkByCompanyId(int companyId); + + @Transactional + void deleteByCompanyId(int categoryId); +} + +interface OwnerRepository extends JpaRepository { +} + +@Service +@RequiredArgsConstructor +class CompanyService { + final CompanyRepository companyRepository; + final OwnerRepository ownerRepository; + + public void create() { + Stream companies = IntStream.range(1, 11).mapToObj(i -> { + Company c = Company.builder().name("Company " + i).build(); + + IntStream.range(1, 3).forEach(j -> { + Employee q = Employee.builder().name(String.format("Employee %d.%d", i, j)) + .company(c).build(); + c.employees.add(q); + }); + + IntStream.range(1, 3).forEach(j -> { + Department d = Department.builder().name(String.format("Department %d.%d", i, j)) + .company(c).build(); + c.departments.add(d); + }); + + IntStream.range(1, 3).forEach(j -> { + Owner o = Owner.builder().name(String.format("Owner %d.%d", i, j)).build(); + ownerRepository.save(o); + + CompanyOwner co = CompanyOwner.builder().owner(o).company(c).noOfStocks(i*100).build(); + o.companyOwners.add(co); + c.companyOwners.add(co); + }); + + c.address = Address.builder().address("Address " + i).company(c).build(); + + return c; + }); + + companyRepository.saveAll(companies.collect(Collectors.toList())); + } + + public List findNames() { + return companyRepository.findFirst10ByOrderByNameAsc().stream() + .map(c -> c.name).collect(Collectors.toList()); + } + + @Transactional + public List findCompanyIdNames() { + return companyRepository.findFirst10ByOrderByNameAsc().stream() + .map(CompanyIdName::of) + .collect(Collectors.toList()); + } + + @Transactional + public List findCompanyIdNamesWithDTO() { + return companyRepository.findFirst10ByOrderByIdDesc(); + } +} + +@Service +@RequiredArgsConstructor +class EmployeeService { + final EmployeeRepository employeeRepository; + + public void delete() { + employeeRepository.deleteByCompanyId(1); + } +} + +@SpringBootApplication @RequiredArgsConstructor +public class CompanyApplication implements CommandLineRunner { + private final CompanyService companyService; + private final EmployeeService employeeService; + + public void run(String... args) { + companyService.create(); + //companyService.findNames().forEach(System.out::println); + //companyService.findCompanyIdNamesWithDTO(); +// companyService.findCompanyIdNamesWithDTO().forEach(c -> { +// System.out.println(c.getName()); +// c.getEmployees().forEach(e -> { +// System.out.println(e.getName()); +// }); +// }); + employeeService.delete(); + } + + public static void main(String[] args) { + SpringApplication.run(QuoteApplication.class, args); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/QuoteApplication.java b/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/QuoteApplication.java new file mode 100644 index 00000000..adf3962e --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-problems/src/main/java/com/hellokoding/jpa/QuoteApplication.java @@ -0,0 +1,137 @@ +package com.hellokoding.jpa; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Service; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static javax.persistence.CascadeType.PERSIST; +import static javax.persistence.FetchType.LAZY; + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class Author { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + String name; + @OneToMany(fetch = LAZY, mappedBy = "author", cascade = PERSIST) + @Builder.Default Set quotes = new HashSet<>(); +} + +interface AuthorProjection { + Integer getId(); + String getName(); +} + +@AllArgsConstructor +class AuthorIdName { + Integer id; + String name; +} + +interface AuthorRepository extends JpaRepository {} + +@Entity @Builder @AllArgsConstructor @NoArgsConstructor +class Quote { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) int id; + String name; + @ManyToOne(fetch = LAZY) Author author; +} + +interface QuoteProjection { + Integer getId(); + String getName(); + AuthorProjection getAuthor(); +} + +@AllArgsConstructor +class QuoteWithAuthor { + Integer id; + String name; + AuthorIdName author; + + static List of(List quotes) { + return quotes.stream().map(q -> { + AuthorIdName ain = new AuthorIdName(q.author.id, q.author.name); + return new QuoteWithAuthor(q.id, q.name, ain); + }).collect(Collectors.toList()); + } +} + +interface QuoteRepository extends JpaRepository { + @EntityGraph(attributePaths = "author") + List findFirst10ByOrderByName(); + + @Query(value="FROM Quote q LEFT JOIN FETCH q.author") + List findWithAuthorAndJoinFetch(Pageable pageable); + + List findFirst10ByOrderByNameDesc(); +} + +@Service @RequiredArgsConstructor +class QuoteService { + private final AuthorRepository authorRepository; + private final QuoteRepository quoteRepository; + + public void create() { + Stream authors = IntStream.range(1, 11).mapToObj(i -> { + Author a = Author.builder().name("Author " + i).build(); + IntStream.range(1, 3).forEach(j -> { + Quote q = Quote.builder().name(String.format("Quote %d.%d", i, j)) + .author(a).build(); + a.quotes.add(q); + }); + + return a; + }); + + authorRepository.saveAll(authors.collect(Collectors.toList())); + } + + public List findWithLazy() { + return QuoteWithAuthor.of(quoteRepository.findAll(PageRequest.of(0, 10)).toList()); + } + + public List findWithEntityGraph() { + return QuoteWithAuthor.of(quoteRepository.findFirst10ByOrderByName()); + } + + public List findWithJoinFetch() { + return QuoteWithAuthor.of(quoteRepository.findWithAuthorAndJoinFetch(PageRequest.of(0, 10))); + } + + public List findWithAuthorProjection() { + return quoteRepository.findFirst10ByOrderByNameDesc(); + } +} + +@SpringBootApplication @RequiredArgsConstructor +public class QuoteApplication implements CommandLineRunner { + private final QuoteService quoteService; + + public void run(String... args) { + quoteService.create(); + quoteService.findWithAuthorProjection().forEach(q -> { + System.out.println(q.getName() + " " + q.getAuthor().getName()); + }); + } + + public static void main(String[] args) { + SpringApplication.run(QuoteApplication.class, args); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-problems/src/main/resources/application.properties b/jpa-hibernate-examples/jpa-hibernate-problems/src/main/resources/application.properties new file mode 100644 index 00000000..18a932ed --- /dev/null +++ b/jpa-hibernate-examples/jpa-hibernate-problems/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true +spring.datasource.username=root +spring.datasource.password=hellokoding + +spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect +spring.jpa.hibernate.ddl-auto=create +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.generate_statistics=true \ No newline at end of file diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/Dockerfile b/jpa-hibernate-examples/springboot-mapstruct-onetomany/Dockerfile new file mode 100644 index 00000000..6ae65e4e --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/Dockerfile @@ -0,0 +1 @@ +FROM maven:3.5-jdk-8 \ No newline at end of file diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/docker-compose.yml b/jpa-hibernate-examples/springboot-mapstruct-onetomany/docker-compose.yml new file mode 100644 index 00000000..2d76d303 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' +services: + hk-mysql: + container_name: hk-mysql + image: mysql/mysql-server:8.0 + environment: + MYSQL_DATABASE: test + MYSQL_ROOT_PASSWORD: hellokoding + MYSQL_ROOT_HOST: '%' + ports: + - "3306:3306" + restart: always + + app: + build: . + volumes: + - .:/app + - ~/.m2:/root/.m2 + working_dir: /app + command: mvn clean spring-boot:run + depends_on: + - hk-mysql diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/pom.xml b/jpa-hibernate-examples/springboot-mapstruct-onetomany/pom.xml new file mode 100644 index 00000000..56100314 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + com.hellokoding.springboot + springboot-mapstruct + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + + + + UTF-8 + 1.8 + 1.2.0.Final + 1.18.2 + 3.6.0 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + mysql + mysql-connector-java + runtime + + + org.projectlombok + lombok + ${org.projectlombok.version} + + + org.mapstruct + mapstruct-jdk8 + ${org.mapstruct.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${org.apache.maven.plugins.version} + + ${java.version} + ${java.version} + + + org.projectlombok + lombok + ${org.projectlombok.version} + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + + -Amapstruct.suppressGeneratorTimestamp=true + -Amapstruct.defaultComponentModel=spring + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Application.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Application.java new file mode 100644 index 00000000..5bf15d80 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Application.java @@ -0,0 +1,11 @@ +package com.hellokoding.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Book.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Book.java new file mode 100644 index 00000000..8061f393 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Book.java @@ -0,0 +1,46 @@ +package com.hellokoding.springboot; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +@Entity +public class Book { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + @Column(name = "name") + private String name; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "library_id") + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Library library; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Library getLibrary() { + return library; + } + + public void setLibrary(Library library) { + this.library = library; + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/BookController.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/BookController.java new file mode 100644 index 00000000..55f2d8ad --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/BookController.java @@ -0,0 +1,87 @@ +package com.hellokoding.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.validation.Valid; +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/books") +public class BookController { + private final BookRepository bookRepository; + private final LibraryRepository libraryRepository; + + @Autowired + public BookController(BookRepository bookRepository, LibraryRepository libraryRepository) { + this.bookRepository = bookRepository; + this.libraryRepository = libraryRepository; + } + + @PostMapping + public ResponseEntity create(@RequestBody @Valid Book book) { + Optional optionalLibrary = libraryRepository.findById(book.getLibrary().getId()); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + book.setLibrary(optionalLibrary.get()); + + Book savedBook = bookRepository.save(book); + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") + .buildAndExpand(savedBook.getId()).toUri(); + + return ResponseEntity.created(location).body(savedBook); + } + + @PutMapping("/{id}") + public ResponseEntity update(@RequestBody @Valid Book book, @PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(book.getLibrary().getId()); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + book.setLibrary(optionalLibrary.get()); + book.setId(optionalBook.get().getId()); + bookRepository.save(book); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + bookRepository.delete(optionalBook.get()); + + return ResponseEntity.noContent().build(); + } + + @GetMapping + public ResponseEntity> getAll(Pageable pageable) { + return ResponseEntity.ok(bookRepository.findAll(pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + return ResponseEntity.ok(optionalBook.get()); + } +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/BookRepository.java similarity index 79% rename from jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java rename to jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/BookRepository.java index e2e14a22..43d860cb 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-shared-primary-key-mysql/src/main/java/com/hellokoding/jpa/book/BookRepository.java +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/BookRepository.java @@ -1,4 +1,4 @@ -package com.hellokoding.jpa.book; +package com.hellokoding.springboot; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Library.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Library.java new file mode 100644 index 00000000..d24d9308 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/Library.java @@ -0,0 +1,47 @@ +package com.hellokoding.springboot; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.util.HashSet; +import java.util.Set; + +@Entity +public class Library { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + private String name; + + @OneToMany(mappedBy = "library", cascade = CascadeType.PERSIST) + private Set books = new HashSet<>(); + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +// public Set getBooks() { +// return books; +// } + + public void setBooks(Set books) { + this.books = books; + + for(Book b : books) { + b.setLibrary(this); + } + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryController.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryController.java new file mode 100644 index 00000000..a5cf1768 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryController.java @@ -0,0 +1,74 @@ +package com.hellokoding.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.validation.Valid; +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/libraries") +public class LibraryController { + private final LibraryRepository libraryRepository; + private final BookRepository bookRepository; + + @Autowired + public LibraryController(LibraryRepository libraryRepository, BookRepository bookRepository) { + this.libraryRepository = libraryRepository; + this.bookRepository = bookRepository; + } + + @PostMapping + public ResponseEntity create(@Valid @RequestBody Library library) { + Library savedLibrary = libraryRepository.save(library); + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") + .buildAndExpand(savedLibrary.getId()).toUri(); + + return ResponseEntity.created(location).body(savedLibrary); + } + + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Integer id, @Valid @RequestBody Library library) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + library.setId(optionalLibrary.get().getId()); + libraryRepository.save(library); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + libraryRepository.delete(optionalLibrary.get()); + + return ResponseEntity.noContent().build(); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + return ResponseEntity.ok(optionalLibrary.get()); + } + + @GetMapping + public ResponseEntity> getAll(Pageable pageable) { + return ResponseEntity.ok(libraryRepository.findAll(pageable)); + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryDTO.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryDTO.java new file mode 100644 index 00000000..aa6a75bb --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryDTO.java @@ -0,0 +1,39 @@ +package com.hellokoding.springboot; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.util.HashSet; +import java.util.Set; + +public class LibraryDTO { + private int id; + + @NotNull + private String name; + + private Set books = new HashSet<>(); + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getBooks() { + return books; + } + + public void setBooks(Set books) { + this.books = books; + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryMapper.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryMapper.java new file mode 100644 index 00000000..0abda714 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryMapper.java @@ -0,0 +1,8 @@ +package com.hellokoding.springboot; + +import org.mapstruct.Mapper; + +@Mapper +public class LibraryMapper { + +} diff --git a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/LibraryRepository.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryRepository.java similarity index 79% rename from jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/LibraryRepository.java rename to jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryRepository.java index a23fc1e1..683677f6 100644 --- a/jpa-hibernate-examples/jpa-hibernate-one-to-one-mysql/src/main/java/com/hellokoding/jpa/book/LibraryRepository.java +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/LibraryRepository.java @@ -1,4 +1,4 @@ -package com.hellokoding.jpa.book; +package com.hellokoding.springboot; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Application.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Application.java new file mode 100644 index 00000000..c340aeaf --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Application.java @@ -0,0 +1,11 @@ +package com.hellokoding.springboot.unidirectional; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Book.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Book.java new file mode 100644 index 00000000..c545ead0 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Book.java @@ -0,0 +1,45 @@ +package com.hellokoding.springboot.unidirectional; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +@Entity +public class Book { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + private String name; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Library library; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Library getLibrary() { + return library; + } + + public void setLibrary(Library library) { + this.library = library; + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/BookController.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/BookController.java new file mode 100644 index 00000000..2682196a --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/BookController.java @@ -0,0 +1,92 @@ +package com.hellokoding.springboot.unidirectional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.validation.Valid; +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/books") +public class BookController { + private final BookRepository bookRepository; + private final LibraryRepository libraryRepository; + + @Autowired + public BookController(BookRepository bookRepository, LibraryRepository libraryRepository) { + this.bookRepository = bookRepository; + this.libraryRepository = libraryRepository; + } + + @PostMapping + public ResponseEntity create(@RequestBody @Valid Book book) { + Optional optionalLibrary = libraryRepository.findById(book.getLibrary().getId()); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + book.setLibrary(optionalLibrary.get()); + + Book savedBook = bookRepository.save(book); + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") + .buildAndExpand(savedBook.getId()).toUri(); + + return ResponseEntity.created(location).body(savedBook); + } + + @PutMapping("/{id}") + public ResponseEntity update(@RequestBody @Valid Book book, @PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(book.getLibrary().getId()); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + book.setLibrary(optionalLibrary.get()); + book.setId(optionalBook.get().getId()); + bookRepository.save(book); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + bookRepository.delete(optionalBook.get()); + + return ResponseEntity.noContent().build(); + } + + @GetMapping + public ResponseEntity> getAll(Pageable pageable) { + return ResponseEntity.ok(bookRepository.findAll(pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional optionalBook = bookRepository.findById(id); + if (!optionalBook.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + return ResponseEntity.ok(optionalBook.get()); + } + + @GetMapping("/library/{libraryId}") + public ResponseEntity> getByLibraryId(@PathVariable Integer libraryId, Pageable pageable) { + return ResponseEntity.ok(bookRepository.findByLibraryId(libraryId, pageable)); + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/BookRepository.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/BookRepository.java new file mode 100644 index 00000000..96fb0d16 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/BookRepository.java @@ -0,0 +1,18 @@ +package com.hellokoding.springboot.unidirectional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +import javax.transaction.Transactional; + +public interface BookRepository extends JpaRepository{ + Page findByLibraryId(Integer libraryId, Pageable pageable); + + @Modifying + @Transactional + @Query("DELETE FROM Book b WHERE b.library.id = ?1") + void deleteByLibraryId(Integer libraryId); +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Library.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Library.java new file mode 100644 index 00000000..fbac465a --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/Library.java @@ -0,0 +1,33 @@ +package com.hellokoding.springboot.unidirectional; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.validation.constraints.NotNull; + +@Entity +public class Library { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @NotNull + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/LibraryController.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/LibraryController.java new file mode 100644 index 00000000..982c6251 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/LibraryController.java @@ -0,0 +1,81 @@ +package com.hellokoding.springboot.unidirectional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.transaction.Transactional; +import javax.validation.Valid; +import java.net.URI; +import java.util.Optional; + +@RestController +@RequestMapping("/api/v1/libraries") +public class LibraryController { + private final LibraryRepository libraryRepository; + private final BookRepository bookRepository; + + @Autowired + public LibraryController(LibraryRepository libraryRepository, BookRepository bookRepository) { + this.libraryRepository = libraryRepository; + this.bookRepository = bookRepository; + } + + @PostMapping("/") + public ResponseEntity create(@Valid @RequestBody Library library) { + Library savedLibrary = libraryRepository.save(library); + URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}") + .buildAndExpand(savedLibrary.getId()).toUri(); + + return ResponseEntity.created(location).body(savedLibrary); + } + + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Integer id, @Valid @RequestBody Library library) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + library.setId(optionalLibrary.get().getId()); + libraryRepository.save(library); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + deleteLibraryInTransaction(optionalLibrary.get()); + + return ResponseEntity.noContent().build(); + } + + @Transactional + public void deleteLibraryInTransaction(Library library) { + bookRepository.deleteByLibraryId(library.getId()); + libraryRepository.delete(library); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Integer id) { + Optional optionalLibrary = libraryRepository.findById(id); + if (!optionalLibrary.isPresent()) { + return ResponseEntity.unprocessableEntity().build(); + } + + return ResponseEntity.ok(optionalLibrary.get()); + } + + @GetMapping("/") + public ResponseEntity> getAll(Pageable pageable) { + return ResponseEntity.ok(libraryRepository.findAll(pageable)); + } +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/LibraryRepository.java b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/LibraryRepository.java new file mode 100644 index 00000000..912f0428 --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/java/com/hellokoding/springboot/unidirectional/LibraryRepository.java @@ -0,0 +1,6 @@ +package com.hellokoding.springboot.unidirectional; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface LibraryRepository extends JpaRepository{ +} diff --git a/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/resources/application.properties b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/resources/application.properties new file mode 100644 index 00000000..047b482b --- /dev/null +++ b/jpa-hibernate-examples/springboot-mapstruct-onetomany/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true +spring.datasource.username=root +spring.datasource.password=hellokoding + +spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect +spring.jpa.hibernate.ddl-auto=create +spring.jpa.show-sql=true diff --git a/spring-examples/spring-aop-example/mvnw b/spring-examples/spring-aop-example/mvnw new file mode 100755 index 00000000..a16b5431 --- /dev/null +++ b/spring-examples/spring-aop-example/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-examples/spring-aop-example/mvnw.cmd b/spring-examples/spring-aop-example/mvnw.cmd new file mode 100644 index 00000000..c8d43372 --- /dev/null +++ b/spring-examples/spring-aop-example/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/spring-examples/spring-aop-example/pom.xml b/spring-examples/spring-aop-example/pom.xml new file mode 100644 index 00000000..4454fffa --- /dev/null +++ b/spring-examples/spring-aop-example/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.3.RELEASE + + + com.hellokoding.spring + spring-aop-example + 0.0.1-SNAPSHOT + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-test + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/Application.java b/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/Application.java new file mode 100644 index 00000000..be5b6b3d --- /dev/null +++ b/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/Application.java @@ -0,0 +1,22 @@ +package com.hellokoding.spring; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Bean + @LogExecutionTime + public CommandLineRunner commandLineRunner(ApplicationContext ctx) { + return args -> { + Thread.sleep(100); + }; + } +} diff --git a/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/LogExecutionTime.java b/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/LogExecutionTime.java new file mode 100644 index 00000000..cf02a93d --- /dev/null +++ b/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/LogExecutionTime.java @@ -0,0 +1,11 @@ +package com.hellokoding.spring; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface LogExecutionTime { +} diff --git a/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/LoggingAspect.java b/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/LoggingAspect.java new file mode 100644 index 00000000..80e34eed --- /dev/null +++ b/spring-examples/spring-aop-example/src/main/java/com/hellokoding/spring/LoggingAspect.java @@ -0,0 +1,30 @@ +package com.hellokoding.spring; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class LoggingAspect { + @Pointcut("execution(* commandLineRunner(..))") + private void commandLineRunner() {} + + //@Around("@annotation(LogExecutionTime)") + //@Around("execution(* commandLineRunner(..))") + @Around("commandLineRunner()") + public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { + long start = System.currentTimeMillis(); + + Object proceed = joinPoint.proceed(); + + long executionTime = System.currentTimeMillis() - start; + + System.out.printf("%s executed in %sms %s", + joinPoint.getSignature(), executionTime, System.lineSeparator()); + + return proceed; + } +} diff --git a/spring-examples/spring-aop-example/src/test/java/com/hellokoding/spring/AOPProxyTest.java b/spring-examples/spring-aop-example/src/test/java/com/hellokoding/spring/AOPProxyTest.java new file mode 100644 index 00000000..63c980f2 --- /dev/null +++ b/spring-examples/spring-aop-example/src/test/java/com/hellokoding/spring/AOPProxyTest.java @@ -0,0 +1,29 @@ +package com.hellokoding.spring; + +import org.aopalliance.aop.Advice; +import org.junit.Test; +import org.springframework.aop.framework.ProxyFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AOPProxyTest { + @Test + public void callDirectly() { + Foo foo = new Foo(); + String s = foo.fooBar(); + + assertThat(s).isEqualTo("foo"); + } + + @Test + public void callOnProxy() { + ProxyFactory factory = new ProxyFactory(new Foo()); + //factory.addInterface(FooInterface.class); + //factory.addAdvisor(new LoggingAspect()); + + Foo pojo = (Foo) factory.getProxy(); + String s = pojo.fooBar(); + + assertThat(s).isEqualTo("foobar"); + } +} diff --git a/spring-examples/spring-aop-example/src/test/java/com/hellokoding/spring/Foo.java b/spring-examples/spring-aop-example/src/test/java/com/hellokoding/spring/Foo.java new file mode 100644 index 00000000..f2f679ce --- /dev/null +++ b/spring-examples/spring-aop-example/src/test/java/com/hellokoding/spring/Foo.java @@ -0,0 +1,19 @@ +package com.hellokoding.spring; + +public class Foo { + public String fooBar() { + return "foo" + this.bar(); + } + + private String fooPrivate() { + return "foo"; + } + + private String bar() { + return "bar"; + } +} + +interface FooInterface { + String foo(); +} diff --git a/spring-examples/spring-webflux/.gitignore b/spring-examples/spring-webflux/.gitignore new file mode 100644 index 00000000..a2a3040a --- /dev/null +++ b/spring-examples/spring-webflux/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/spring-examples/spring-webflux/.mvn/wrapper/MavenWrapperDownloader.java b/spring-examples/spring-webflux/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..e76d1f32 --- /dev/null +++ b/spring-examples/spring-webflux/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/spring-examples/spring-webflux/.mvn/wrapper/maven-wrapper.jar b/spring-examples/spring-webflux/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..2cc7d4a5 Binary files /dev/null and b/spring-examples/spring-webflux/.mvn/wrapper/maven-wrapper.jar differ diff --git a/spring-examples/spring-webflux/.mvn/wrapper/maven-wrapper.properties b/spring-examples/spring-webflux/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..642d572c --- /dev/null +++ b/spring-examples/spring-webflux/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-examples/spring-webflux/mvnw b/spring-examples/spring-webflux/mvnw new file mode 100755 index 00000000..a16b5431 --- /dev/null +++ b/spring-examples/spring-webflux/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-examples/spring-webflux/mvnw.cmd b/spring-examples/spring-webflux/mvnw.cmd new file mode 100644 index 00000000..c8d43372 --- /dev/null +++ b/spring-examples/spring-webflux/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/spring-examples/spring-webflux/pom.xml b/spring-examples/spring-webflux/pom.xml new file mode 100644 index 00000000..663165f2 --- /dev/null +++ b/spring-examples/spring-webflux/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.3.RELEASE + + + com.hellokoding.spring + spring-webflux + 0.0.1-SNAPSHOT + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloApplication.java b/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloApplication.java new file mode 100644 index 00000000..b57ebeeb --- /dev/null +++ b/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloApplication.java @@ -0,0 +1,11 @@ +package com.hellokoding.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class HelloApplication { + public static void main(String[] args) { + SpringApplication.run(HelloApplication.class, args); + } +} diff --git a/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloHandler.java b/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloHandler.java new file mode 100644 index 00000000..e7638c6c --- /dev/null +++ b/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloHandler.java @@ -0,0 +1,20 @@ +package com.hellokoding.spring; + +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class HelloHandler { + public Mono hello(ServerRequest request) { + String name = request + .queryParam("name") + .orElse("Spring WebFlux"); + + return ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .bodyValue(String.format("Hello, %s!", name)); + } +} diff --git a/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloRouter.java b/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloRouter.java new file mode 100644 index 00000000..2d37e1aa --- /dev/null +++ b/spring-examples/spring-webflux/src/main/java/com/hellokoding/spring/HelloRouter.java @@ -0,0 +1,20 @@ +package com.hellokoding.spring; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +import static org.springframework.web.reactive.function.server.RequestPredicates.GET; +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; + +@Configuration +public class HelloRouter { + @Bean + public RouterFunction route(HelloHandler helloHandler) { + return RouterFunctions + .route(GET("/hello").and(accept(MediaType.TEXT_PLAIN)), helloHandler::hello); + } +} diff --git a/spring-examples/spring-webflux/src/main/resources/application.properties b/spring-examples/spring-webflux/src/main/resources/application.properties new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/spring-examples/spring-webflux/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/spring-examples/spring-webflux/src/test/java/com/hellokoding/spring/HelloApplicationTests.java b/spring-examples/spring-webflux/src/test/java/com/hellokoding/spring/HelloApplicationTests.java new file mode 100644 index 00000000..b9dad1b2 --- /dev/null +++ b/spring-examples/spring-webflux/src/test/java/com/hellokoding/spring/HelloApplicationTests.java @@ -0,0 +1,13 @@ +package com.hellokoding.spring; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class HelloApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-examples/spring-webflux/src/test/java/com/hellokoding/spring/WebClientTest.java b/spring-examples/spring-webflux/src/test/java/com/hellokoding/spring/WebClientTest.java new file mode 100644 index 00000000..47644099 --- /dev/null +++ b/spring-examples/spring-webflux/src/test/java/com/hellokoding/spring/WebClientTest.java @@ -0,0 +1,57 @@ +package com.hellokoding.spring; + +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import static org.assertj.core.api.Assertions.assertThat; + +public class WebClientTest { + @Test + public void createWebClient() { + WebClient webClient1 = WebClient.create(); + WebClient webClient2 = WebClient.create("http://localhost:8080"); + } + + @Test + public void createWebClientWithBuilder() { + WebClient webClient = WebClient.builder() + .baseUrl("http://localhost:8080") + .defaultHeader(HttpHeaders.USER_AGENT, "Hello Koding") + .defaultCookie("cookie name", "cookie value") + .build(); + } + + @Test + public void buildAnHttpRequest() { + WebClient webClient = WebClient.create("http://localhost:8080"); + + Mono result = webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/hello") + .queryParam("name", "Koding") + .build()) + .retrieve() + .bodyToMono(String.class); + + assertThat(result.block()) + .isEqualTo("Hello, Koding!"); + } + + @Test + public void exchange() { + WebClient webClient = WebClient.create("http://localhost:8080"); + + Mono result = webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/hello") + .queryParam("name", "Koding") + .build()) + .exchange(); + + assertThat(result.flatMap(res -> res.bodyToMono(String.class)).block()) + .isEqualTo("Hello, Koding!"); + } +} diff --git a/springboot-examples/lombok-example/pom.xml b/springboot-examples/lombok-example/pom.xml new file mode 100644 index 00000000..dc542118 --- /dev/null +++ b/springboot-examples/lombok-example/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + com.hellokoding.springboot + lombok-example + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + + + + UTF-8 + 1.8 + + + + + org.projectlombok + lombok + + + + org.slf4j + slf4j-api + + + + \ No newline at end of file diff --git a/springboot-examples/lombok-example/src/main/java/com/hellokoding/springboot/product/Main.java b/springboot-examples/lombok-example/src/main/java/com/hellokoding/springboot/product/Main.java new file mode 100644 index 00000000..ae637883 --- /dev/null +++ b/springboot-examples/lombok-example/src/main/java/com/hellokoding/springboot/product/Main.java @@ -0,0 +1,5 @@ +package com.hellokoding.springboot.product; + +public class Main { + +} diff --git a/springboot-examples/lombok-example/src/main/java/com/hellokoding/springboot/product/Product.java b/springboot-examples/lombok-example/src/main/java/com/hellokoding/springboot/product/Product.java new file mode 100644 index 00000000..2ba351a2 --- /dev/null +++ b/springboot-examples/lombok-example/src/main/java/com/hellokoding/springboot/product/Product.java @@ -0,0 +1,19 @@ +package com.hellokoding.springboot.product; + +import lombok.*; +import lombok.extern.slf4j.Slf4j; + + +@Getter +@Setter +@ToString +@EqualsAndHashCode +@Slf4j +public class Product { + private Integer id; + private String name; + + static void main() { + + } +} diff --git a/springboot-examples/scribejava-oauth2/.gitignore b/springboot-examples/scribejava-oauth2/.gitignore new file mode 100644 index 00000000..4fe46d00 --- /dev/null +++ b/springboot-examples/scribejava-oauth2/.gitignore @@ -0,0 +1,4 @@ +config +.env +target +.idea diff --git a/springboot-examples/scribejava-oauth2/pom.xml b/springboot-examples/scribejava-oauth2/pom.xml new file mode 100644 index 00000000..1fe4837e --- /dev/null +++ b/springboot-examples/scribejava-oauth2/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + com.hellokoding.springboot + scribejava-oauth2 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.2.RELEASE + + + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-redis + + + com.github.scribejava + scribejava-apis + 6.9.0 + + + org.projectlombok + lombok + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Application.java b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Application.java new file mode 100644 index 00000000..ec6ca598 --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Application.java @@ -0,0 +1,11 @@ +package com.hellokoding.springboot.oauth2; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OAuth2Application { + public static void main(String[] args) { + SpringApplication.run(OAuth2Application.class, args); + } +} diff --git a/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Controller.java b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Controller.java new file mode 100644 index 00000000..ef8e0e89 --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Controller.java @@ -0,0 +1,98 @@ +package com.hellokoding.springboot.oauth2; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.revoke.TokenTypeHint; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.view.RedirectView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.concurrent.ExecutionException; + +@RestController +@RequiredArgsConstructor +public class OAuth2Controller { + private final OAuth2ServiceFactory oAuth2ServiceFactory; + private final ObjectMapper objectMapper; + private final UserSessionInRedis userSession; + private final HttpServletResponse httpServletResponse; + + private static final String KEY_USER = "user"; + private static final String KEY_STATE = "state"; + private static final String KEY_USERNAME = "username"; + private static final String KEY_ACCESS_TOKEN = "accessToken"; + private static final String KEY_SERVICE_ID = "serviceId"; + + @GetMapping("/oauth2/authorization/{serviceId}") + public RedirectView oauth2Login(@PathVariable String serviceId) { + String state = UUID.randomUUID().toString(); + userSession.put(KEY_STATE, state, Duration.of(60, ChronoUnit.SECONDS)); + return new RedirectView(oAuth2ServiceFactory.getService(serviceId).getAuthorizationUrl(state)); + } + + @GetMapping("/login/oauth2/code/{serviceId}") + public RedirectView oauth2Code(@PathVariable String serviceId, String code, String state) throws InterruptedException, ExecutionException, IOException { + if (!Objects.equals(state, userSession.get(KEY_STATE))) { + httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); + } else { + OAuth20Service oAuth20Service = oAuth2ServiceFactory.getService(serviceId); + OAuth2AccessToken accessToken = oAuth20Service.getAccessToken(code); + + OAuth2ServiceFactory.OAuth2Api oAuth2Api = (OAuth2ServiceFactory.OAuth2Api)oAuth20Service.getApi(); + final OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, oAuth2Api.getUserInfoEndpoint()); + oAuth20Service.signRequest(accessToken, oAuthRequest); + Map map = objectMapper.readValue(oAuth20Service.execute(oAuthRequest).getBody(), Map.class); + map.put(KEY_ACCESS_TOKEN, accessToken.getAccessToken()); + map.put(KEY_SERVICE_ID, serviceId); + map.put(KEY_USERNAME, map.get(oAuth2Api.getUserNameAttribute())); + int expiresIn = Optional.ofNullable(accessToken.getExpiresIn()).orElse(3600); + userSession.put(KEY_USER, map, Duration.of(expiresIn, ChronoUnit.SECONDS)); + } + + return new RedirectView("/"); + } + + @GetMapping("/user") + public Map user(HttpServletRequest request) { + Map user = ((Map) userSession.get(KEY_USER)); + return Objects.isNull(user) ? null : Collections.singletonMap(KEY_USERNAME, user.get(KEY_USERNAME)); + } + + @PostMapping("/logout") + public void logout() throws InterruptedException, ExecutionException, IOException { + Map user = (Map) userSession.get(KEY_USER); + if (Objects.nonNull(user)) { + String serviceId = user.get(KEY_SERVICE_ID); + OAuth20Service oAuth20Service = oAuth2ServiceFactory.getService(serviceId); + if (Objects.equals("facebook", serviceId)) { + revokeFacebookPermission(oAuth20Service, user); + } else if (!StringUtils.isEmpty(oAuth20Service.getApi().getRevokeTokenEndpoint())) { + oAuth20Service.revokeToken(user.get(KEY_ACCESS_TOKEN), TokenTypeHint.ACCESS_TOKEN); + } + + userSession.invalidate(); + } + } + + void revokeFacebookPermission(OAuth20Service oAuth20Service, Map user) throws InterruptedException, ExecutionException, IOException { + OAuth2ServiceFactory.OAuth2Api oAuth2Api = (OAuth2ServiceFactory.OAuth2Api)oAuth20Service.getApi(); + String endPoint = oAuth2Api.getRevokePermissionEndpoint().replace("{user-id}", user.get("id")); + final OAuthRequest oAuthRequest = new OAuthRequest(Verb.DELETE, endPoint); + oAuth20Service.signRequest(user.get(KEY_ACCESS_TOKEN), oAuthRequest); + oAuth20Service.execute(oAuthRequest); + } +} diff --git a/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Properties.java b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Properties.java new file mode 100644 index 00000000..2101e86a --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Properties.java @@ -0,0 +1,37 @@ +package com.hellokoding.springboot.oauth2; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +@ConfigurationProperties(prefix = "oauth2.client") +@Getter +public class OAuth2Properties { + private final Map registration = new HashMap<>(); + private final Map provider = new HashMap<>(); + + @Getter @Setter + public static class Registration { + private String clientId; + private String clientSecret; + private String redirectUri; + private String scope; + private String authorizationGrantType = "code"; + } + + @Getter @Setter + public static class Provider { + private String name; + private String authorizationUri; + private String tokenUri; + private String userInfoUri; + private String revokeTokenUri; + private String revokePermissionUri; + private String userNameAttribute = "name"; + } +} diff --git a/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2ServiceFactory.java b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2ServiceFactory.java new file mode 100644 index 00000000..775b7354 --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2ServiceFactory.java @@ -0,0 +1,80 @@ +package com.hellokoding.springboot.oauth2; + +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.OAuth2AccessTokenExtractor; +import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.oauth.OAuth20Service; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@RequiredArgsConstructor +@Component +public class OAuth2ServiceFactory { + private static Map services = new HashMap<>(); + private final OAuth2Properties oAuth2Properties; + + public OAuth20Service getService(String serviceId) { + if (services.containsKey(serviceId)) + return services.get(serviceId); + + OAuth2Properties.Registration registration = oAuth2Properties.getRegistration().get(serviceId); + OAuth2Properties.Provider provider = oAuth2Properties.getProvider().get(serviceId); + + OAuth20Service oAuth20Service = new ServiceBuilder(registration.getClientId()) + .apiSecret(registration.getClientSecret()) + .callback(registration.getRedirectUri()) + .defaultScope(registration.getScope()) + .responseType(registration.getAuthorizationGrantType()) + .userAgent("HelloKoding") + .build(new OAuth2Api(provider)); + services.put(serviceId, oAuth20Service); + + return oAuth20Service; + } + + @RequiredArgsConstructor + class OAuth2Api extends DefaultApi20 { + private final OAuth2Properties.Provider provider; + + @Override + public String getAccessTokenEndpoint() { + return provider.getTokenUri(); + } + + @Override + public String getAuthorizationBaseUrl() { + return provider.getAuthorizationUri(); + } + + @Override + public String getRevokeTokenEndpoint() { + return provider.getRevokeTokenUri(); + } + + @Override + public TokenExtractor getAccessTokenExtractor() { + if ("github".equalsIgnoreCase(provider.getName())) + return OAuth2AccessTokenExtractor.instance(); + + return OAuth2AccessTokenJsonExtractor.instance(); + } + + public String getUserInfoEndpoint() { + return provider.getUserInfoUri(); + } + + public String getUserNameAttribute() { + return provider.getUserNameAttribute(); + } + + public String getRevokePermissionEndpoint() { + return provider.getRevokePermissionUri(); + } + } +} diff --git a/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/RedisConfig.java b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/RedisConfig.java new file mode 100644 index 00000000..88a2dd3c --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/RedisConfig.java @@ -0,0 +1,18 @@ +package com.hellokoding.springboot.oauth2; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; + +@Configuration +public class RedisConfig { + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setKeySerializer(RedisSerializer.string()); + template.setConnectionFactory(redisConnectionFactory); + return template; + } +} diff --git a/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/SecurityFilter.java b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/SecurityFilter.java new file mode 100644 index 00000000..bb74ee40 --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/SecurityFilter.java @@ -0,0 +1,39 @@ +package com.hellokoding.springboot.oauth2; + +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.util.WebUtils; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Objects; +import java.util.UUID; + +@Component +@WebFilter("/*") +public class SecurityFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + if ("GET".equalsIgnoreCase(httpServletRequest.getMethod())) { + Cookie cookie = new Cookie("XSRF-TOKEN", UUID.randomUUID().toString()); + cookie.setHttpOnly(false); + httpServletResponse.addCookie(cookie); + } else if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) { + Cookie cookie = WebUtils.getCookie(httpServletRequest, "XSRF-TOKEN"); + String csrfHeader = httpServletRequest.getHeader("X-XSRF-TOKEN"); + if (!Objects.equals(csrfHeader, cookie.getValue())) { + httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value()); + return; + } + } + + chain.doFilter(request, response); + } +} diff --git a/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/UserSessionInRedis.java b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/UserSessionInRedis.java new file mode 100644 index 00000000..4df4ee59 --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/java/com/hellokoding/springboot/oauth2/UserSessionInRedis.java @@ -0,0 +1,45 @@ +package com.hellokoding.springboot.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.util.WebUtils; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.Duration; +import java.util.Set; + +@Component +@RequiredArgsConstructor +public class UserSessionInRedis { + private final RedisTemplate redisTemplate; + private final HttpServletRequest httpServletRequest; + private final HttpServletResponse httpServletResponse; + + public void put(String key, Object value, Duration timeout) { + redisTemplate.opsForValue().set(buildSessionKey(key), value, timeout); + } + + public Object get(String key) { + return redisTemplate.opsForValue().get(buildSessionKey(key)); + } + + public void invalidate() { + Set keys = redisTemplate.keys(httpServletRequest.getSession().getId().concat("*")); + for (String key : keys) { + redisTemplate.expire(key, Duration.ZERO); + } + + Cookie sessionCookie = WebUtils.getCookie(httpServletRequest, "JSESSIONID"); + sessionCookie.setMaxAge(0); + httpServletResponse.addCookie(sessionCookie); + + httpServletRequest.getSession().invalidate(); + } + + private String buildSessionKey(String key) { + return String.format("%s-%s", httpServletRequest.getSession().getId(), key); + } +} diff --git a/springboot-examples/scribejava-oauth2/src/main/resources/application.yml b/springboot-examples/scribejava-oauth2/src/main/resources/application.yml new file mode 100644 index 00000000..98ad2e1b --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/resources/application.yml @@ -0,0 +1,84 @@ +server: + port: 8081 + +oauth2: + client: + registration: + github: + clientId: ${GITHUB_CLIENT_ID} + clientSecret: ${GITHUB_CLIENT_SECRET} + redirectUri: http://localhost:8081/login/oauth2/code/github + scope: read:user + + google: + clientId: ${GOOGLE_CLIENT_ID} + clientSecret: ${GOOGLE_CLIENT_SECRET} + redirectUri: http://localhost:8081/login/oauth2/code/google + scope: email profile + + facebook: + clientId: ${FACEBOOK_CLIENT_ID} + clientSecret: ${FACEBOOK_CLIENT_SECRET} + redirectUri: http://localhost:8081/login/oauth2/code/facebook + scope: email public_profile + + okta: + clientId: ${OKTA_CLIENT_ID} + clientSecret: ${OKTA_CLIENT_SECRET} + redirectUri: http://localhost:8081/login/oauth2/code/okta + scope: openid profile email + + linkedin: + clientId: ${LINKEDIN_CLIENT_ID} + clientSecret: ${LINKEDIN_CLIENT_SECRET} + redirectUri: http://localhost:8081/login/oauth2/code/linkedin + scope: r_liteprofile r_emailaddress + + discord: + clientId: ${DISCORD_CLIENT_ID} + clientSecret: ${DISCORD_CLIENT_SECRET} + redirectUri: http://localhost:8081/login/oauth2/code/discord + scope: identify email + + provider: + github: + name: github + authorizationUri: https://github.com/login/oauth/authorize + tokenUri: https://github.com/login/oauth/access_token + userInfoUri: https://api.github.com/user + + google: + name: google + authorizationUri: https://accounts.google.com/o/oauth2/v2/auth + tokenUri: https://oauth2.googleapis.com/token + userInfoUri: https://openidconnect.googleapis.com/v1/userinfo + revokeTokenUri: https://oauth2.googleapis.com/revoke + + facebook: + name: facebook + authorizationUri: https://graph.facebook.com/oauth/authorize + tokenUri: https://graph.facebook.com/oauth/access_token + userInfoUri: https://graph.facebook.com/me?fields=id,name,email + revokePermissionUri: https://graph.facebook.com/{user-id}/permissions + + okta: + name: okta + authorizationUri: https://${OKTA_SUBDOMAIN}.okta.com/oauth2/default/v1/authorize + tokenUri: https://${OKTA_SUBDOMAIN}.okta.com/oauth2/default/v1/token + userInfoUri: https://${OKTA_SUBDOMAIN}.okta.com/oauth2/default/v1/userinfo + revokeTokenUri: https://${OKTA_SUBDOMAIN}.okta.com/oauth2/default/v1/revoke + + linkedin: + name: linkedin + authorizationUri: https://www.linkedin.com/oauth/v2/authorization + tokenUri: https://www.linkedin.com/oauth/v2/accessToken + userInfoUri: https://api.linkedin.com/v2/me + userNameAttribute: localizedFirstName + + discord: + name: discord + authorizationUri: https://discord.com/api/oauth2/authorize + tokenUri: https://discord.com/api/oauth2/token + userInfoUri: https://discord.com/api/users/@me + revokeTokenUri: https://discord.com/api/oauth2/token/revoke + userNameAttribute: username diff --git a/springboot-examples/scribejava-oauth2/src/main/resources/static/index.html b/springboot-examples/scribejava-oauth2/src/main/resources/static/index.html new file mode 100644 index 00000000..1a7982a8 --- /dev/null +++ b/springboot-examples/scribejava-oauth2/src/main/resources/static/index.html @@ -0,0 +1,75 @@ + + + + Login with OAuth2 / OpenId Connect + + + +

Login with OAuth2 / OpenId Connect

+ + + + \ No newline at end of file diff --git a/springboot-examples/spring-core-examples/pom.xml b/springboot-examples/spring-core-examples/pom.xml new file mode 100644 index 00000000..81a5ba75 --- /dev/null +++ b/springboot-examples/spring-core-examples/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.hellokoding.springcore + spring-core-examples + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.2.2.RELEASE + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + \ No newline at end of file diff --git a/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/ConsumerService.java b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/ConsumerService.java new file mode 100644 index 00000000..d7cc3e75 --- /dev/null +++ b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/ConsumerService.java @@ -0,0 +1,16 @@ +package com.hellokoding.springcore; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class ConsumerService { + @Autowired + RestTemplate restTemplateWithErrorHandler; + + public ResponseEntity consume(String url, Class responseType) { + return restTemplateWithErrorHandler.getForEntity(url, responseType); + } +} diff --git a/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/CustomResponseErrorHandler.java b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/CustomResponseErrorHandler.java new file mode 100644 index 00000000..d4c60157 --- /dev/null +++ b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/CustomResponseErrorHandler.java @@ -0,0 +1,48 @@ +package com.hellokoding.springcore; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.Scanner; + +@Slf4j +public class CustomResponseErrorHandler implements ResponseErrorHandler { + @Override + public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException { + HttpStatus status = clientHttpResponse.getStatusCode(); + return status.is4xxClientError() || status.is5xxServerError(); + } + + @Override + public void handleError(ClientHttpResponse clientHttpResponse) throws IOException { + String responseAsString = toString(clientHttpResponse.getBody()); + log.error("ResponseBody: {}", responseAsString); + + throw new CustomException(responseAsString); + } + + @Override + public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { + String responseAsString = toString(response.getBody()); + log.error("URL: {}, HttpMethod: {}, ResponseBody: {}", url, method, responseAsString); + + throw new CustomException(responseAsString); + } + + String toString(InputStream inputStream) { + Scanner s = new Scanner(inputStream).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } + + static class CustomException extends IOException { + public CustomException(String message) { + super(message); + } + } +} diff --git a/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/RestTemplateWithErrorHandlerConfig.java b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/RestTemplateWithErrorHandlerConfig.java new file mode 100644 index 00000000..52050d95 --- /dev/null +++ b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/RestTemplateWithErrorHandlerConfig.java @@ -0,0 +1,16 @@ +package com.hellokoding.springcore; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateWithErrorHandlerConfig { + @Bean + RestTemplate restTemplateWithErrorHandler() { + return new RestTemplateBuilder() + .errorHandler(new CustomResponseErrorHandler()) + .build(); + } +} diff --git a/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/RestTemplateWithTimeoutConfig.java b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/RestTemplateWithTimeoutConfig.java new file mode 100644 index 00000000..21ec0d90 --- /dev/null +++ b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/RestTemplateWithTimeoutConfig.java @@ -0,0 +1,38 @@ +package com.hellokoding.springcore; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import java.time.Duration; + +@Configuration +public class RestTemplateWithTimeoutConfig { + static final int TIMEOUT = 500; + + @Bean + RestTemplate restTemplateWithConnectReadTimeout() { + return new RestTemplateBuilder() + .setConnectTimeout(Duration.ofMillis(TIMEOUT)) + .setReadTimeout(Duration.ofMillis(TIMEOUT)) + .build(); + } + + @Bean + RestTemplate restTemplateWithConnectTimeout() { + return new RestTemplateBuilder() + .setConnectTimeout(Duration.ofMillis(TIMEOUT)) + .build(); + } + + @Bean + RestTemplate restTemplateTimeoutWithRequestFactory() { + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setConnectTimeout(TIMEOUT); + requestFactory.setReadTimeout(TIMEOUT); + + return new RestTemplate(requestFactory); + } +} diff --git a/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/CustomResponseErrorHandlerTest.java b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/CustomResponseErrorHandlerTest.java new file mode 100644 index 00000000..704e841c --- /dev/null +++ b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/CustomResponseErrorHandlerTest.java @@ -0,0 +1,51 @@ +package com.hellokoding.springcore; + +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.http.HttpMethod; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.ResponseActions; + +import static com.hellokoding.springcore.CustomResponseErrorHandler.CustomException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withBadRequest; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withServerError; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {RestTemplateWithErrorHandlerConfig.class, ConsumerService.class}) +public class CustomResponseErrorHandlerTest { + private static final String URL = "/server/products/1"; + private ResponseActions responseActions; + + @Autowired + private ConsumerService consumerService; + + @BeforeEach + public void setUp() { + responseActions = MockRestServiceServer.createServer(consumerService.restTemplateWithErrorHandler) + .expect(requestTo(URL)) + .andExpect(method(HttpMethod.GET)); + } + + @Test + public void response4xx() { + responseActions.andRespond(withBadRequest()); + + assertThatThrownBy(() -> consumerService.consume(URL, Object.class)) + .hasCauseInstanceOf(CustomException.class); + } + + @Test + public void response5xx() { + responseActions.andRespond(withServerError()); + + assertThatThrownBy(() -> consumerService.consume(URL, Object.class)) + .hasCauseInstanceOf(CustomException.class); + } +} diff --git a/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestClientResponseExceptionTest.java b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestClientResponseExceptionTest.java new file mode 100644 index 00000000..b2653fd8 --- /dev/null +++ b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestClientResponseExceptionTest.java @@ -0,0 +1,62 @@ +package com.hellokoding.springcore; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestClientResponseException; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withBadRequest; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withServerError; + +public class RestClientResponseExceptionTest { + private static final String URL = "/server/products/1"; + private MockRestServiceServer mockRestServiceServer; + private RestTemplate restTemplate; + + @BeforeEach + public void setUp() { + restTemplate = new RestTemplate(); + mockRestServiceServer = MockRestServiceServer.createServer(restTemplate); + } + + @Test + public void response4xx() { + mockRestServiceServer + .expect(requestTo(URL)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withBadRequest()); + + ResponseEntity responseEntity = consumeWebService(URL, Object.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + } + + @Test + public void response5xx() { + mockRestServiceServer + .expect(requestTo(URL)) + .andExpect(method(HttpMethod.GET)) + .andRespond(withServerError()); + + ResponseEntity responseEntity = consumeWebService(URL, Object.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + + ResponseEntity consumeWebService(String url, Class responseType) { + try { + return restTemplate.getForEntity(url, responseType); + } catch (RestClientResponseException e) { + return ResponseEntity + .status(e.getRawStatusCode()) + .body(e.getResponseBodyAsString()); + } + } +} diff --git a/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateConnectTimeoutTest.java b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateConnectTimeoutTest.java new file mode 100644 index 00000000..49e2d2b9 --- /dev/null +++ b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateConnectTimeoutTest.java @@ -0,0 +1,36 @@ +package com.hellokoding.springcore; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.web.client.RestTemplate; + +import java.net.SocketTimeoutException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.ThrowableAssert.catchThrowable; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = RestTemplateWithTimeoutConfig.class) +public class RestTemplateConnectTimeoutTest { + @Autowired + private RestTemplate restTemplateWithConnectTimeout; + + @ParameterizedTest + @ValueSource(strings = {"http://example.com:81", "http://10.255.255.255"}) + public void testConnectTimeout(String url) { + long startMillis = System.currentTimeMillis(); + + Throwable throwable = catchThrowable(() -> { + restTemplateWithConnectTimeout.getForObject(url, String.class); + }); + + long endMillis = System.currentTimeMillis(); + System.out.println("Execution time: " + (endMillis - startMillis)); + + assertThat(throwable).hasRootCauseInstanceOf(SocketTimeoutException.class); + } +} diff --git a/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateReadTimeoutApplication.java b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateReadTimeoutApplication.java new file mode 100644 index 00000000..3803f672 --- /dev/null +++ b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateReadTimeoutApplication.java @@ -0,0 +1,22 @@ +package com.hellokoding.springcore; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class RestTemplateReadTimeoutApplication { + public static void main(String[] args) { + SpringApplication.run(RestTemplateReadTimeoutApplication.class, args); + } + + @GetMapping("/test/delay") + public ResponseEntity delay(int millis) throws InterruptedException { + Thread.sleep(millis); + + return ResponseEntity.ok().build(); + } +} diff --git a/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateReadTimeoutTest.java b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateReadTimeoutTest.java new file mode 100644 index 00000000..661ed811 --- /dev/null +++ b/springboot-examples/spring-core-examples/src/test/java/com/hellokoding/springcore/RestTemplateReadTimeoutTest.java @@ -0,0 +1,58 @@ +package com.hellokoding.springcore; + +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.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.web.client.RestTemplate; + +import java.net.SocketTimeoutException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.ThrowableAssert.catchThrowable; + +@SpringBootTest( + webEnvironment = WebEnvironment.RANDOM_PORT, + classes = {RestTemplateWithTimeoutConfig.class, RestTemplateReadTimeoutApplication.class} +) +public class RestTemplateReadTimeoutTest { + @LocalServerPort + private int port; + + @Autowired + private RestTemplate restTemplateWithConnectTimeout; + + @Autowired + private RestTemplate restTemplateWithConnectReadTimeout; + + @Test + public void testReadTimeout() { + long startMillis = System.currentTimeMillis(); + + Throwable throwable = catchThrowable(() -> { + String url = String.format("http://localhost:%d/test/delay?millis=%d", port, 600); + restTemplateWithConnectReadTimeout.getForObject(url, String.class); + }); + + long endMillis = System.currentTimeMillis(); + System.out.println("Execution time: " + (endMillis - startMillis)); + + assertThat(throwable).hasRootCauseInstanceOf(SocketTimeoutException.class); + } + + @Test + public void testReadTimeout2() { + long startMillis = System.currentTimeMillis(); + + Throwable throwable = catchThrowable(() -> { + String url = String.format("http://localhost:%d/test/delay?millis=%d", port, 600); + restTemplateWithConnectTimeout.getForObject(url, String.class); + }); + + long endMillis = System.currentTimeMillis(); + System.out.println("Execution time: " + (endMillis - startMillis)); + + assertThat(throwable).hasRootCauseInstanceOf(SocketTimeoutException.class); + } +} diff --git a/springboot-examples/spring-security-oauth2/.gitignore b/springboot-examples/spring-security-oauth2/.gitignore new file mode 100644 index 00000000..4fe46d00 --- /dev/null +++ b/springboot-examples/spring-security-oauth2/.gitignore @@ -0,0 +1,4 @@ +config +.env +target +.idea diff --git a/springboot-examples/spring-security-oauth2/pom.xml b/springboot-examples/spring-security-oauth2/pom.xml new file mode 100644 index 00000000..d9c6ca6a --- /dev/null +++ b/springboot-examples/spring-security-oauth2/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + com.hellokoding.springboot + spring-security-oauth2 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.2.RELEASE + + + + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Application.java b/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Application.java new file mode 100644 index 00000000..ec6ca598 --- /dev/null +++ b/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Application.java @@ -0,0 +1,11 @@ +package com.hellokoding.springboot.oauth2; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OAuth2Application { + public static void main(String[] args) { + SpringApplication.run(OAuth2Application.class, args); + } +} diff --git a/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Controller.java b/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Controller.java new file mode 100644 index 00000000..25ddcf07 --- /dev/null +++ b/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/OAuth2Controller.java @@ -0,0 +1,17 @@ +package com.hellokoding.springboot.oauth2; + +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.Map; + +@RestController +public class OAuth2Controller { + @GetMapping("/user") + public Map user(@AuthenticationPrincipal OAuth2User principal) { + return Collections.singletonMap("name", principal.getAttribute("name")); + } +} diff --git a/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/SecurityConfig.java b/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/SecurityConfig.java new file mode 100644 index 00000000..8eb135aa --- /dev/null +++ b/springboot-examples/spring-security-oauth2/src/main/java/com/hellokoding/springboot/oauth2/SecurityConfig.java @@ -0,0 +1,23 @@ +package com.hellokoding.springboot.oauth2; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; + +@Configuration +public class SecurityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests(a -> a + .antMatchers("/").permitAll() + .anyRequest().authenticated()) + .csrf(c -> c.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) + .logout(l -> l.logoutSuccessUrl("/").permitAll()) + .oauth2Login(o -> o + .successHandler((request, response, authentication) -> { + response.sendRedirect("/"); + })); + } +} diff --git a/springboot-examples/spring-security-oauth2/src/main/resources/application.yml b/springboot-examples/spring-security-oauth2/src/main/resources/application.yml new file mode 100644 index 00000000..cf1b1f41 --- /dev/null +++ b/springboot-examples/spring-security-oauth2/src/main/resources/application.yml @@ -0,0 +1,42 @@ +server: + port: 8081 + +spring: + security: + oauth2: + client: + registration: + github: + clientId: ${GITHUB_CLIENT_ID} + clientSecret: ${GITHUB_CLIENT_SECRET} + + google: + clientId: ${GOOGLE_CLIENT_ID} + clientSecret: ${GOOGLE_CLIENT_SECRET} + + facebook: + clientId: ${FACEBOOK_CLIENT_ID} + clientSecret: ${FACEBOOK_CLIENT_SECRET} + + okta: + clientId: ${OKTA_CLIENT_ID} + clientSecret: ${OKTA_CLIENT_SECRET} + clientName: Okta + + linkedin: + clientId: ${LINKEDIN_CLIENT_ID} + clientSecret: ${LINKEDIN_CLIENT_SECRET} + clientName: LinkedIn + authorizationGrantType: authorization_code + redirectUri: "{baseUrl}/login/oauth2/code/{registrationId}" + scope: r_liteprofile, r_emailaddress + + provider: + okta: + issuerUri: https://${YOUR_OKTA_SUBDOMAIN:example}.okta.com/oauth2/default + + linkedin: + authorizationUri: https://www.linkedin.com/oauth/v2/authorization + tokenUri: https://www.linkedin.com/oauth/v2/accessToken + userInfoUri: https://api.linkedin.com/v2/me + userNameAttribute: id diff --git a/springboot-examples/spring-security-oauth2/src/main/resources/static/index.html b/springboot-examples/spring-security-oauth2/src/main/resources/static/index.html new file mode 100644 index 00000000..dcf6ff7b --- /dev/null +++ b/springboot-examples/spring-security-oauth2/src/main/resources/static/index.html @@ -0,0 +1,74 @@ + + + + Login with OAuth2 / OpenId Connect + + + +

Login with OAuth2 / OpenId Connect

+ + + + \ No newline at end of file diff --git a/springboot-examples/springboot-actuator/.gitignore b/springboot-examples/springboot-actuator/.gitignore new file mode 100644 index 00000000..a2a3040a --- /dev/null +++ b/springboot-examples/springboot-actuator/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/springboot-examples/springboot-actuator/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-examples/springboot-actuator/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..22810754 --- /dev/null +++ b/springboot-examples/springboot-actuator/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.5"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/springboot-examples/springboot-actuator/.mvn/wrapper/maven-wrapper.jar b/springboot-examples/springboot-actuator/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..0d5e6498 Binary files /dev/null and b/springboot-examples/springboot-actuator/.mvn/wrapper/maven-wrapper.jar differ diff --git a/springboot-examples/springboot-actuator/.mvn/wrapper/maven-wrapper.properties b/springboot-examples/springboot-actuator/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..7d59a01f --- /dev/null +++ b/springboot-examples/springboot-actuator/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar diff --git a/springboot-examples/springboot-actuator/mvnw b/springboot-examples/springboot-actuator/mvnw new file mode 100755 index 00000000..21d3ee84 --- /dev/null +++ b/springboot-examples/springboot-actuator/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/springboot-examples/springboot-actuator/mvnw.cmd b/springboot-examples/springboot-actuator/mvnw.cmd new file mode 100644 index 00000000..84d60abc --- /dev/null +++ b/springboot-examples/springboot-actuator/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/springboot-examples/springboot-actuator/pom.xml b/springboot-examples/springboot-actuator/pom.xml new file mode 100644 index 00000000..ca5d6e3b --- /dev/null +++ b/springboot-examples/springboot-actuator/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.1.RELEASE + + + com.example + springboot-actuator + 0.0.1-SNAPSHOT + springboot-actuator + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/springboot-examples/springboot-actuator/src/main/java/com/hellokoding/springboot/SpringbootActuatorApplication.java b/springboot-examples/springboot-actuator/src/main/java/com/hellokoding/springboot/SpringbootActuatorApplication.java new file mode 100644 index 00000000..411aa8f1 --- /dev/null +++ b/springboot-examples/springboot-actuator/src/main/java/com/hellokoding/springboot/SpringbootActuatorApplication.java @@ -0,0 +1,13 @@ +package com.hellokoding.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringbootActuatorApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringbootActuatorApplication.class, args); + } + +} diff --git a/springboot-examples/springboot-actuator/src/main/resources/application.properties b/springboot-examples/springboot-actuator/src/main/resources/application.properties new file mode 100644 index 00000000..930a925b --- /dev/null +++ b/springboot-examples/springboot-actuator/src/main/resources/application.properties @@ -0,0 +1,3 @@ +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always +management.health.diskspace.enabled=false \ No newline at end of file diff --git a/springboot-examples/springboot-actuator/src/test/java/com/hellokoding/springboot/SpringbootActuatorApplicationTests.java b/springboot-examples/springboot-actuator/src/test/java/com/hellokoding/springboot/SpringbootActuatorApplicationTests.java new file mode 100644 index 00000000..ba85fb77 --- /dev/null +++ b/springboot-examples/springboot-actuator/src/test/java/com/hellokoding/springboot/SpringbootActuatorApplicationTests.java @@ -0,0 +1,13 @@ +package com.hellokoding.springboot; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringbootActuatorApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/springboot-examples/springboot-flyway/pom.xml b/springboot-examples/springboot-flyway/pom.xml index 2d5a7f3f..59f15c4b 100644 --- a/springboot-examples/springboot-flyway/pom.xml +++ b/springboot-examples/springboot-flyway/pom.xml @@ -20,11 +20,21 @@
+ + org.springframework.boot + spring-boot-starter-web + + org.springframework.boot spring-boot-starter-data-jpa + + org.springframework.boot + spring-boot-starter-actuator + + org.flywaydb flyway-core diff --git a/springboot-examples/springboot-flyway/src/main/java/com/hellokoding/springboot/jpa/book/Product.java b/springboot-examples/springboot-flyway/src/main/java/com/hellokoding/springboot/jpa/book/Product.java new file mode 100644 index 00000000..c3e35d96 --- /dev/null +++ b/springboot-examples/springboot-flyway/src/main/java/com/hellokoding/springboot/jpa/book/Product.java @@ -0,0 +1,9 @@ +package com.hellokoding.springboot.jpa.book; + +import lombok.Value; + +@Value +public class Product { + private Integer id; + private String name; +} diff --git a/springboot-examples/springboot-flyway/src/main/resources/application.properties b/springboot-examples/springboot-flyway/src/main/resources/application.properties index fdab3484..11421017 100644 --- a/springboot-examples/springboot-flyway/src/main/resources/application.properties +++ b/springboot-examples/springboot-flyway/src/main/resources/application.properties @@ -3,7 +3,9 @@ spring.datasource.username=root spring.datasource.password=hellokoding spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.jpa.hibernate.ddl-auto=validate +spring.jpa.hibernate.ddl-auto=none spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect spring.jpa.generate-ddl=true spring.jpa.show-sql=true + +management.endpoints.web.exposure.include=info,health,flyway diff --git a/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.0__init.sql b/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.0__create_book.sql similarity index 100% rename from springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.0__init.sql rename to springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.0__create_book.sql diff --git a/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.1__insert_book.sql b/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.1__insert_book.sql new file mode 100644 index 00000000..89b3f761 --- /dev/null +++ b/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.1__insert_book.sql @@ -0,0 +1 @@ +INSERT INTO `book`(`title`, `description`) VALUES('Hello Koding', 'Coding tutorials series'); \ No newline at end of file diff --git a/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.1__insert_data.sql b/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.1__insert_data.sql deleted file mode 100644 index 4c8bdec6..00000000 --- a/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.1__insert_data.sql +++ /dev/null @@ -1,2 +0,0 @@ -INSERT INTO `book`(`description`, `title`) VALUES('Hello Koding', 'Simple coding examples and tutorials'); -INSERT INTO `book`(`description`, `title`) VALUES('Hello Koding 2', 'Simple coding examples and tutorials 2'); \ No newline at end of file diff --git a/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.2__insert_book.sql b/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.2__insert_book.sql new file mode 100644 index 00000000..a26ea9f5 --- /dev/null +++ b/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.2__insert_book.sql @@ -0,0 +1 @@ +INSERT INTO `book`(`title`, `description`) VALUES('Hello Koding 2', 'Coding tutorials series 2'); \ No newline at end of file diff --git a/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.3__delete_book.sql b/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.3__delete_book.sql new file mode 100644 index 00000000..4f970917 --- /dev/null +++ b/springboot-examples/springboot-flyway/src/main/resources/db/migration/V1.3__delete_book.sql @@ -0,0 +1 @@ +DELETE FROM `book` where `title` = 'Hello Koding 2'; \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-form-contact/pom.xml b/springboot-examples/springboot-freemarker-form-contact/pom.xml index 1ebead00..c8959aba 100644 --- a/springboot-examples/springboot-freemarker-form-contact/pom.xml +++ b/springboot-examples/springboot-freemarker-form-contact/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.4.RELEASE + 2.3.4.RELEASE @@ -22,10 +22,13 @@ org.springframework.boot spring-boot-starter-freemarker + + org.springframework.boot + spring-boot-starter-validation + com.sun.mail - javax.mail - 1.6.0 + jakarta.mail diff --git a/springboot-examples/springboot-freemarker-form-contact/src/main/resources/templates/form.ftl b/springboot-examples/springboot-freemarker-form-contact/src/main/resources/templates/form.ftlh similarity index 100% rename from springboot-examples/springboot-freemarker-form-contact/src/main/resources/templates/form.ftl rename to springboot-examples/springboot-freemarker-form-contact/src/main/resources/templates/form.ftlh diff --git a/springboot-examples/springboot-freemarker-form-submission/pom.xml b/springboot-examples/springboot-freemarker-form-submission/pom.xml index e34e3dab..de227802 100644 --- a/springboot-examples/springboot-freemarker-form-submission/pom.xml +++ b/springboot-examples/springboot-freemarker-form-submission/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.4.RELEASE + 2.3.4.RELEASE diff --git a/springboot-examples/springboot-freemarker-form-submission/src/main/java/com/hellokoding/springboot/User.java b/springboot-examples/springboot-freemarker-form-submission/src/main/java/com/hellokoding/springboot/User.java index 997a67c8..bb7a5375 100644 --- a/springboot-examples/springboot-freemarker-form-submission/src/main/java/com/hellokoding/springboot/User.java +++ b/springboot-examples/springboot-freemarker-form-submission/src/main/java/com/hellokoding/springboot/User.java @@ -1,22 +1,22 @@ package com.hellokoding.springboot; public class User { - private String firstName; - private String lastName; + private String name; + private String message; - public String getFirstName() { - return firstName; + public String getName() { + return name; } - public void setFirstName(String firstName) { - this.firstName = firstName; + public void setName(String name) { + this.name = name; } - public String getLastName() { - return lastName; + public String getMessage() { + return message; } - public void setLastName(String lastName) { - this.lastName = lastName; + public void setMessage(String message) { + this.message = message; } } diff --git a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/application.properties b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/application.properties index 1ae14b63..1670f5b3 100644 --- a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/application.properties +++ b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/application.properties @@ -1,2 +1,2 @@ spring.freemarker.template-loader-path: classpath:/templates -spring.freemarker.suffix: .ftl \ No newline at end of file +spring.freemarker.suffix: .ftlh \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/static/css/main.css b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/static/css/main.css index c461e5a7..cebd1092 100644 --- a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/static/css/main.css +++ b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/static/css/main.css @@ -1,3 +1,9 @@ -h2 { - color: darkgreen; +body { + width: 50%; + margin-left: auto; + margin-right: auto; +} + +body * { + font-weight: 400; } \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/static/js/main.js b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/static/js/main.js index 10e0f8de..2445304c 100644 --- a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/static/js/main.js +++ b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/static/js/main.js @@ -1,3 +1,3 @@ (function(){ - console.log("Hello World!"); + console.log("Hello FreeMarker Form!"); })(); \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/templates/form.ftl b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/templates/form.ftl deleted file mode 100644 index 90034965..00000000 --- a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/templates/form.ftl +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Form example with Java, Spring Boot, FreeMarker - - - -

Handling Form Submission example with Java, Spring Boot, FreeMarker

- <#if user?? > - Your submitted data
- First name: ${user.firstName}
- Last name: ${user.lastName}
- <#else> -
- First name:
- -

- Last name:
- -

- -
- - - - \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-form-submission/src/main/resources/templates/form.ftlh b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/templates/form.ftlh new file mode 100644 index 00000000..cc22accc --- /dev/null +++ b/springboot-examples/springboot-freemarker-form-submission/src/main/resources/templates/form.ftlh @@ -0,0 +1,28 @@ + + + + + Spring Boot Form Submission + + + +

Spring Boot Form Submission

+ <#if user?? > + Your submitted data
+ Name: ${user.name}
+ Message: ${user.message}
+ <#else> +
+
Name:
+ +

+
Message:
+ +

+ + +
+ + + + \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-form-validation/pom.xml b/springboot-examples/springboot-freemarker-form-validation/pom.xml index 00a35846..77491869 100644 --- a/springboot-examples/springboot-freemarker-form-validation/pom.xml +++ b/springboot-examples/springboot-freemarker-form-validation/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.4.RELEASE + 2.3.4.RELEASE @@ -22,6 +22,10 @@ org.springframework.boot spring-boot-starter-freemarker + + org.springframework.boot + spring-boot-starter-validation + diff --git a/springboot-examples/springboot-freemarker-form-validation/src/main/resources/application.properties b/springboot-examples/springboot-freemarker-form-validation/src/main/resources/application.properties index 1ae14b63..1670f5b3 100644 --- a/springboot-examples/springboot-freemarker-form-validation/src/main/resources/application.properties +++ b/springboot-examples/springboot-freemarker-form-validation/src/main/resources/application.properties @@ -1,2 +1,2 @@ spring.freemarker.template-loader-path: classpath:/templates -spring.freemarker.suffix: .ftl \ No newline at end of file +spring.freemarker.suffix: .ftlh \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-form-validation/src/main/resources/static/css/main.css b/springboot-examples/springboot-freemarker-form-validation/src/main/resources/static/css/main.css index 68a5bb63..de75ee80 100644 --- a/springboot-examples/springboot-freemarker-form-validation/src/main/resources/static/css/main.css +++ b/springboot-examples/springboot-freemarker-form-validation/src/main/resources/static/css/main.css @@ -1,7 +1,7 @@ -h2 { - color: darkgreen; +body * { + font-weight: 400; } form b { color: red; -} \ No newline at end of file +} diff --git a/springboot-examples/springboot-freemarker-form-validation/src/main/resources/static/js/main.js b/springboot-examples/springboot-freemarker-form-validation/src/main/resources/static/js/main.js index 10e0f8de..2d77c984 100644 --- a/springboot-examples/springboot-freemarker-form-validation/src/main/resources/static/js/main.js +++ b/springboot-examples/springboot-freemarker-form-validation/src/main/resources/static/js/main.js @@ -1,3 +1,3 @@ (function(){ - console.log("Hello World!"); + console.log("Hello FreeMaker Form!"); })(); \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-form-validation/src/main/resources/templates/form.ftl b/springboot-examples/springboot-freemarker-form-validation/src/main/resources/templates/form.ftlh similarity index 93% rename from springboot-examples/springboot-freemarker-form-validation/src/main/resources/templates/form.ftl rename to springboot-examples/springboot-freemarker-form-validation/src/main/resources/templates/form.ftlh index 9b00b3a9..45dbe828 100644 --- a/springboot-examples/springboot-freemarker-form-validation/src/main/resources/templates/form.ftl +++ b/springboot-examples/springboot-freemarker-form-validation/src/main/resources/templates/form.ftlh @@ -8,9 +8,7 @@ -

Form Data Binding and Validation

- - <@spring.bind "user"/> +

Form Data Validation and Binding

<#if user?? && noErrors??> Your submitted data
First name: ${user.firstName}
diff --git a/springboot-examples/springboot-freemarker-helloworld/pom.xml b/springboot-examples/springboot-freemarker-helloworld/pom.xml index f9b00cd1..1e0808d1 100644 --- a/springboot-examples/springboot-freemarker-helloworld/pom.xml +++ b/springboot-examples/springboot-freemarker-helloworld/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.2.2.RELEASE @@ -24,7 +24,6 @@ org.springframework.boot spring-boot-starter-web - org.springframework.boot spring-boot-starter-freemarker diff --git a/springboot-examples/springboot-freemarker-helloworld/src/main/java/com/hellokoding/springboot/view/HelloController.java b/springboot-examples/springboot-freemarker-helloworld/src/main/java/com/hellokoding/springboot/view/HelloController.java index 5b66510e..5325331b 100644 --- a/springboot-examples/springboot-freemarker-helloworld/src/main/java/com/hellokoding/springboot/view/HelloController.java +++ b/springboot-examples/springboot-freemarker-helloworld/src/main/java/com/hellokoding/springboot/view/HelloController.java @@ -8,7 +8,8 @@ @Controller public class HelloController { @GetMapping({"/", "/hello"}) - public String hello(Model model, @RequestParam(value="name", required=false, defaultValue="World") String name) { + public String hello(Model model, + @RequestParam(value="name", required=false, defaultValue="World") String name) { model.addAttribute("name", name); return "hello"; } diff --git a/springboot-examples/springboot-freemarker-helloworld/src/main/resources/application.properties b/springboot-examples/springboot-freemarker-helloworld/src/main/resources/application.properties index 1ae14b63..1670f5b3 100644 --- a/springboot-examples/springboot-freemarker-helloworld/src/main/resources/application.properties +++ b/springboot-examples/springboot-freemarker-helloworld/src/main/resources/application.properties @@ -1,2 +1,2 @@ spring.freemarker.template-loader-path: classpath:/templates -spring.freemarker.suffix: .ftl \ No newline at end of file +spring.freemarker.suffix: .ftlh \ No newline at end of file diff --git a/springboot-examples/springboot-freemarker-helloworld/src/main/resources/templates/hello.ftl b/springboot-examples/springboot-freemarker-helloworld/src/main/resources/templates/hello.ftlh similarity index 100% rename from springboot-examples/springboot-freemarker-helloworld/src/main/resources/templates/hello.ftl rename to springboot-examples/springboot-freemarker-helloworld/src/main/resources/templates/hello.ftlh diff --git a/springboot-examples/springboot-jsp-helloworld/pom.xml b/springboot-examples/springboot-jsp-helloworld/pom.xml index b633f31b..5657f372 100644 --- a/springboot-examples/springboot-jsp-helloworld/pom.xml +++ b/springboot-examples/springboot-jsp-helloworld/pom.xml @@ -7,12 +7,11 @@ com.hellokoding.springboot springboot-jsp-helloworld 1.0-SNAPSHOT - war org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.3.2.RELEASE @@ -25,17 +24,14 @@ org.springframework.boot spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-tomcat - provided - - org.apache.tomcat.embed tomcat-embed-jasper - provided + + + org.springframework.boot + spring-boot-devtools + true diff --git a/springboot-examples/springboot-jsp-helloworld/src/main/java/com/hellokoding/springboot/view/Application.java b/springboot-examples/springboot-jsp-helloworld/src/main/java/com/hellokoding/springboot/view/Application.java index 391d906c..370fe7e6 100644 --- a/springboot-examples/springboot-jsp-helloworld/src/main/java/com/hellokoding/springboot/view/Application.java +++ b/springboot-examples/springboot-jsp-helloworld/src/main/java/com/hellokoding/springboot/view/Application.java @@ -2,19 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication -public class Application extends SpringBootServletInitializer { - - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - return application.sources(Application.class); - } - +public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } - } diff --git a/springboot-examples/springboot-jsp-helloworld/src/main/resources/application.properties b/springboot-examples/springboot-jsp-helloworld/src/main/resources/application.properties index ae00fe72..eca7eb65 100644 --- a/springboot-examples/springboot-jsp-helloworld/src/main/resources/application.properties +++ b/springboot-examples/springboot-jsp-helloworld/src/main/resources/application.properties @@ -1,2 +1,2 @@ spring.mvc.view.prefix: /WEB-INF/jsp/ -spring.mvc.view.suffix: .jsp \ No newline at end of file +spring.mvc.view.suffix: .jsp diff --git a/springboot-examples/springboot-mapstruct/pom.xml b/springboot-examples/springboot-mapstruct/pom.xml index ee15edfd..bfb8e3b3 100644 --- a/springboot-examples/springboot-mapstruct/pom.xml +++ b/springboot-examples/springboot-mapstruct/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.3.4.RELEASE diff --git a/springboot-examples/springboot-mapstruct/src/main/java/com/hellokoding/springboot/restful/product/ProductService.java b/springboot-examples/springboot-mapstruct/src/main/java/com/hellokoding/springboot/restful/product/ProductService.java index 02e1a1d4..c6f0fc81 100644 --- a/springboot-examples/springboot-mapstruct/src/main/java/com/hellokoding/springboot/restful/product/ProductService.java +++ b/springboot-examples/springboot-mapstruct/src/main/java/com/hellokoding/springboot/restful/product/ProductService.java @@ -7,7 +7,6 @@ import java.util.Optional; @RequiredArgsConstructor - @Service public class ProductService { private final ProductRespository productRespository; diff --git a/springboot-examples/springboot-registration-login/Dockerfile b/springboot-examples/springboot-registration-login/Dockerfile new file mode 100644 index 00000000..6ae65e4e --- /dev/null +++ b/springboot-examples/springboot-registration-login/Dockerfile @@ -0,0 +1 @@ +FROM maven:3.5-jdk-8 \ No newline at end of file diff --git a/springboot-examples/springboot-registration-login/docker-compose.yml b/springboot-examples/springboot-registration-login/docker-compose.yml new file mode 100644 index 00000000..1fbe27da --- /dev/null +++ b/springboot-examples/springboot-registration-login/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3' +services: + hk-mysql: + container_name: hk-mysql + image: mysql/mysql-server:5.7 + environment: + MYSQL_DATABASE: test + MYSQL_ROOT_PASSWORD: hellokoding + MYSQL_ROOT_HOST: '%' + ports: + - "3306:3306" + restart: always + + registration-login: + build: . + volumes: + - .:/app + - ~/.m2:/root/.m2 + working_dir: /app + ports: + - 8080:8080 + command: mvn clean spring-boot:run + depends_on: + - hk-mysql diff --git a/springboot-examples/springboot-registration-login/pom.xml b/springboot-examples/springboot-registration-login/pom.xml index c17371f6..90e6970d 100644 --- a/springboot-examples/springboot-registration-login/pom.xml +++ b/springboot-examples/springboot-registration-login/pom.xml @@ -1,14 +1,15 @@ 4.0.0 - auth - auth - auth - war + + com.hellokoding.springboot + springboot-registration-login + 1.0-SNAPSHOT + org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.2.3.RELEASE @@ -23,36 +24,29 @@ - org.springframework.boot - spring-boot-starter-data-jpa - - - - org.springframework.boot - spring-boot-starter-security + org.apache.tomcat.embed + tomcat-embed-jasper - org.hsqldb - hsqldb - runtime + javax.servlet + jstl org.springframework.boot - spring-boot-starter-tomcat - provided + spring-boot-starter-data-jpa - org.apache.tomcat.embed - tomcat-embed-jasper - provided + org.springframework.boot + spring-boot-starter-security - javax.servlet - jstl + mysql + mysql-connector-java + runtime diff --git a/springboot-examples/springboot-registration-login/src/main/java/com/hellokoding/auth/WebApplication.java b/springboot-examples/springboot-registration-login/src/main/java/com/hellokoding/auth/WebApplication.java index 06931131..03d5cc85 100644 --- a/springboot-examples/springboot-registration-login/src/main/java/com/hellokoding/auth/WebApplication.java +++ b/springboot-examples/springboot-registration-login/src/main/java/com/hellokoding/auth/WebApplication.java @@ -6,12 +6,7 @@ import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication -public class WebApplication extends SpringBootServletInitializer { - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - return application.sources(WebApplication.class); - } - +public class WebApplication { public static void main(String[] args) throws Exception { SpringApplication.run(WebApplication.class, args); } diff --git a/springboot-examples/springboot-registration-login/src/main/java/com/hellokoding/auth/WebSecurityConfig.java b/springboot-examples/springboot-registration-login/src/main/java/com/hellokoding/auth/WebSecurityConfig.java index baeeb887..9a662904 100644 --- a/springboot-examples/springboot-registration-login/src/main/java/com/hellokoding/auth/WebSecurityConfig.java +++ b/springboot-examples/springboot-registration-login/src/main/java/com/hellokoding/auth/WebSecurityConfig.java @@ -1,6 +1,7 @@ package com.hellokoding.auth; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -14,6 +15,7 @@ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Qualifier("userDetailsServiceImpl") @Autowired private UserDetailsService userDetailsService; diff --git a/springboot-examples/springboot-registration-login/src/main/resources/application.properties b/springboot-examples/springboot-registration-login/src/main/resources/application.properties index 9178e80b..e4ba26fe 100644 --- a/springboot-examples/springboot-registration-login/src/main/resources/application.properties +++ b/springboot-examples/springboot-registration-login/src/main/resources/application.properties @@ -1,4 +1,11 @@ +spring.datasource.url=jdbc:mysql://hk-mysql:3306/test?useSSL=false +spring.datasource.username=root +spring.datasource.password=hellokoding +spring.datasource.driver-class-name=com.mysql.jdbc.Driver + spring.jpa.hibernate.ddl-auto=create +spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect +spring.jpa.generate-ddl=true spring.jpa.show-sql=true spring.mvc.view.prefix: / diff --git a/springboot-examples/springboot-restapi-testing-all-layers/src/main/java/com/hellokoding/springboot/restful/product/TransactionalProductService.java b/springboot-examples/springboot-restapi-testing-all-layers/src/main/java/com/hellokoding/springboot/restful/product/TransactionalProductService.java new file mode 100644 index 00000000..6de68322 --- /dev/null +++ b/springboot-examples/springboot-restapi-testing-all-layers/src/main/java/com/hellokoding/springboot/restful/product/TransactionalProductService.java @@ -0,0 +1,78 @@ +package com.hellokoding.springboot.restful.product; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Optional; + +@RequiredArgsConstructor + +@Service +public class TransactionalProductService { + private final ProductRespository productRespository; + + public Optional findById(Long id) { + return productRespository.findById(id); + } + + public Optional findByName(String name) { + return productRespository.findByName(name); + } + + public Product save(Product stock) { + return productRespository.save(stock); + } + + @Transactional + public void updateImplicitly(Long id, String name) { + Product product = findById(id).get(); + product.setName(name); + + // productRespository.save(product); + } + + @Transactional + public void updateOnCondition(Long id, String name) { + Product product = findById(id).get(); + product.setName(name); + + if (product.getPrice().compareTo(new BigDecimal("10")) == 0) { + productRespository.save(product); + } + } + + @Transactional + void updateImplicitlyNonPublic(Long id, String name) { + Product product = findById(id).get(); + product.setName(name); + } + + @Transactional + public void updateWithThrowingRuntimeException(Long id, String name) { + Product product = findById(id).get(); + product.setName(name); + throw new MyRuntimeException(); + } + + static class MyRuntimeException extends RuntimeException { + } + + @Transactional + public void updateWithThrowingException(Long id, String name) throws Exception { + Product product = findById(id).get(); + product.setName(name); + throw new MyException(); + } + + static class MyException extends Exception { + } + + @Transactional(noRollbackFor = MyException.class) + public void updateWithNoRollbackFor(Long id, String name) throws Exception { + Product product = findById(id).get(); + product.setName(name); + throw new MyException(); + } +} diff --git a/springboot-examples/springboot-restapi-testing-all-layers/src/test/java/com/hellokoding/springboot/restful/product/TransactionalTest.java b/springboot-examples/springboot-restapi-testing-all-layers/src/test/java/com/hellokoding/springboot/restful/product/TransactionalTest.java new file mode 100644 index 00000000..6fe94c66 --- /dev/null +++ b/springboot-examples/springboot-restapi-testing-all-layers/src/test/java/com/hellokoding/springboot/restful/product/TransactionalTest.java @@ -0,0 +1,92 @@ +package com.hellokoding.springboot.restful.product; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.math.BigDecimal; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class TransactionalTest { + @Autowired + private TransactionalProductService productService; + + @Before + public void setUp(){ + // given + Product product = Product.builder() + .name("P1") + .description("P1 desc") + .price(new BigDecimal("1")) + .build(); + + productService.save(product); + } + + @Test + public void testUpdateImplicitly() { + productService.updateImplicitly(1L, "updated"); + + Optional updatedProduct = productService.findByName("updated"); + assertThat(updatedProduct).isPresent(); + } + + @Test + public void testUpdateOnCondition() { + productService.updateOnCondition(1L, "updated"); + + Optional updatedProduct = productService.findByName("updated"); + assertThat(updatedProduct).isPresent(); + } + + @Test + public void testUpdateImplicitlyNonPublic() { + productService.updateImplicitlyNonPublic(1L, "updated"); + + Optional updatedProduct = productService.findByName("updated"); + assertThat(updatedProduct).isNotPresent(); + } + + @Test + public void testRollbackRuntimeException() { + try { + productService.updateWithThrowingRuntimeException(1L, "updated"); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + + Optional updatedProduct = productService.findByName("updated"); + assertThat(updatedProduct).isNotPresent(); + } + + @Test + public void testRollbackException() { + try { + productService.updateWithThrowingException(1L, "updated"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + + Optional updatedProduct = productService.findByName("updated"); + assertThat(updatedProduct).isPresent(); + } + + @Test + public void testNoRollbackFor() { + try { + productService.updateWithNoRollbackFor(1L, "updated"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + + Optional updatedProduct = productService.findByName("updated"); + assertThat(updatedProduct).isPresent(); + } +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/.gitignore b/springboot-examples/springboot-security-login-thymeleaf/.gitignore new file mode 100644 index 00000000..549e00a2 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..e76d1f32 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/maven-wrapper.jar b/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..2cc7d4a5 Binary files /dev/null and b/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/maven-wrapper.jar differ diff --git a/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/maven-wrapper.properties b/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..642d572c --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/springboot-examples/springboot-security-login-thymeleaf/mvnw b/springboot-examples/springboot-security-login-thymeleaf/mvnw new file mode 100755 index 00000000..a16b5431 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/springboot-examples/springboot-security-login-thymeleaf/mvnw.cmd b/springboot-examples/springboot-security-login-thymeleaf/mvnw.cmd new file mode 100644 index 00000000..c8d43372 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/springboot-examples/springboot-security-login-thymeleaf/pom.xml b/springboot-examples/springboot-security-login-thymeleaf/pom.xml new file mode 100644 index 00000000..7fda0e2b --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + + com.hellokoding.tutorials + springboot-security-login-thymeleaf + 0.0.1-SNAPSHOT + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + + + org.hsqldb + hsqldb + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.springframework.security + spring-security-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/DemoApplication.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/DemoApplication.java new file mode 100644 index 00000000..56a61b08 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/DemoApplication.java @@ -0,0 +1,13 @@ +package com.hellokoding.tutorials; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/WebSecurityConfig.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/WebSecurityConfig.java new file mode 100644 index 00000000..5743480a --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/WebSecurityConfig.java @@ -0,0 +1,51 @@ +package com.hellokoding.tutorials; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Qualifier("userDetailsServiceImpl") + @Autowired + private UserDetailsService userDetailsService; + + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .antMatchers("/css/**", "/js/**", "/registration").permitAll() + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage("/login") + .permitAll() + .and() + .logout() + .permitAll(); + } + + @Bean + public AuthenticationManager customAuthenticationManager() throws Exception { + return authenticationManager(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } +} \ No newline at end of file diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/model/Role.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/model/Role.java new file mode 100644 index 00000000..1f229514 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/model/Role.java @@ -0,0 +1,41 @@ +package com.hellokoding.tutorials.model; + +import javax.persistence.*; +import java.util.Set; + +@Entity +@Table(name = "role") +public class Role { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @ManyToMany(mappedBy = "roles") + private Set users; + + 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 Set getUsers() { + return users; + } + + public void setUsers(Set users) { + this.users = users; + } +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/model/User.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/model/User.java new file mode 100644 index 00000000..fa79858e --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/model/User.java @@ -0,0 +1,62 @@ +package com.hellokoding.tutorials.model; + +import javax.persistence.*; +import java.util.Set; + +@Entity +@Table(name = "user") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String username; + + private String password; + + @Transient + private String passwordConfirm; + + @ManyToMany + private Set roles; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPasswordConfirm() { + return passwordConfirm; + } + + public void setPasswordConfirm(String passwordConfirm) { + this.passwordConfirm = passwordConfirm; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/repository/RoleRepository.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/repository/RoleRepository.java new file mode 100644 index 00000000..f82f6cdc --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/repository/RoleRepository.java @@ -0,0 +1,7 @@ +package com.hellokoding.tutorials.repository; + +import com.hellokoding.tutorials.model.Role; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RoleRepository extends JpaRepository{ +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/repository/UserRepository.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/repository/UserRepository.java new file mode 100644 index 00000000..8c81367f --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/repository/UserRepository.java @@ -0,0 +1,8 @@ +package com.hellokoding.tutorials.repository; + +import com.hellokoding.tutorials.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + User findByUsername(String username); +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/SecurityService.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/SecurityService.java new file mode 100644 index 00000000..f5584e42 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/SecurityService.java @@ -0,0 +1,6 @@ +package com.hellokoding.tutorials.service; + +public interface SecurityService { + boolean isAuthenticated(); + void autoLogin(String username, String password); +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/SecurityServiceImpl.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/SecurityServiceImpl.java new file mode 100644 index 00000000..f8893356 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/SecurityServiceImpl.java @@ -0,0 +1,46 @@ +package com.hellokoding.tutorials.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; + +@Service +public class SecurityServiceImpl implements SecurityService{ + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private UserDetailsService userDetailsService; + + private static final Logger logger = LoggerFactory.getLogger(SecurityServiceImpl.class); + + public boolean isAuthenticated() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || AnonymousAuthenticationToken.class. + isAssignableFrom(authentication.getClass())) { + return false; + } + return authentication.isAuthenticated(); + } + + @Override + public void autoLogin(String username, String password) { + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities()); + + authenticationManager.authenticate(usernamePasswordAuthenticationToken); + + if (usernamePasswordAuthenticationToken.isAuthenticated()) { + SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); + logger.debug(String.format("Auto login %s successfully!", username)); + } + } +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserDetailsServiceImpl.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserDetailsServiceImpl.java new file mode 100644 index 00000000..17bf81a5 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserDetailsServiceImpl.java @@ -0,0 +1,36 @@ +package com.hellokoding.tutorials.service; + +import com.hellokoding.tutorials.model.Role; +import com.hellokoding.tutorials.model.User; +import com.hellokoding.tutorials.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashSet; +import java.util.Set; + +@Service +public class UserDetailsServiceImpl implements UserDetailsService{ + @Autowired + private UserRepository userRepository; + + @Override + @Transactional(readOnly = true) + public UserDetails loadUserByUsername(String username) { + User user = userRepository.findByUsername(username); + if (user == null) throw new UsernameNotFoundException(username); + + Set grantedAuthorities = new HashSet<>(); + for (Role role : user.getRoles()){ + grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())); + } + + return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); + } +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserService.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserService.java new file mode 100644 index 00000000..0c0dc5f9 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserService.java @@ -0,0 +1,9 @@ +package com.hellokoding.tutorials.service; + +import com.hellokoding.tutorials.model.User; + +public interface UserService { + void save(User user); + + User findByUsername(String username); +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserServiceImpl.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserServiceImpl.java new file mode 100644 index 00000000..39f4abfc --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/service/UserServiceImpl.java @@ -0,0 +1,32 @@ +package com.hellokoding.tutorials.service; + +import com.hellokoding.tutorials.model.User; +import com.hellokoding.tutorials.repository.RoleRepository; +import com.hellokoding.tutorials.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.HashSet; + +@Service +public class UserServiceImpl implements UserService { + @Autowired + private UserRepository userRepository; + @Autowired + private RoleRepository roleRepository; + @Autowired + private BCryptPasswordEncoder bCryptPasswordEncoder; + + @Override + public void save(User user) { + user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); + user.setRoles(new HashSet<>(roleRepository.findAll())); + userRepository.save(user); + } + + @Override + public User findByUsername(String username) { + return userRepository.findByUsername(username); + } +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/web/UserController.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/web/UserController.java new file mode 100644 index 00000000..7be3a359 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/web/UserController.java @@ -0,0 +1,70 @@ +package com.hellokoding.tutorials.web; + +import com.hellokoding.tutorials.model.User; +import com.hellokoding.tutorials.service.SecurityService; +import com.hellokoding.tutorials.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +public class UserController { + @Autowired + private UserService userService; + + @Autowired + private SecurityService securityService; + + @Autowired + private UserValidator userValidator; + + @GetMapping("/registration") + public String registration(Model model) { + if (securityService.isAuthenticated()) { + return "redirect:/"; + } + + model.addAttribute("userForm", new User()); + + return "registration"; + } + + @PostMapping("/registration") + public String registration(@ModelAttribute("userForm") User userForm, BindingResult bindingResult) { + userValidator.validate(userForm, bindingResult); + + if (bindingResult.hasErrors()) { + return "registration"; + } + + userService.save(userForm); + + securityService.autoLogin(userForm.getUsername(), userForm.getPasswordConfirm()); + + return "redirect:/welcome"; + } + + @GetMapping("/login") + public String login(Model model, String error, String logout) { + if (securityService.isAuthenticated()) { + return "redirect:/"; + } + + if (error != null) + model.addAttribute("error", "Your username and password is invalid."); + + if (logout != null) + model.addAttribute("message", "You have been logged out successfully."); + + return "login"; + } + + @GetMapping({"/", "/welcome"}) + public String welcome(Model model) { + return "welcome"; + } +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/web/UserValidator.java b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/web/UserValidator.java new file mode 100644 index 00000000..0a30f0ca --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/java/com/hellokoding/tutorials/web/UserValidator.java @@ -0,0 +1,42 @@ +package com.hellokoding.tutorials.web; + +import com.hellokoding.tutorials.model.User; +import com.hellokoding.tutorials.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; + +@Component +public class UserValidator implements Validator { + @Autowired + private UserService userService; + + @Override + public boolean supports(Class aClass) { + return User.class.equals(aClass); + } + + @Override + public void validate(Object o, Errors errors) { + User user = (User) o; + + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "NotEmpty"); + if (user.getUsername().length() < 6 || user.getUsername().length() > 32) { + errors.rejectValue("username", "Size.userForm.username"); + } + if (userService.findByUsername(user.getUsername()) != null) { + errors.rejectValue("username", "Duplicate.userForm.username"); + } + + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty"); + if (user.getPassword().length() < 8 || user.getPassword().length() > 32) { + errors.rejectValue("password", "Size.userForm.password"); + } + + if (!user.getPasswordConfirm().equals(user.getPassword())) { + errors.rejectValue("passwordConfirm", "Diff.userForm.passwordConfirm"); + } + } +} diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/application.properties b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/application.properties new file mode 100644 index 00000000..26ec6e4c --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true + +spring.thymeleaf.template-loader-path: classpath:/templates +spring.thymeleaf.suffix: .html +spring.thymeleaf.cache: false + +spring.messages.basename=validation diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/static/css/main.css b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/static/css/main.css new file mode 100644 index 00000000..520b29a4 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/static/css/main.css @@ -0,0 +1,48 @@ +body { + padding-top: 40px; + padding-bottom: 40px; + background-color: #eee; +} + +.form-signin { + max-width: 330px; + padding: 15px; + margin: 0 auto; +} + +.form-signin .form-signin-heading, +.form-signin .checkbox { + margin-bottom: 10px; +} + +.form-signin .checkbox { + font-weight: normal; +} + +.form-signin .form-control { + position: relative; + height: auto; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 10px; + font-size: 16px; +} + +.form-signin .form-control:focus { + z-index: 2; +} + +.form-signin input { + margin-top: 10px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.form-signin button { + margin-top: 10px; +} + +.has-error { + color: red +} \ No newline at end of file diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/static/js/main.js b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/static/js/main.js new file mode 100644 index 00000000..4d33be36 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/static/js/main.js @@ -0,0 +1,3 @@ +(function(){ + console.log("Hello World!"); +})(); \ No newline at end of file diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/login.html b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/login.html new file mode 100644 index 00000000..842aba1a --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/login.html @@ -0,0 +1,36 @@ + + + + + Log in with your account + + + + + +
+ +
+ + + + + + + diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/registration.html b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/registration.html new file mode 100644 index 00000000..acac49a4 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/registration.html @@ -0,0 +1,43 @@ + + + + + Create an account + + + + + + +
+ + + +
+ + + + + + + diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/welcome.html b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/welcome.html new file mode 100644 index 00000000..c359d256 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/templates/welcome.html @@ -0,0 +1,24 @@ + + + + + Create an account + + + +
+
+
+ +
+ +

Welcome | Logout

+
+
+ + + + + + + diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/validation.properties b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/validation.properties new file mode 100644 index 00000000..698c5f4f --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/main/resources/validation.properties @@ -0,0 +1,5 @@ +NotEmpty=This field is required. +Size.userForm.username=Please use between 6 and 32 characters. +Duplicate.userForm.username=Someone already has that username. +Size.userForm.password=Try one with at least 8 characters. +Diff.userForm.passwordConfirm=These passwords don't match. \ No newline at end of file diff --git a/springboot-examples/springboot-security-login-thymeleaf/src/test/java/com/hellokoding/tutorials/DemoApplicationTests.java b/springboot-examples/springboot-security-login-thymeleaf/src/test/java/com/hellokoding/tutorials/DemoApplicationTests.java new file mode 100644 index 00000000..d2442b22 --- /dev/null +++ b/springboot-examples/springboot-security-login-thymeleaf/src/test/java/com/hellokoding/tutorials/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package com.hellokoding.tutorials; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/springboot-examples/springboot-thymeleaf-helloworld/pom.xml b/springboot-examples/springboot-thymeleaf-helloworld/pom.xml index 178b1868..35c0c20c 100644 --- a/springboot-examples/springboot-thymeleaf-helloworld/pom.xml +++ b/springboot-examples/springboot-thymeleaf-helloworld/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.2.2.RELEASE