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/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/AnagramsByHistogramming.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsByHistogramming.java index 5fd10752..0697d731 100644 --- a/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsByHistogramming.java +++ b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/AnagramsByHistogramming.java @@ -1,22 +1,22 @@ package com.hellokoding.algorithm; +import java.util.HashMap; +import java.util.Map; + public class AnagramsByHistogramming { static boolean areAnagrams(String s, String t) { if (s.length() != t.length()) { return false; } - s = s.replaceAll("\\s", ""); - t = t.replaceAll("\\s", ""); - - int[] frequencies = new int[26]; + Map histogram = new HashMap<>(); for (int i = 0; i < s.length(); i++) { - frequencies[s.charAt(i) - 'a']++; - frequencies[t.charAt(i) - 'a']--; + 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 : frequencies) { + for (int count : histogram.values()) { if (count != 0) { return false; } 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/SubarrayGivenSumHashtable.java b/datastructure-algorithm-java-examples/src/main/java/com/hellokoding/algorithm/SubarrayGivenSumHashtable.java index 42163088..3e51b274 100644 --- 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 @@ -1,30 +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 count = 0; - int cumulativeSum = 0; + int cumulativeCount = 0; + int ci = 0, cj = 0; Map map = new HashMap<>(); map.put(0, 1); for (int value : a) { - cumulativeSum += value; - if (map.containsKey(cumulativeSum - k)) { - count += map.get(cumulativeSum - k); + cj += value; + ci = cj - k; + if (map.containsKey(ci)) { + cumulativeCount += map.get(ci); } - map.put(cumulativeSum, map.getOrDefault(cumulativeSum, 0) + 1); + map.put(cj, map.getOrDefault(cj, 0) + 1); } - return count; + 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/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/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/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/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/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/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/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/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 1179daa1..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 @@ -5,6 +5,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import javax.persistence.*; + @SpringBootApplication public class JpaApplication implements CommandLineRunner { private final BookService bookService; @@ -23,4 +25,49 @@ public void run(String... args) { 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-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/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/pom.xml b/spring-examples/spring-aop-example/pom.xml index 0081b011..4454fffa 100644 --- a/spring-examples/spring-aop-example/pom.xml +++ b/spring-examples/spring-aop-example/pom.xml @@ -22,6 +22,11 @@ spring-boot-starter-aop + + org.springframework.boot + spring-boot-starter-test + + 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 index 2598622d..80e34eed 100644 --- 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 @@ -3,12 +3,18 @@ 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 { - @Around("@annotation(LogExecutionTime)") + @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(); 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/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/src/main/java/com/hellokoding/springcore/RestTemplateWithTimeoutConfig.java b/springboot-examples/spring-core-examples/src/main/java/com/hellokoding/springcore/RestTemplateWithTimeoutConfig.java index 9b3ce071..21ec0d90 100644 --- 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 @@ -28,7 +28,7 @@ RestTemplate restTemplateWithConnectTimeout() { } @Bean - RestTemplate restTemplateTimeoutConfigWithRequestFactory() { + RestTemplate restTemplateTimeoutWithRequestFactory() { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setConnectTimeout(TIMEOUT); requestFactory.setReadTimeout(TIMEOUT); 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-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/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 d703fb99..5657f372 100644 --- a/springboot-examples/springboot-jsp-helloworld/pom.xml +++ b/springboot-examples/springboot-jsp-helloworld/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.2.3.RELEASE + 2.3.2.RELEASE @@ -28,6 +28,11 @@ org.apache.tomcat.embed tomcat-embed-jasper + + org.springframework.boot + spring-boot-devtools + true + 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-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() { + } + +}