From 55d29ff9f5e42684686119e66f73c337a0f24e2e Mon Sep 17 00:00:00 2001 From: mikeleo03 Date: Mon, 24 Jun 2024 23:49:22 +0700 Subject: [PATCH 01/30] [Refactor] Fix on Assignment 05 --- .../Lecture 03/Assignment 05/ListToMap.java | 14 ++-- Week 02/Lecture 03/Assignment 05/README.md | 14 +++- .../Assignment 05/RemoveDuplicates.java | 70 ++++++++++++++---- .../Lecture 03/Assignment 05/img/Task5.png | Bin 14919 -> 8283 bytes 4 files changed, 77 insertions(+), 21 deletions(-) diff --git a/Week 02/Lecture 03/Assignment 05/ListToMap.java b/Week 02/Lecture 03/Assignment 05/ListToMap.java index 686f587..de47030 100644 --- a/Week 02/Lecture 03/Assignment 05/ListToMap.java +++ b/Week 02/Lecture 03/Assignment 05/ListToMap.java @@ -1,7 +1,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.TreeMap; // Assume Employee class with fields: int employeeID, String name, String department class Employee { @@ -46,11 +46,15 @@ public static void main(String[] args) { new Employee(3, "Charlie", "Finance") ); - // Convert List to Map using employeeID as key - Map employeeMap = employees.stream() - .collect(Collectors.toMap(Employee::getEmployeeID, emp -> emp)); + // Creating employeeMap using TreeMap + Map employeesMap = new TreeMap<>(); + + // Convert List to Map + for (Employee emp : employees){ + employeesMap.put(emp.getEmployeeID(), emp.getName()); + } // Print the resulting Map - employeeMap.forEach((id, emp) -> System.out.println("Employee ID: " + id + ", Employee: " + emp)); + employeesMap.forEach((id, emp) -> System.out.println("Employee ID: " + id + ", Employee: " + emp)); } } \ No newline at end of file diff --git a/Week 02/Lecture 03/Assignment 05/README.md b/Week 02/Lecture 03/Assignment 05/README.md index 6769ca7..55b8de1 100644 --- a/Week 02/Lecture 03/Assignment 05/README.md +++ b/Week 02/Lecture 03/Assignment 05/README.md @@ -80,7 +80,7 @@ To remove duplicate lines from a file: 4. **Writing Unique Lines**: Write only those lines to a new file that haven't been seen before (not in the `HashSet`). #### πŸ“‹ Case CSV Content -In this program, i use CSV file [`input.csv`](/Week%2002%20-%20Jun%2017-21/Lecture%2003/Assignment%205/data/input.csv) with content like this. +In this program, i use CSV file [`input.csv`](/Week%2002/Lecture%2003/Assignment%2005/RemoveDuplicates.java) with content like this. ```csv employeeID,name,department 1,Alice,HR @@ -106,6 +106,16 @@ Detail implementation is written on [this code](/Week%2002%20-%20Jun%2017-21/Lec The output of the program shows on this [`output.csv`](/Week%2002%20-%20Jun%2017-21/Lecture%2003/Assignment%205/data/output.csv) +Here is how to run the updated code of program +```bash +$ java RemoveDuplicates +``` + +for example. +```bash +java RemoveDuplicates data/input.csv data/output.csv 0 +``` +
### πŸ–¨οΈ Task 4 - Get a Shallow Copy of a `HashMap` @@ -166,7 +176,7 @@ Here i implement class `BankAccount` and `BankAccountDemo`. 3. **Use Java Streams for Transformation**: Utilize Java Streams API to transform the `List` into a `Map`. 4. **Collect into Map**: Use the `Collectors.toMap()` method to collect elements of the `List` into a `Map` using the specified key and value mappings. -Detail implementation is written on [this code](/Week%2002%20-%20Jun%2017-21/Lecture%2003/Assignment%205/ListToMap.java), and the output of the program shows like this. +Detail implementation is written on [this code](/Week%2002/Lecture%2003/Assignment%2005/ListToMap.java), and the output of the program shows like this (updated based on comment). ![Screenshot](img/Task5.png) diff --git a/Week 02/Lecture 03/Assignment 05/RemoveDuplicates.java b/Week 02/Lecture 03/Assignment 05/RemoveDuplicates.java index f364dd0..19287f7 100644 --- a/Week 02/Lecture 03/Assignment 05/RemoveDuplicates.java +++ b/Week 02/Lecture 03/Assignment 05/RemoveDuplicates.java @@ -3,29 +3,42 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; public class RemoveDuplicates { public static void main(String[] args) { - String inputFileName = "data/input.csv"; - String outputFileName = "data/output.csv"; - String delimiter = ","; // Parse CSV format - - // Set to store unique keys (employeeID in this case) + if (args.length < 3) { + System.out.println("Usage: java RemoveDuplicates "); + return; + } + + String inputFileName = args[0]; + String outputFileName = args[1]; + int keyFieldIndex; + try { + keyFieldIndex = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + System.out.println("Invalid keyFieldIndex. It must be an integer."); + return; + } + + // Set to store unique keys Set seenKeys = new HashSet<>(); - + try (BufferedReader reader = new BufferedReader(new FileReader(inputFileName)); PrintWriter writer = new PrintWriter(new FileWriter(outputFileName))) { String line; while ((line = reader.readLine()) != null) { - // Split the line into fields - String[] fields = line.split(delimiter); - - // Ensure there are enough fields and key field is valid - if (fields.length > 1) { - String key = fields[0]; // Assuming employeeID is the first field + // Properly split the line respecting quoted commas + String[] fields = parseCsvLine(line); + + // Ensure key field index is valid + if (fields.length > keyFieldIndex) { + String key = fields[keyFieldIndex]; if (!seenKeys.contains(key)) { seenKeys.add(key); // Add the key to set (marks as seen) writer.println(line); // Write the line to output @@ -36,7 +49,36 @@ public static void main(String[] args) { System.out.println("Duplicates removed successfully. Output written to " + outputFileName); } catch (IOException e) { - System.out.println("I/O Error occured:" + e); + System.out.println("I/O Error occurred: " + e); } } -} + + // Function to parse CSV line while handling commas within quotes + private static String[] parseCsvLine(String line) { + boolean inQuotes = false; + StringBuilder field = new StringBuilder(); + List fields = new ArrayList<>(); + + for (char c : line.toCharArray()) { + switch (c) { + case '"': + inQuotes = !inQuotes; // Toggle the inQuotes flag + break; + case ',': + if (inQuotes) { + field.append(c); // Inside quotes, include comma + } else { + fields.add(field.toString()); + field.setLength(0); // Reset the field buffer + } + break; + default: + field.append(c); // Add character to field buffer + break; + } + } + fields.add(field.toString()); // Add last field + + return fields.toArray(new String[0]); + } +} \ No newline at end of file diff --git a/Week 02/Lecture 03/Assignment 05/img/Task5.png b/Week 02/Lecture 03/Assignment 05/img/Task5.png index ec754bd052f5acbafcd8bbdde0561e8ac5e71835..620f6598358a0dfb43ff00ee9ee38be7242eb3fa 100644 GIT binary patch literal 8283 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 S Date: Mon, 24 Jun 2024 23:49:28 +0700 Subject: [PATCH 02/30] [Refactor] Fix on Assignment 06 --- Week 02/Lecture 04/Assignment 06/README.md | 21 ++++-- .../Assignment 06/RemoveDuplicatesCSV.java | 65 +++++++++++-------- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/Week 02/Lecture 04/Assignment 06/README.md b/Week 02/Lecture 04/Assignment 06/README.md index 7a36c3c..26cad3a 100644 --- a/Week 02/Lecture 04/Assignment 06/README.md +++ b/Week 02/Lecture 04/Assignment 06/README.md @@ -119,11 +119,22 @@ Here’s a detailed process on how to remove duplicate lines from files based on 4. **Write the Output**: Write the processed, duplicate-free data to a new file. #### πŸ‘¨πŸ»β€πŸ’» Implementation -Detail implementation is written on [this code](/Week%2002%20-%20Jun%2017-21/Lecture%2004/Assignment%206/RemoveDuplicatesCSV.java). Here’s what the program actually done. -1. **Read All Lines**: `Files.readAllLines(Paths.get(inputFilePath))` reads the CSV file into a list of strings. -2. **Extract Header**: The first line is treated as the header to determine the key field's index. -3. **Stream Processing**: The stream skips the header, then collects lines into a map using the key field (`id`). If a duplicate key is found, the first occurrence is retained. -4. **Write Results**: The header is re-added, and the list is written to the new file. +Detail implementation is written on [this code](/Week%2002/Lecture%2004/Assignment%2006/RemoveDuplicatesCSV.java). Here’s what the program actually done (updated based on comment). +1. **Initialize Readers and Writers** + - `BufferedReader` is used to read the input CSV file line by line. + - `BufferedWriter` is used to write the unique lines to the output CSV file. +2. **Extract and Write Header** + - The first line, which is the header, is read using `reader.readLine()`. + - This header is written immediately to the output file using `writer.write(header)`. +3. **Determine Key Field Index** + - The header is split to determine the index of the key field (`id`). + - This is done by iterating through the headers to find the matching field. +4. **Stream Processing and Duplicate Removal** + - A `Set` is used to keep track of the keys that have already been processed. + - For each subsequent line, the key field's value is checked against the `Set`. If the key is unique, the line is written to the output file. +5. **Read and Write Lines** + - The program continues to read each line from the input file, splits it to get the key field, and checks the key against the `Set`. + - If the key is not in the `Set`, the line is written to the output file and the key is added to the `Set`. **Best Practices Highlighted** 1. **`BufferedReader` for Large Files**: Using `BufferedReader` with `lines()` streams data efficiently. diff --git a/Week 02/Lecture 04/Assignment 06/RemoveDuplicatesCSV.java b/Week 02/Lecture 04/Assignment 06/RemoveDuplicatesCSV.java index 77d29de..01e728e 100644 --- a/Week 02/Lecture 04/Assignment 06/RemoveDuplicatesCSV.java +++ b/Week 02/Lecture 04/Assignment 06/RemoveDuplicatesCSV.java @@ -1,10 +1,10 @@ import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; +import java.util.HashSet; +import java.util.Set; public class RemoveDuplicatesCSV { public static void main(String[] args) { @@ -12,35 +12,44 @@ public static void main(String[] args) { String outputFilePath = "data/unique.csv"; String keyFieldName = "id"; - try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputFilePath))) { - List lines = reader.lines().collect(Collectors.toList()); - if (lines.isEmpty()) return; + try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputFilePath)); + BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputFilePath))) { + + String header = reader.readLine(); + if (header == null) return; - // Extract header and determine the key field index - String header = lines.get(0); - List headers = Arrays.asList(header.split(",")); - int keyIndex = headers.indexOf(keyFieldName); - if (keyIndex == -1) throw new IllegalArgumentException("Invalid key field name"); + // Write header to the output file + writer.write(header); + writer.newLine(); - // Process lines and remove duplicates based on the key field - List uniqueLines = lines.stream() - .skip(1) // Skip header - .collect(Collectors.toMap( - line -> line.split(",")[keyIndex], // Use the key field - line -> line, // Use the line as value - (existing, replacement) -> existing // Keep the first occurrence - )) - .values() - .stream() - .collect(Collectors.toList()); + // Determine the key field index + String[] headers = header.split(","); + int keyIndex = -1; + for (int i = 0; i < headers.length; i++) { + if (headers[i].trim().equals(keyFieldName)) { + keyIndex = i; + break; + } + } + if (keyIndex == -1) throw new IllegalArgumentException("Invalid key field name"); - // Add header back to the list - uniqueLines.add(0, header); + // Use a Set to track unique keys + Set seenKeys = new HashSet<>(); - // Write the results to a new file - Files.write(Paths.get(outputFilePath), uniqueLines); + // Read and process each line + String line; + while ((line = reader.readLine()) != null) { + String[] fields = line.split(","); + if (fields.length > keyIndex) { + String key = fields[keyIndex]; + if (seenKeys.add(key)) { // Add returns false if the key was already present + writer.write(line); + writer.newLine(); + } + } + } } catch (IOException e) { - System.out.println("I/O Error occured:" + e); + System.out.println("I/O Error occurred: " + e); } } -} +} \ No newline at end of file From f03e8728add5c57564757e55aeb92373ddc570a2 Mon Sep 17 00:00:00 2001 From: mikeleo03 Date: Tue, 16 Jul 2024 23:03:19 +0700 Subject: [PATCH 03/30] [Init] Initialize Lecture 11 project --- .gitignore | 3 +- Week 06/Lecture 11/lecture_11/.gitignore | 33 +++ .../.mvn/wrapper/maven-wrapper.properties | 19 ++ Week 06/Lecture 11/lecture_11/mvnw | 259 ++++++++++++++++++ Week 06/Lecture 11/lecture_11/mvnw.cmd | 149 ++++++++++ Week 06/Lecture 11/lecture_11/pom.xml | 54 ++++ .../lecture_11/Lecture11Application.java | 13 + .../src/main/resources/application.properties | 1 + .../lecture_11/Lecture11ApplicationTests.java | 13 + 9 files changed, 543 insertions(+), 1 deletion(-) create mode 100644 Week 06/Lecture 11/lecture_11/.gitignore create mode 100644 Week 06/Lecture 11/lecture_11/.mvn/wrapper/maven-wrapper.properties create mode 100644 Week 06/Lecture 11/lecture_11/mvnw create mode 100644 Week 06/Lecture 11/lecture_11/mvnw.cmd create mode 100644 Week 06/Lecture 11/lecture_11/pom.xml create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/resources/application.properties create mode 100644 Week 06/Lecture 11/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java diff --git a/.gitignore b/.gitignore index 1de5659..794c788 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -target \ No newline at end of file +target +HELP.md \ No newline at end of file diff --git a/Week 06/Lecture 11/lecture_11/.gitignore b/Week 06/Lecture 11/lecture_11/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/.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/Week 06/Lecture 11/lecture_11/.mvn/wrapper/maven-wrapper.properties b/Week 06/Lecture 11/lecture_11/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..8f96f52 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,19 @@ +# 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. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip diff --git a/Week 06/Lecture 11/lecture_11/mvnw b/Week 06/Lecture 11/lecture_11/mvnw new file mode 100644 index 0000000..d7c358e --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/mvnw @@ -0,0 +1,259 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + 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" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/Week 06/Lecture 11/lecture_11/mvnw.cmd b/Week 06/Lecture 11/lecture_11/mvnw.cmd new file mode 100644 index 0000000..6f779cf --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@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 Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/Week 06/Lecture 11/lecture_11/pom.xml b/Week 06/Lecture 11/lecture_11/pom.xml new file mode 100644 index 0000000..ea3d4ed --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.1 + + + com.example + lecture_11 + 0.0.1-SNAPSHOT + lecture_11 + Demo project for Spring Boot + + + + + + + + + + + + + + + 21 + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java new file mode 100644 index 0000000..389e390 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java @@ -0,0 +1,13 @@ +package com.example.lecture_11; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Lecture11Application { + + public static void main(String[] args) { + SpringApplication.run(Lecture11Application.class, args); + } + +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/resources/application.properties b/Week 06/Lecture 11/lecture_11/src/main/resources/application.properties new file mode 100644 index 0000000..da416dd --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=lecture_11 diff --git a/Week 06/Lecture 11/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java b/Week 06/Lecture 11/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java new file mode 100644 index 0000000..12ec4ae --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.lecture_11; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class Lecture11ApplicationTests { + + @Test + void contextLoads() { + } + +} From 3ad20656a70cabee889ce3d19f83314a7adf1a20 Mon Sep 17 00:00:00 2001 From: mikeleo03 Date: Wed, 17 Jul 2024 00:11:53 +0700 Subject: [PATCH 04/30] [Feat] All the datas (model, repo, and composite keys) --- Week 06/Lecture 11/README.md | 0 Week 06/Lecture 11/lecture_11/pom.xml | 52 ++++++++++++++++++- .../lecture_11/data/model/Department.java | 24 +++++++++ .../lecture_11/data/model/DeptEmp.java | 41 +++++++++++++++ .../lecture_11/data/model/DeptManager.java | 41 +++++++++++++++ .../lecture_11/data/model/Employee.java | 47 +++++++++++++++++ .../example/lecture_11/data/model/Salary.java | 41 +++++++++++++++ .../example/lecture_11/data/model/Title.java | 41 +++++++++++++++ .../data/model/composite/DeptEmpId.java | 11 ++++ .../data/model/composite/DeptManagerId.java | 11 ++++ .../data/model/composite/SalaryId.java | 12 +++++ .../data/model/composite/TitleId.java | 13 +++++ .../data/repository/DepartmentRepository.java | 11 ++++ .../data/repository/DeptEmpRepository.java | 10 ++++ .../repository/DeptManagerRepository.java | 10 ++++ .../data/repository/EmployeeRepository.java | 10 ++++ .../data/repository/SalaryRepository.java | 9 ++++ .../data/repository/TitleRepository.java | 9 ++++ 18 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 Week 06/Lecture 11/README.md create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java diff --git a/Week 06/Lecture 11/README.md b/Week 06/Lecture 11/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Week 06/Lecture 11/lecture_11/pom.xml b/Week 06/Lecture 11/lecture_11/pom.xml index ea3d4ed..bc75dd1 100644 --- a/Week 06/Lecture 11/lecture_11/pom.xml +++ b/Week 06/Lecture 11/lecture_11/pom.xml @@ -30,15 +30,65 @@ 21 + org.springframework.boot spring-boot-starter - org.springframework.boot spring-boot-starter-test test + + + org.springframework.boot + spring-boot-starter-web-services + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + 8.0.33 + + + + + org.projectlombok + lombok + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.hibernate.validator + hibernate-validator + 8.0.0.Final + + + javax.validation + validation-api + 2.0.1.Final + + + + + org.mapstruct + mapstruct + 1.5.3.Final + + + org.mapstruct + mapstruct-processor + 1.5.3.Final + provided diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java new file mode 100644 index 0000000..030191f --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java @@ -0,0 +1,24 @@ +package com.example.lecture_11.data.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "departments") +@NoArgsConstructor +@AllArgsConstructor +public class Department { + + @Id + @Column(length = 4) + private String deptNo; + + @Column(length = 40, nullable = false, unique = true) + private String deptName; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java new file mode 100644 index 0000000..0fecc37 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java @@ -0,0 +1,41 @@ +package com.example.lecture_11.data.model; + +import java.time.LocalDate; + +import com.example.lecture_11.data.model.composite.DeptEmpId; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "dept_emp") +@IdClass(DeptEmpId.class) +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class DeptEmp { + + @Id + private Integer empNo; + + @Id + private String deptNo; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate fromDate; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java new file mode 100644 index 0000000..6729ee0 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java @@ -0,0 +1,41 @@ +package com.example.lecture_11.data.model; + +import java.time.LocalDate; + +import com.example.lecture_11.data.model.composite.DeptManagerId; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "dept_manager") +@IdClass(DeptManagerId.class) +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class DeptManager { + + @Id + private Integer empNo; + + @Id + private String deptNo; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate fromDate; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java new file mode 100644 index 0000000..d3c868e --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java @@ -0,0 +1,47 @@ +package com.example.lecture_11.data.model; + +import java.time.LocalDate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "employees") +@NoArgsConstructor +@AllArgsConstructor +public class Employee { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer empNo; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate birthDate; + + @Column(length = 14, nullable = false) + private String firstName; + + @Column(length = 16, nullable = false) + private String lastName; + + @Column(columnDefinition = "enum('M','F')", nullable = false) + @Enumerated(EnumType.STRING) + private String gender; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate hireDate; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java new file mode 100644 index 0000000..d3902b9 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java @@ -0,0 +1,41 @@ +package com.example.lecture_11.data.model; + +import java.time.LocalDate; + +import com.example.lecture_11.data.model.composite.SalaryId; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "salaries") +@IdClass(SalaryId.class) +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class Salary { + + @Id + private Integer empNo; + + @Column(nullable = false) + private Integer salary; + + @Id + @Temporal(TemporalType.DATE) + private LocalDate fromDate; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java new file mode 100644 index 0000000..5ef4e9a --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java @@ -0,0 +1,41 @@ +package com.example.lecture_11.data.model; + +import java.time.LocalDate; + +import com.example.lecture_11.data.model.composite.TitleId; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "titles") +@IdClass(TitleId.class) +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class Title { + + @Id + private Integer empNo; + + @Id + private String title; + + @Id + @Temporal(TemporalType.DATE) + private LocalDate fromDate; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java new file mode 100644 index 0000000..fed319f --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java @@ -0,0 +1,11 @@ +package com.example.lecture_11.data.model.composite; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class DeptEmpId implements Serializable { + private Integer empNo; + private String deptNo; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java new file mode 100644 index 0000000..f70acab --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java @@ -0,0 +1,11 @@ +package com.example.lecture_11.data.model.composite; + +import java.io.Serializable; + +import lombok.Data; + +@Data +public class DeptManagerId implements Serializable { + private Integer empNo; + private String deptNo; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java new file mode 100644 index 0000000..7d6fb83 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java @@ -0,0 +1,12 @@ +package com.example.lecture_11.data.model.composite; + +import java.io.Serializable; +import java.time.LocalDate; + +import lombok.Data; + +@Data +public class SalaryId implements Serializable { + private Integer empNo; + private LocalDate fromDate; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java new file mode 100644 index 0000000..57cfd0b --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java @@ -0,0 +1,13 @@ +package com.example.lecture_11.data.model.composite; + +import java.io.Serializable; +import java.time.LocalDate; + +import lombok.Data; + +@Data +public class TitleId implements Serializable { + private Integer empNo; + private String title; + private LocalDate fromDate; +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java new file mode 100644 index 0000000..72bf62c --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java @@ -0,0 +1,11 @@ +package com.example.lecture_11.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.example.lecture_11.data.model.Department; + +@Repository +public interface DepartmentRepository extends JpaRepository { +} + diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java new file mode 100644 index 0000000..c285503 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java @@ -0,0 +1,10 @@ +package com.example.lecture_11.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.lecture_11.data.model.DeptEmp; +import com.example.lecture_11.data.model.composite.DeptEmpId; + +public interface DeptEmpRepository extends JpaRepository { +} + diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java new file mode 100644 index 0000000..664b2e2 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java @@ -0,0 +1,10 @@ +package com.example.lecture_11.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.lecture_11.data.model.DeptManager; +import com.example.lecture_11.data.model.composite.DeptManagerId; + +public interface DeptManagerRepository extends JpaRepository { +} + diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java new file mode 100644 index 0000000..ba85bc3 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java @@ -0,0 +1,10 @@ +package com.example.lecture_11.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.example.lecture_11.data.model.Employee; + +@Repository +public interface EmployeeRepository extends JpaRepository { +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java new file mode 100644 index 0000000..535efc7 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java @@ -0,0 +1,9 @@ +package com.example.lecture_11.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.lecture_11.data.model.Salary; +import com.example.lecture_11.data.model.composite.SalaryId; + +public interface SalaryRepository extends JpaRepository { +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java new file mode 100644 index 0000000..2857d84 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java @@ -0,0 +1,9 @@ +package com.example.lecture_11.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.lecture_11.data.model.Title; +import com.example.lecture_11.data.model.composite.TitleId; + +public interface TitleRepository extends JpaRepository { +} From c21f940ca7afba1350c77142c8cfc3b7f1dee8bc Mon Sep 17 00:00:00 2001 From: mikeleo03 Date: Wed, 17 Jul 2024 00:42:15 +0700 Subject: [PATCH 05/30] [Feat] Service implementation --- .../services/DepartmentService.java | 22 +++++++ .../lecture_11/services/EmployeeService.java | 22 +++++++ .../lecture_11/services/SalaryService.java | 23 +++++++ .../lecture_11/services/TitleService.java | 23 +++++++ .../services/impl/DepartmentServiceImpl.java | 64 ++++++++++++++++++ .../services/impl/EmployeeServiceImpl.java | 64 ++++++++++++++++++ .../services/impl/SalaryServiceImpl.java | 65 +++++++++++++++++++ .../services/impl/TitleServiceImpl.java | 65 +++++++++++++++++++ 8 files changed, 348 insertions(+) create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java new file mode 100644 index 0000000..d5ee19c --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java @@ -0,0 +1,22 @@ +package com.example.lecture_11.services; + +import com.example.lecture_11.data.model.Department; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface DepartmentService { + // Retrieves a paginated list of {@link Department} entities. + Page findAll(Pageable pageable); + + // Retrieves an {@link Department} entity by its unique identifier. + Optional findById(String deptNo); + + // Saves or updates an {@link Department} entity in the database. + Department saveOrUpdate(Department department); + + // Deletes an {@link Department} entity from the database by its unique identifier. + void deleteById(String deptNo); +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java new file mode 100644 index 0000000..18d3108 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java @@ -0,0 +1,22 @@ +package com.example.lecture_11.services; + +import com.example.lecture_11.data.model.Employee; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface EmployeeService { + // Retrieves a paginated list of {@link Employee} entities. + Page findAll(Pageable pageable); + + // Retrieves an {@link Employee} entity by its unique identifier. + Optional findById(Integer empNo); + + // Saves or updates an {@link Employee} entity in the database. + Employee saveOrUpdate(Employee employee); + + // Deletes an {@link Employee} entity from the database by its unique identifier. + void deleteById(Integer empNo); +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java new file mode 100644 index 0000000..a7e4fff --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java @@ -0,0 +1,23 @@ +package com.example.lecture_11.services; + +import com.example.lecture_11.data.model.Salary; +import com.example.lecture_11.data.model.composite.SalaryId; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface SalaryService { + // Retrieves a paginated list of {@link Salary} entities. + Page findAll(Pageable pageable); + + // Retrieves an {@link Salary} entity by its unique identifier. + Optional findById(SalaryId id); + + // Saves or updates an {@link Salary} entity in the database. + Salary saveOrUpdate(Salary salary); + + // Deletes an {@link Salary} entity from the database by its unique identifier. + void deleteById(SalaryId id); +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java new file mode 100644 index 0000000..995f8e5 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java @@ -0,0 +1,23 @@ +package com.example.lecture_11.services; + +import com.example.lecture_11.data.model.Title; +import com.example.lecture_11.data.model.composite.TitleId; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface TitleService { + // Retrieves a paginated list of {@link Title} entities. + Page findAll(Pageable pageable); + + // Retrieves an {@link Title} entity by its unique identifier. + Optional<Title> findById(TitleId id); + + // Saves or updates an {@link Title} entity in the database. + Title saveOrUpdate(Title title); + + // Deletes an {@link Title} entity from the database by its unique identifier. + void deleteById(TitleId id); +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java new file mode 100644 index 0000000..62c963c --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java @@ -0,0 +1,64 @@ +package com.example.lecture_11.services.impl; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.example.lecture_11.data.model.Department; +import com.example.lecture_11.data.repository.DepartmentRepository; +import com.example.lecture_11.services.DepartmentService; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class DepartmentServiceImpl implements DepartmentService { + + private final DepartmentRepository departmentRepository; + + /** + * Retrieves a paginated list of {@link Department} entities. + * + * @param pageable The pagination and sorting parameters. + * @return A {@link Page} of {@link Department} entities. + */ + @Override + public Page<Department> findAll(Pageable pageable) { + return departmentRepository.findAll(pageable); + } + + /** + * Retrieves an {@link Department} entity by its unique identifier. + * + * @param deptNo The unique identifier of the {@link Department} entity to retrieve. + * @return An {@link Optional} containing the {@link Department} entity if found, or an empty {@link Optional} if not found. + */ + @Override + public Optional<Department> findById(String deptNo) { + return departmentRepository.findById(deptNo); + } + + /** + * Saves or updates an {@link Department} entity in the database. + * + * @param department The {@link Department} entity to be saved or updated. + * @return The saved or updated {@link Department} entity. + */ + @Override + public Department saveOrUpdate(Department department) { + return departmentRepository.save(department); + } + + /** + * Deletes an {@link Department} entity from the database by its unique identifier. + * + * @param deptNo The unique identifier of the {@link Department} entity to be deleted. + * @return No return value, as the operation is void. + */ + @Override + public void deleteById(String deptNo) { + departmentRepository.deleteById(deptNo); + } +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java new file mode 100644 index 0000000..9cca739 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java @@ -0,0 +1,64 @@ +package com.example.lecture_11.services.impl; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.example.lecture_11.data.model.Employee; +import com.example.lecture_11.data.repository.EmployeeRepository; +import com.example.lecture_11.services.EmployeeService; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class EmployeeServiceImpl implements EmployeeService { + + private final EmployeeRepository employeeRepository; + + /** + * Retrieves a paginated list of {@link Employee} entities. + * + * @param pageable The pagination and sorting parameters. + * @return A {@link Page} of {@link Employee} entities. + */ + @Override + public Page<Employee> findAll(Pageable pageable) { + return employeeRepository.findAll(pageable); + } + + /** + * Retrieves an {@link Employee} entity by its unique identifier. + * + * @param empNo The unique identifier of the {@link Employee} entity to retrieve. + * @return An {@link Optional} containing the {@link Employee} entity if found, or an empty {@link Optional} if not found. + */ + @Override + public Optional<Employee> findById(Integer empNo) { + return employeeRepository.findById(empNo); + } + + /** + * Saves or updates an {@link Employee} entity in the database. + * + * @param employee The {@link Employee} entity to be saved or updated. + * @return The saved or updated {@link Employee} entity. + */ + @Override + public Employee saveOrUpdate(Employee employee) { + return employeeRepository.save(employee); + } + + /** + * Deletes an {@link Employee} entity from the database by its unique identifier. + * + * @param empNo The unique identifier of the {@link Employee} entity to be deleted. + * @return No return value, as the operation is void. + */ + @Override + public void deleteById(Integer empNo) { + employeeRepository.deleteById(empNo); + } +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java new file mode 100644 index 0000000..166cc35 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java @@ -0,0 +1,65 @@ +package com.example.lecture_11.services.impl; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.example.lecture_11.data.model.Salary; +import com.example.lecture_11.data.model.composite.SalaryId; +import com.example.lecture_11.data.repository.SalaryRepository; +import com.example.lecture_11.services.SalaryService; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class SalaryServiceImpl implements SalaryService { + + private final SalaryRepository salaryRepository; + + /** + * Retrieves a paginated list of {@link Salary} entities. + * + * @param pageable The pagination and sorting parameters. + * @return A {@link Page} of {@link Salary} entities. + */ + @Override + public Page<Salary> findAll(Pageable pageable) { + return salaryRepository.findAll(pageable); + } + + /** + * Retrieves an {@link Salary} entity by its unique identifier. + * + * @param id The unique identifier of the {@link Salary} entity to retrieve. + * @return An {@link Optional} containing the {@link Salary} entity if found, or an empty {@link Optional} if not found. + */ + @Override + public Optional<Salary> findById(SalaryId id) { + return salaryRepository.findById(id); + } + + /** + * Saves or updates an {@link Salary} entity in the database. + * + * @param salary The {@link Salary} entity to be saved or updated. + * @return The saved or updated {@link Salary} entity. + */ + @Override + public Salary saveOrUpdate(Salary salary) { + return salaryRepository.save(salary); + } + + /** + * Deletes an {@link Salary} entity from the database by its unique identifier. + * + * @param id The unique identifier of the {@link Salary} entity to be deleted. + * @return No return value, as the operation is void. + */ + @Override + public void deleteById(SalaryId id) { + salaryRepository.deleteById(id); + } +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java new file mode 100644 index 0000000..e8d06ad --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java @@ -0,0 +1,65 @@ +package com.example.lecture_11.services.impl; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.example.lecture_11.data.model.Title; +import com.example.lecture_11.data.model.composite.TitleId; +import com.example.lecture_11.data.repository.TitleRepository; +import com.example.lecture_11.services.TitleService; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class TitleServiceImpl implements TitleService { + + private final TitleRepository titleRepository; + + /** + * Retrieves a paginated list of {@link Title} entities. + * + * @param pageable The pagination and sorting parameters. + * @return A {@link Page} of {@link Title} entities. + */ + @Override + public Page<Title> findAll(Pageable pageable) { + return titleRepository.findAll(pageable); + } + + /** + * Retrieves an {@link Title} entity by its unique identifier. + * + * @param id The unique identifier of the {@link Title} entity to retrieve. + * @return An {@link Optional} containing the {@link Title} entity if found, or an empty {@link Optional} if not found. + */ + @Override + public Optional<Title> findById(TitleId id) { + return titleRepository.findById(id); + } + + /** + * Saves or updates an {@link Title} entity in the database. + * + * @param title The {@link Title} entity to be saved or updated. + * @return The saved or updated {@link Title} entity. + */ + @Override + public Title saveOrUpdate(Title title) { + return titleRepository.save(title); + } + + /** + * Deletes an {@link Title} entity from the database by its unique identifier. + * + * @param id The unique identifier of the {@link Title} entity to be deleted. + * @return No return value, as the operation is void. + */ + @Override + public void deleteById(TitleId id) { + titleRepository.deleteById(id); + } +} From 0036d7b1cf9c003361fe96713b8a905148b39608 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 11:01:54 +0700 Subject: [PATCH 06/30] [Feat] Lombok all args constructor --- .../com/example/lecture_11/data/model/composite/DeptEmpId.java | 2 ++ .../example/lecture_11/data/model/composite/DeptManagerId.java | 2 ++ .../com/example/lecture_11/data/model/composite/SalaryId.java | 2 ++ .../com/example/lecture_11/data/model/composite/TitleId.java | 2 ++ 4 files changed, 8 insertions(+) diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java index fed319f..7057f19 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java @@ -2,9 +2,11 @@ import java.io.Serializable; +import lombok.AllArgsConstructor; import lombok.Data; @Data +@AllArgsConstructor public class DeptEmpId implements Serializable { private Integer empNo; private String deptNo; diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java index f70acab..c286684 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java @@ -2,9 +2,11 @@ import java.io.Serializable; +import lombok.AllArgsConstructor; import lombok.Data; @Data +@AllArgsConstructor public class DeptManagerId implements Serializable { private Integer empNo; private String deptNo; diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java index 7d6fb83..3e07c0e 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java @@ -3,9 +3,11 @@ import java.io.Serializable; import java.time.LocalDate; +import lombok.AllArgsConstructor; import lombok.Data; @Data +@AllArgsConstructor public class SalaryId implements Serializable { private Integer empNo; private LocalDate fromDate; diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java index 57cfd0b..ea69f0d 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java @@ -3,9 +3,11 @@ import java.io.Serializable; import java.time.LocalDate; +import lombok.AllArgsConstructor; import lombok.Data; @Data +@AllArgsConstructor public class TitleId implements Serializable { private Integer empNo; private String title; From ed3347fe37cd07450253db3098ee075ac836799c Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 11:02:06 +0700 Subject: [PATCH 07/30] [Feat] Implementation of REST APIs service --- .../controllers/DepartmentController.java | 98 +++++++++++++++++ .../controllers/EmployeeController.java | 98 +++++++++++++++++ .../controllers/SalaryController.java | 100 ++++++++++++++++++ .../controllers/TitleController.java | 100 ++++++++++++++++++ 4 files changed, 396 insertions(+) create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java create mode 100644 Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java new file mode 100644 index 0000000..96e78fd --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java @@ -0,0 +1,98 @@ +package com.example.lecture_11.controllers; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.lecture_11.data.model.Department; +import com.example.lecture_11.services.DepartmentService; + +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/departments") +@AllArgsConstructor +public class DepartmentController { + + private final DepartmentService departmentService; + + /** + * This method retrieves {@link Page} of {@link Department} from the database. + * + * @return ResponseEntity<List<Department>> - A response entity containing a pages of {@link Department}. + * If the pages is empty, it returns a HTTP status code 204 (No Content). + * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Department}. + */ + @GetMapping + public ResponseEntity<Page<Department>> findAll(Pageable pageable) { + Page<Department> departments = departmentService.findAll(pageable); + + if (departments.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(departments); + } + + /** + * This method retrieves an {@link Department} from the database by its deptNo. + * + * @param deptNo The unique identifier of the {@link Department}. + * @return ResponseEntity<Department> - A response entity containing the {@link Department} if found, or a 404 Not Found status code if not found. + */ + @GetMapping(value = "/{deptNo}") + public ResponseEntity<Department> findDepartmentById(@PathVariable("deptNo") String deptNo) { + Optional<Department> departmentOpt= departmentService.findById(deptNo); + + if(departmentOpt.isPresent()) { + return ResponseEntity.ok(departmentOpt.get()); + } + + return ResponseEntity.notFound().build(); + } + + /** + * This method saves or updates an {@link Department} to the database. + * + * @param department The department object to be saved. + * @return ResponseEntity<Department> - A response entity containing the saved {@link Department}. + * If the {@link Department} already exists in the database, it returns a HTTP status code 400 (Bad Request). + */ + @PostMapping + public ResponseEntity<Department> saveOrUpdate(@RequestBody Department department) { + Optional<Department> departmentOpt = departmentService.findById(department.getDeptNo()); + + if(departmentOpt.isPresent()) { + return ResponseEntity.badRequest().build(); + } + + return ResponseEntity.ok(departmentService.saveOrUpdate(department)); + } + + /** + * This method deletes an {@link Department} from the database by its deptNo. + * + * @param deptNo The unique identifier of the {@link Department} to be deleted. + * @return ResponseEntity<Department> - A response entity containing the deleted {@link Department} if found, or a 404 Not Found status code if not found. + */ + @DeleteMapping(value = "/{deptNo}") + public ResponseEntity<Department> deleteDepartment(@PathVariable(value = "deptNo") String deptNo) { + Optional<Department> departmentOpt = departmentService.findById(deptNo); + + if(departmentOpt.isPresent()) { + departmentService.deleteById(deptNo); + return ResponseEntity.ok().build(); + } + + return ResponseEntity.notFound().build(); + } +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java new file mode 100644 index 0000000..870a275 --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java @@ -0,0 +1,98 @@ +package com.example.lecture_11.controllers; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.lecture_11.data.model.Employee; +import com.example.lecture_11.services.EmployeeService; + +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/employees") +@AllArgsConstructor +public class EmployeeController { + + private final EmployeeService employeeService; + + /** + * This method retrieves {@link Page} of {@link Employee} from the database. + * + * @return ResponseEntity<List<Employee>> - A response entity containing a pages of {@link Employee}. + * If the pages is empty, it returns a HTTP status code 204 (No Content). + * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Employee}. + */ + @GetMapping + public ResponseEntity<Page<Employee>> findAll(Pageable pageable) { + Page<Employee> employees = employeeService.findAll(pageable); + + if (employees.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(employees); + } + + /** + * This method retrieves an {@link Employee} from the database by its empNo. + * + * @param empNo The unique identifier of the {@link Employee}. + * @return ResponseEntity<Employee> - A response entity containing the {@link Employee} if found, or a 404 Not Found status code if not found. + */ + @GetMapping(value = "/{empNo}") + public ResponseEntity<Employee> findEmployeeById(@PathVariable("empNo") Integer empNo) { + Optional<Employee> employeeOpt= employeeService.findById(empNo); + + if(employeeOpt.isPresent()) { + return ResponseEntity.ok(employeeOpt.get()); + } + + return ResponseEntity.notFound().build(); + } + + /** + * This method saves or updates an {@link Employee} to the database. + * + * @param employee The employee object to be saved. + * @return ResponseEntity<Employee> - A response entity containing the saved {@link Employee}. + * If the {@link Employee} already exists in the database, it returns a HTTP status code 400 (Bad Request). + */ + @PostMapping + public ResponseEntity<Employee> saveOrUpdate(@RequestBody Employee employee) { + Optional<Employee> employeeOpt = employeeService.findById(employee.getEmpNo()); + + if(employeeOpt.isPresent()) { + return ResponseEntity.badRequest().build(); + } + + return ResponseEntity.ok(employeeService.saveOrUpdate(employee)); + } + + /** + * This method deletes an {@link Employee} from the database by its empNo. + * + * @param empNo The unique identifier of the {@link Employee} to be deleted. + * @return ResponseEntity<Employee> - A response entity containing the deleted {@link Employee} if found, or a 404 Not Found status code if not found. + */ + @DeleteMapping(value = "/{empNo}") + public ResponseEntity<Employee> deleteEmployee(@PathVariable(value = "empNo") Integer empNo) { + Optional<Employee> employeeOpt = employeeService.findById(empNo); + + if(employeeOpt.isPresent()) { + employeeService.deleteById(empNo); + return ResponseEntity.ok().build(); + } + + return ResponseEntity.notFound().build(); + } +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java new file mode 100644 index 0000000..47ab89c --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java @@ -0,0 +1,100 @@ +package com.example.lecture_11.controllers; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.lecture_11.data.model.Salary; +import com.example.lecture_11.data.model.composite.SalaryId; +import com.example.lecture_11.services.SalaryService; + +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/salaries") +@AllArgsConstructor +public class SalaryController { + + private final SalaryService salaryService; + + /** + * This method retrieves {@link Page} of {@link Salary} from the database. + * + * @return ResponseEntity<List<Salary>> - A response entity containing a pages of {@link Salary}. + * If the pages is empty, it returns a HTTP status code 204 (No Content). + * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Salary}. + */ + @GetMapping + public ResponseEntity<Page<Salary>> findAll(Pageable pageable) { + Page<Salary> salaries = salaryService.findAll(pageable); + + if (salaries.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(salaries); + } + + /** + * This method retrieves an {@link Salary} from the database by its id. + * + * @param id The unique identifier of the {@link Salary}. + * @return ResponseEntity<Salary> - A response entity containing the {@link Salary} if found, or a 404 Not Found status code if not found. + */ + @GetMapping(value = "/{id}") + public ResponseEntity<Salary> findSalaryById(@PathVariable("id") SalaryId id) { + Optional<Salary> salaryOpt= salaryService.findById(id); + + if(salaryOpt.isPresent()) { + return ResponseEntity.ok(salaryOpt.get()); + } + + return ResponseEntity.notFound().build(); + } + + /** + * This method saves or updates a {@link Salary} to the database. + * + * @param salary The salary object to be saved. + * @return ResponseEntity<Salary> - A response entity containing the saved {@link Salary}. + * If the {@link Salary} already exists in the database, it returns a HTTP status code 400 (Bad Request). + */ + @PostMapping + public ResponseEntity<Salary> saveOrUpdate(@RequestBody Salary salary) { + SalaryId salaryId = new SalaryId(salary.getEmpNo(), salary.getFromDate()); + Optional<Salary> salaryOpt = salaryService.findById(salaryId); + + if(salaryOpt.isPresent()) { + return ResponseEntity.badRequest().build(); + } + + return ResponseEntity.ok(salaryService.saveOrUpdate(salary)); + } + + /** + * This method deletes an {@link Salary} from the database by its id. + * + * @param id The unique identifier of the {@link Salary} to be deleted. + * @return ResponseEntity<Salary> - A response entity containing the deleted {@link Salary} if found, or a 404 Not Found status code if not found. + */ + @DeleteMapping(value = "/{id}") + public ResponseEntity<Salary> deleteSalary(@PathVariable(value = "id") SalaryId id) { + Optional<Salary> salaryOpt = salaryService.findById(id); + + if(salaryOpt.isPresent()) { + salaryService.deleteById(id); + return ResponseEntity.ok().build(); + } + + return ResponseEntity.notFound().build(); + } +} diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java new file mode 100644 index 0000000..d8be89d --- /dev/null +++ b/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java @@ -0,0 +1,100 @@ +package com.example.lecture_11.controllers; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.lecture_11.data.model.Title; +import com.example.lecture_11.data.model.composite.TitleId; +import com.example.lecture_11.services.TitleService; + +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/titles") +@AllArgsConstructor +public class TitleController { + + private final TitleService titleService; + + /** + * This method retrieves {@link Page} of {@link Title} from the database. + * + * @return ResponseEntity<List<Title>> - A response entity containing a pages of {@link Title}. + * If the pages is empty, it returns a HTTP status code 204 (No Content). + * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Title}. + */ + @GetMapping + public ResponseEntity<Page<Title>> findAll(Pageable pageable) { + Page<Title> titles = titleService.findAll(pageable); + + if (titles.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(titles); + } + + /** + * This method retrieves an {@link Title} from the database by its id. + * + * @param id The unique identifier of the {@link Title}. + * @return ResponseEntity<Title> - A response entity containing the {@link Title} if found, or a 404 Not Found status code if not found. + */ + @GetMapping(value = "/{id}") + public ResponseEntity<Title> findTitleById(@PathVariable("id") TitleId id) { + Optional<Title> titleOpt= titleService.findById(id); + + if(titleOpt.isPresent()) { + return ResponseEntity.ok(titleOpt.get()); + } + + return ResponseEntity.notFound().build(); + } + + /** + * This method saves or updates a {@link Title} to the database. + * + * @param title The title object to be saved. + * @return ResponseEntity<Title> - A response entity containing the saved {@link Title}. + * If the {@link Title} already exists in the database, it returns a HTTP status code 400 (Bad Request). + */ + @PostMapping + public ResponseEntity<Title> saveOrUpdate(@RequestBody Title title) { + TitleId titleId = new TitleId(title.getEmpNo(), title.getTitle(), title.getFromDate()); + Optional<Title> titleOpt = titleService.findById(titleId); + + if(titleOpt.isPresent()) { + return ResponseEntity.badRequest().build(); + } + + return ResponseEntity.ok(titleService.saveOrUpdate(title)); + } + + /** + * This method deletes an {@link Title} from the database by its id. + * + * @param id The unique identifier of the {@link Title} to be deleted. + * @return ResponseEntity<Title> - A response entity containing the deleted {@link Title} if found, or a 404 Not Found status code if not found. + */ + @DeleteMapping(value = "/{id}") + public ResponseEntity<Title> deleteTitle(@PathVariable(value = "id") TitleId id) { + Optional<Title> titleOpt = titleService.findById(id); + + if(titleOpt.isPresent()) { + titleService.deleteById(id); + return ResponseEntity.ok().build(); + } + + return ResponseEntity.notFound().build(); + } +} From 272817f99d91ef0966c96bcd655b3c03d73156c9 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 14:14:14 +0700 Subject: [PATCH 08/30] [Feat] Code resctructure (unfinished) --- Week 06/Lecture 11/Assignment 01/README.md | 260 ++++++++++++++++++ .../{ => Assignment 01}/lecture_11/.gitignore | 0 .../.mvn/wrapper/maven-wrapper.properties | 0 .../{ => Assignment 01}/lecture_11/mvnw | 0 .../{ => Assignment 01}/lecture_11/mvnw.cmd | 0 .../{ => Assignment 01}/lecture_11/pom.xml | 3 +- .../Assignment 01/lecture_11/run.bat | 3 + .../Assignment 01/lecture_11/run.sh | 3 + .../lecture_11/Lecture11Application.java | 0 .../controllers/DepartmentController.java | 0 .../controllers/EmployeeController.java | 0 .../controllers/SalaryController.java | 0 .../controllers/TitleController.java | 0 .../lecture_11/data/model/Department.java | 0 .../lecture_11/data/model/DeptEmp.java | 0 .../lecture_11/data/model/DeptManager.java | 0 .../lecture_11/data/model/Employee.java | 3 - .../example/lecture_11/data/model/Salary.java | 0 .../example/lecture_11/data/model/Title.java | 0 .../data/model/composite/DeptEmpId.java | 2 + .../data/model/composite/DeptManagerId.java | 2 + .../data/model/composite/SalaryId.java | 2 + .../data/model/composite/TitleId.java | 2 + .../data/repository/DepartmentRepository.java | 0 .../data/repository/DeptEmpRepository.java | 0 .../repository/DeptManagerRepository.java | 0 .../data/repository/EmployeeRepository.java | 0 .../data/repository/SalaryRepository.java | 0 .../data/repository/TitleRepository.java | 0 .../services/DepartmentService.java | 0 .../lecture_11/services/EmployeeService.java | 0 .../lecture_11/services/SalaryService.java | 0 .../lecture_11/services/TitleService.java | 0 .../services/impl/DepartmentServiceImpl.java | 0 .../services/impl/EmployeeServiceImpl.java | 0 .../services/impl/SalaryServiceImpl.java | 0 .../services/impl/TitleServiceImpl.java | 0 .../src/main/resources/application.properties | 8 + .../lecture_11/src/main/resources/data.sql | 149 ++++++++++ .../lecture_11/Lecture11ApplicationTests.java | 0 Week 06/Lecture 11/README.md | 0 .../src/main/resources/application.properties | 1 - 42 files changed, 433 insertions(+), 5 deletions(-) create mode 100644 Week 06/Lecture 11/Assignment 01/README.md rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/.gitignore (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/.mvn/wrapper/maven-wrapper.properties (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/mvnw (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/mvnw.cmd (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/pom.xml (98%) create mode 100644 Week 06/Lecture 11/Assignment 01/lecture_11/run.bat create mode 100644 Week 06/Lecture 11/Assignment 01/lecture_11/run.sh rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java (90%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java (84%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java (84%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java (85%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java (86%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java (100%) rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java (100%) create mode 100644 Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/application.properties create mode 100644 Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/data.sql rename Week 06/Lecture 11/{ => Assignment 01}/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java (100%) delete mode 100644 Week 06/Lecture 11/README.md delete mode 100644 Week 06/Lecture 11/lecture_11/src/main/resources/application.properties diff --git a/Week 06/Lecture 11/Assignment 01/README.md b/Week 06/Lecture 11/Assignment 01/README.md new file mode 100644 index 0000000..60a97d5 --- /dev/null +++ b/Week 06/Lecture 11/Assignment 01/README.md @@ -0,0 +1,260 @@ +# πŸ‘¨πŸ»β€πŸ« Lecture 11 - Spring Data JPA +> This repository is created as a part of assignment for Lecture 11 - Spring Data JPA + +## πŸ“ Assignment 01 - Implementation of Model, JPA, Repositories, Services, and REST APIs +### 🌳 Project Structure +```bash +lecture_11 +β”œβ”€β”€ .mvn/wrapper/ +β”‚ └── maven-wrapper.properties +β”œβ”€β”€ src/main/ +β”‚ β”œβ”€β”€ java/com/example/lecture_11/ +β”‚ β”‚ β”œβ”€β”€ controller/ +β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentController.java +β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeController.java +β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryController.java +β”‚ β”‚ β”‚ └── TitleController.java +β”‚ β”‚ β”œβ”€β”€ data/ +β”‚ β”‚ β”‚ β”œβ”€β”€ model/ +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ composite/ +β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DeptEmpId.java +β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DeptManagerId.java +β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryId.java +β”‚ β”‚ β”‚ β”‚ β”‚ └── TitleId.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Department.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DeptEmp.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DeptManager.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Employee.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Salary.java +β”‚ β”‚ β”‚ β”‚ └── Title.java +β”‚ β”‚ β”‚ └── repository/ +β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentRepository.java +β”‚ β”‚ β”‚ β”œβ”€β”€ DeptEmpRepository.java +β”‚ β”‚ β”‚ β”œβ”€β”€ DeptManagerRepository.java +β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeRepository.java +β”‚ β”‚ β”‚ β”œβ”€β”€ Salary.Repositoryjava +β”‚ β”‚ β”‚ └── TitleRepository.java +β”‚ β”‚ β”œβ”€β”€ service/ +β”‚ β”‚ β”‚ β”œβ”€β”€ impl/ +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentServiceImpl.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeServiceImpl.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryServiceImpl.java +β”‚ β”‚ β”‚ β”‚ └── TitleServiceImpl.java +β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentService.java +β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeService.java +β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryService.java +β”‚ β”‚ β”‚ └── TitleService.java +β”‚ β”‚ └── Lecture11Application.java +β”‚ └── resources/ +β”‚ └── application.properties +β”œβ”€β”€ .gitignore +β”œβ”€β”€ mvnw +β”œβ”€β”€ mvnw.cmd +β”œβ”€β”€ pom.xml +β”œβ”€β”€ run.bat +└── run.sh +``` + +### 🧩 SQL Query Data +Here is the SQL query to create the database, table, and instantiate some data. +```sql +-- Create the database +CREATE DATABASE week6_lecture11; + +-- Use the database +USE week6_lecture11; + +-- Create employees table +CREATE TABLE employees ( + emp_no INT AUTO_INCREMENT PRIMARY KEY, + birth_date DATE NOT NULL, + first_name VARCHAR(14) NOT NULL, + last_name VARCHAR(16) NOT NULL, + gender ENUM('M', 'F') NOT NULL, + hire_date DATE NOT NULL +); + +-- Create departments table +CREATE TABLE departments ( + dept_no CHAR(4) PRIMARY KEY, + dept_name VARCHAR(40) NOT NULL UNIQUE +); + +-- Create dept_emp table +CREATE TABLE dept_emp ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, dept_no), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE +); + +-- Create dept_manager table +CREATE TABLE dept_manager ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, dept_no), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE +); + +-- Create salaries table +CREATE TABLE salaries ( + emp_no INT NOT NULL, + from_date DATE NOT NULL, + salary INT NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, from_date), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE +); + +-- Create titles table +CREATE TABLE titles ( + emp_no INT NOT NULL, + title VARCHAR(50) NOT NULL, + from_date DATE NOT NULL, + to_date DATE, + PRIMARY KEY (emp_no, title, from_date), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE +); +``` + +Here is the query to insert some generated dummy data +```sql +-- Insert employees +INSERT INTO employees (birth_date, first_name, last_name, gender, hire_date) VALUES +('1980-01-01', 'John', 'Doe', 'M', '2000-01-01'), +('1985-05-23', 'Jane', 'Smith', 'F', '2005-05-01'), +('1990-07-11', 'Alice', 'Johnson', 'F', '2010-06-01'), +('1975-02-14', 'Bob', 'Brown', 'M', '1995-03-01'), +('1988-12-25', 'Charlie', 'Davis', 'M', '2008-12-01'), +('1981-04-10', 'David', 'Evans', 'M', '2001-04-10'), +('1986-08-15', 'Laura', 'Wilson', 'F', '2006-08-15'), +('1991-03-22', 'Karen', 'Garcia', 'F', '2011-03-22'), +('1976-06-12', 'Paul', 'Martinez', 'M', '1996-06-12'), +('1989-11-30', 'Nancy', 'Rodriguez', 'F', '2009-11-30'); + +-- Insert departments +INSERT INTO departments (dept_no, dept_name) VALUES +('d001', 'Marketing'), +('d002', 'Finance'), +('d003', 'Human Resources'), +('d004', 'Engineering'), +('d005', 'Sales'); + +-- Insert dept_emp +INSERT INTO dept_emp (emp_no, dept_no, from_date, to_date) VALUES +(1, 'd001', '2000-01-01', '2002-01-01'), +(1, 'd002', '2002-01-01', '9999-01-01'), +(2, 'd002', '2005-05-01', '2010-05-01'), +(2, 'd003', '2010-05-01', '9999-01-01'), +(3, 'd003', '2010-06-01', '9999-01-01'), +(3, 'd004', '2011-01-01', '9999-01-01'), +(4, 'd004', '1995-03-01', '9999-01-01'), +(4, 'd005', '2000-01-01', '9999-01-01'), +(5, 'd001', '2008-12-01', '9999-01-01'), +(5, 'd005', '2010-01-01', '9999-01-01'), +(6, 'd002', '2001-04-10', '2003-04-10'), +(6, 'd003', '2003-04-10', '9999-01-01'), +(7, 'd003', '2006-08-15', '2011-08-15'), +(7, 'd004', '2011-08-15', '9999-01-01'), +(8, 'd001', '2011-03-22', '9999-01-01'), +(9, 'd004', '1996-06-12', '2006-06-12'), +(9, 'd005', '2006-06-12', '9999-01-01'), +(10, 'd005', '2009-11-30', '9999-01-01'); + +-- Insert dept_manager +INSERT INTO dept_manager (emp_no, dept_no, from_date, to_date) VALUES +(1, 'd001', '2000-01-01', '2002-01-01'), +(2, 'd002', '2005-05-01', '2010-05-01'), +(3, 'd003', '2010-06-01', '2011-01-01'); + +-- Insert salaries +INSERT INTO salaries (emp_no, salary, from_date, to_date) VALUES +(1, 60000, '2000-01-01', '2002-01-01'), +(1, 65000, '2002-01-01', '9999-01-01'), +(2, 75000, '2005-05-01', '2010-05-01'), +(2, 80000, '2010-05-01', '9999-01-01'), +(3, 80000, '2010-06-01', '2011-01-01'), +(3, 85000, '2011-01-01', '9999-01-01'), +(4, 90000, '1995-03-01', '2000-01-01'), +(4, 95000, '2000-01-01', '9999-01-01'), +(5, 85000, '2008-12-01', '2010-01-01'), +(5, 90000, '2010-01-01', '9999-01-01'), +(6, 65000, '2001-04-10', '2003-04-10'), +(6, 70000, '2003-04-10', '9999-01-01'), +(7, 70000, '2006-08-15', '2011-08-15'), +(7, 75000, '2011-08-15', '9999-01-01'), +(8, 72000, '2011-03-22', '9999-01-01'), +(9, 95000, '1996-06-12', '2006-06-12'), +(9, 100000, '2006-06-12', '9999-01-01'), +(10, 86000, '2009-11-30', '9999-01-01'); + +-- Insert titles +INSERT INTO titles (emp_no, title, from_date, to_date) VALUES +(1, 'Manager', '2000-01-01', '2002-01-01'), +(1, 'Senior Manager', '2002-01-01', '9999-01-01'), +(2, 'Analyst', '2005-05-01', '2010-05-01'), +(2, 'Senior Analyst', '2010-05-01', '9999-01-01'), +(3, 'HR Specialist', '2010-06-01', '2011-01-01'), +(3, 'HR Manager', '2011-01-01', '9999-01-01'), +(4, 'Engineer', '1995-03-01', '2000-01-01'), +(4, 'Senior Engineer', '2000-01-01', '9999-01-01'), +(5, 'Sales Representative', '2008-12-01', '2010-01-01'), +(5, 'Senior Sales Representative', '2010-01-01', '9999-01-01'), +(6, 'Finance Specialist', '2001-04-10', '2003-04-10'), +(6, 'Senior Finance Specialist', '2003-04-10', '9999-01-01'), +(7, 'HR Manager', '2006-08-15', '2011-08-15'), +(7, 'Senior HR Manager', '2011-08-15', '9999-01-01'), +(8, 'Marketing Specialist', '2011-03-22', '9999-01-01'), +(9, 'Senior Engineer', '1996-06-12', '2006-06-12'), +(9, 'Chief Engineer', '2006-06-12', '9999-01-01'), +(10, 'Senior Sales Representative', '2009-11-30', '9999-01-01'); +``` + +All the MySQL queries is available on [this file](/Week%2006/Lecture%2011/lecture_11/src/main/resources/data.sql). Here is the query to drop the database +```sql +-- Drop the database +DROP DATABASE IF EXISTS week6_lecture11; +``` + +Also don't forget to configure [application properties](/Week%2006/Lecture%2011/lecture_11/src/main/resources/application.propertiess) with this format +```java +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.url=jdbc:mysql://localhost:3306/<your_database> +spring.datasource.username=<your_user_name> +spring.datasource.password=<your_password> +``` + +and don't forget to add this +```java +spring.datasource.initialization-mode=always +spring.jpa.hibernate.ddl-auto=update +``` +to do database seeding using JPA Hibernate. + +### βš™οΈ How to run the program +1. Go to the `lecture_11` directory by using this command + ```bash + $ cd lecture_11 + ``` +2. Make sure you have maven installed on your computer, use `mvn -v` to check the version. +3. If you are using windows, you can run the program by using this command. + ```bash + $ ./run.bat + ``` + And if you are using Linux, you can run the program by using this command. + ```bash + $ chmod +x run.sh + $ ./run.sh + ``` + +If all the instruction is well executed, Open [localhost:8080](http://localhost:8080) to see that the REST APIs is now works. + +### πŸ“¬ Postman Collection + +Here is the [postman collection] you can use to demo the API functionality. \ No newline at end of file diff --git a/Week 06/Lecture 11/lecture_11/.gitignore b/Week 06/Lecture 11/Assignment 01/lecture_11/.gitignore similarity index 100% rename from Week 06/Lecture 11/lecture_11/.gitignore rename to Week 06/Lecture 11/Assignment 01/lecture_11/.gitignore diff --git a/Week 06/Lecture 11/lecture_11/.mvn/wrapper/maven-wrapper.properties b/Week 06/Lecture 11/Assignment 01/lecture_11/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from Week 06/Lecture 11/lecture_11/.mvn/wrapper/maven-wrapper.properties rename to Week 06/Lecture 11/Assignment 01/lecture_11/.mvn/wrapper/maven-wrapper.properties diff --git a/Week 06/Lecture 11/lecture_11/mvnw b/Week 06/Lecture 11/Assignment 01/lecture_11/mvnw similarity index 100% rename from Week 06/Lecture 11/lecture_11/mvnw rename to Week 06/Lecture 11/Assignment 01/lecture_11/mvnw diff --git a/Week 06/Lecture 11/lecture_11/mvnw.cmd b/Week 06/Lecture 11/Assignment 01/lecture_11/mvnw.cmd similarity index 100% rename from Week 06/Lecture 11/lecture_11/mvnw.cmd rename to Week 06/Lecture 11/Assignment 01/lecture_11/mvnw.cmd diff --git a/Week 06/Lecture 11/lecture_11/pom.xml b/Week 06/Lecture 11/Assignment 01/lecture_11/pom.xml similarity index 98% rename from Week 06/Lecture 11/lecture_11/pom.xml rename to Week 06/Lecture 11/Assignment 01/lecture_11/pom.xml index bc75dd1..ed71858 100644 --- a/Week 06/Lecture 11/lecture_11/pom.xml +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/pom.xml @@ -10,7 +10,7 @@ </parent> <groupId>com.example</groupId> <artifactId>lecture_11</artifactId> - <version>0.0.1-SNAPSHOT</version> + <version>1.0-SNAPSHOT</version> <name>lecture_11</name> <description>Demo project for Spring Boot</description> <url/> @@ -54,6 +54,7 @@ <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> + <scope>runtime</scope> </dependency> <!-- Lombok Annotation --> diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/run.bat b/Week 06/Lecture 11/Assignment 01/lecture_11/run.bat new file mode 100644 index 0000000..1dee2cc --- /dev/null +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/run.bat @@ -0,0 +1,3 @@ +@echo off +echo Building the project with Maven... +mvn clean install && java -jar target/lecture_11-1.0-SNAPSHOT.jar \ No newline at end of file diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/run.sh b/Week 06/Lecture 11/Assignment 01/lecture_11/run.sh new file mode 100644 index 0000000..7b72a65 --- /dev/null +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo Building the project with Maven... +mvn clean install && java -jar target/lecture_11-1.0-SNAPSHOT.jar \ No newline at end of file diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/Lecture11Application.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Department.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java similarity index 90% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java index d3c868e..5367cf0 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Employee.java @@ -4,8 +4,6 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -38,7 +36,6 @@ public class Employee { private String lastName; @Column(columnDefinition = "enum('M','F')", nullable = false) - @Enumerated(EnumType.STRING) private String gender; @Temporal(TemporalType.DATE) diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java similarity index 84% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java index 7057f19..c3839a5 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java @@ -2,10 +2,12 @@ import java.io.Serializable; +import jakarta.persistence.Embeddable; import lombok.AllArgsConstructor; import lombok.Data; @Data +@Embeddable @AllArgsConstructor public class DeptEmpId implements Serializable { private Integer empNo; diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java similarity index 84% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java index c286684..95d2f64 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java @@ -2,10 +2,12 @@ import java.io.Serializable; +import jakarta.persistence.Embeddable; import lombok.AllArgsConstructor; import lombok.Data; @Data +@Embeddable @AllArgsConstructor public class DeptManagerId implements Serializable { private Integer empNo; diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java similarity index 85% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java index 3e07c0e..1ca65dc 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java @@ -3,10 +3,12 @@ import java.io.Serializable; import java.time.LocalDate; +import jakarta.persistence.Embeddable; import lombok.AllArgsConstructor; import lombok.Data; @Data +@Embeddable @AllArgsConstructor public class SalaryId implements Serializable { private Integer empNo; diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java similarity index 86% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java index ea69f0d..2e717d6 100644 --- a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java @@ -3,10 +3,12 @@ import java.io.Serializable; import java.time.LocalDate; +import jakarta.persistence.Embeddable; import lombok.AllArgsConstructor; import lombok.Data; @Data +@Embeddable @AllArgsConstructor public class TitleId implements Serializable { private Integer empNo; diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/DepartmentRepository.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptEmpRepository.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/DeptManagerRepository.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/EmployeeRepository.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/SalaryRepository.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/repository/TitleRepository.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java diff --git a/Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/application.properties b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/application.properties new file mode 100644 index 0000000..af2c18f --- /dev/null +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.application.name=lecture_11 + +spring.datasource.url=jdbc:mysql://localhost:3308/week6_lecture11?allowPublicKeyRetrieval=true&useSSL=false +spring.datasource.username=root +spring.datasource.password=Michaeleon16606_ +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.initialization-mode=always +spring.jpa.hibernate.ddl-auto=create-drop \ No newline at end of file diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/data.sql b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/data.sql new file mode 100644 index 0000000..ff17cbb --- /dev/null +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/data.sql @@ -0,0 +1,149 @@ +-- Database schema initializer +-- Create employees table +CREATE TABLE employees ( + emp_no INT AUTO_INCREMENT PRIMARY KEY, + birth_date DATE NOT NULL, + first_name VARCHAR(14) NOT NULL, + last_name VARCHAR(16) NOT NULL, + gender ENUM('M', 'F') NOT NULL, + hire_date DATE NOT NULL +); + +-- Create departments table +CREATE TABLE departments ( + dept_no CHAR(4) PRIMARY KEY, + dept_name VARCHAR(40) NOT NULL UNIQUE +); + +-- Create dept_emp table +CREATE TABLE dept_emp ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, dept_no), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE +); + +-- Create dept_manager table +CREATE TABLE dept_manager ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, dept_no), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE +); + +-- Create salaries table +CREATE TABLE salaries ( + emp_no INT NOT NULL, + from_date DATE NOT NULL, + salary INT NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, from_date), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE +); + +-- Create titles table +CREATE TABLE titles ( + emp_no INT NOT NULL, + title VARCHAR(50) NOT NULL, + from_date DATE NOT NULL, + to_date DATE, + PRIMARY KEY (emp_no, title, from_date), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE +); + +-- Database Initial Seeding +-- Insert employees +INSERT INTO employees (birth_date, first_name, last_name, gender, hire_date) VALUES +('1980-01-01', 'John', 'Doe', 'M', '2000-01-01'), +('1985-05-23', 'Jane', 'Smith', 'F', '2005-05-01'), +('1990-07-11', 'Alice', 'Johnson', 'F', '2010-06-01'), +('1975-02-14', 'Bob', 'Brown', 'M', '1995-03-01'), +('1988-12-25', 'Charlie', 'Davis', 'M', '2008-12-01'), +('1981-04-10', 'David', 'Evans', 'M', '2001-04-10'), +('1986-08-15', 'Laura', 'Wilson', 'F', '2006-08-15'), +('1991-03-22', 'Karen', 'Garcia', 'F', '2011-03-22'), +('1976-06-12', 'Paul', 'Martinez', 'M', '1996-06-12'), +('1989-11-30', 'Nancy', 'Rodriguez', 'F', '2009-11-30'); + +-- Insert departments +INSERT INTO departments (dept_no, dept_name) VALUES +('d001', 'Marketing'), +('d002', 'Finance'), +('d003', 'Human Resources'), +('d004', 'Engineering'), +('d005', 'Sales'); + +-- Insert dept_emp +INSERT INTO dept_emp (emp_no, dept_no, from_date, to_date) VALUES +(1, 'd001', '2000-01-01', '2002-01-01'), +(1, 'd002', '2002-01-01', '9999-01-01'), +(2, 'd002', '2005-05-01', '2010-05-01'), +(2, 'd003', '2010-05-01', '9999-01-01'), +(3, 'd003', '2010-06-01', '9999-01-01'), +(3, 'd004', '2011-01-01', '9999-01-01'), +(4, 'd004', '1995-03-01', '9999-01-01'), +(4, 'd005', '2000-01-01', '9999-01-01'), +(5, 'd001', '2008-12-01', '9999-01-01'), +(5, 'd005', '2010-01-01', '9999-01-01'), +(6, 'd002', '2001-04-10', '2003-04-10'), +(6, 'd003', '2003-04-10', '9999-01-01'), +(7, 'd003', '2006-08-15', '2011-08-15'), +(7, 'd004', '2011-08-15', '9999-01-01'), +(8, 'd001', '2011-03-22', '9999-01-01'), +(9, 'd004', '1996-06-12', '2006-06-12'), +(9, 'd005', '2006-06-12', '9999-01-01'), +(10, 'd005', '2009-11-30', '9999-01-01'); + +-- Insert dept_manager +INSERT INTO dept_manager (emp_no, dept_no, from_date, to_date) VALUES +(1, 'd001', '2000-01-01', '2002-01-01'), +(2, 'd002', '2005-05-01', '2010-05-01'), +(3, 'd003', '2010-06-01', '2011-01-01'); + +-- Insert salaries +INSERT INTO salaries (emp_no, salary, from_date, to_date) VALUES +(1, 60000, '2000-01-01', '2002-01-01'), +(1, 65000, '2002-01-01', '9999-01-01'), +(2, 75000, '2005-05-01', '2010-05-01'), +(2, 80000, '2010-05-01', '9999-01-01'), +(3, 80000, '2010-06-01', '2011-01-01'), +(3, 85000, '2011-01-01', '9999-01-01'), +(4, 90000, '1995-03-01', '2000-01-01'), +(4, 95000, '2000-01-01', '9999-01-01'), +(5, 85000, '2008-12-01', '2010-01-01'), +(5, 90000, '2010-01-01', '9999-01-01'), +(6, 65000, '2001-04-10', '2003-04-10'), +(6, 70000, '2003-04-10', '9999-01-01'), +(7, 70000, '2006-08-15', '2011-08-15'), +(7, 75000, '2011-08-15', '9999-01-01'), +(8, 72000, '2011-03-22', '9999-01-01'), +(9, 95000, '1996-06-12', '2006-06-12'), +(9, 100000, '2006-06-12', '9999-01-01'), +(10, 86000, '2009-11-30', '9999-01-01'); + +-- Insert titles +INSERT INTO titles (emp_no, title, from_date, to_date) VALUES +(1, 'Manager', '2000-01-01', '2002-01-01'), +(1, 'Senior Manager', '2002-01-01', '9999-01-01'), +(2, 'Analyst', '2005-05-01', '2010-05-01'), +(2, 'Senior Analyst', '2010-05-01', '9999-01-01'), +(3, 'HR Specialist', '2010-06-01', '2011-01-01'), +(3, 'HR Manager', '2011-01-01', '9999-01-01'), +(4, 'Engineer', '1995-03-01', '2000-01-01'), +(4, 'Senior Engineer', '2000-01-01', '9999-01-01'), +(5, 'Sales Representative', '2008-12-01', '2010-01-01'), +(5, 'Senior Sales Representative', '2010-01-01', '9999-01-01'), +(6, 'Finance Specialist', '2001-04-10', '2003-04-10'), +(6, 'Senior Finance Specialist', '2003-04-10', '9999-01-01'), +(7, 'HR Manager', '2006-08-15', '2011-08-15'), +(7, 'Senior HR Manager', '2011-08-15', '9999-01-01'), +(8, 'Marketing Specialist', '2011-03-22', '9999-01-01'), +(9, 'Senior Engineer', '1996-06-12', '2006-06-12'), +(9, 'Chief Engineer', '2006-06-12', '9999-01-01'), +(10, 'Senior Sales Representative', '2009-11-30', '9999-01-01'); \ No newline at end of file diff --git a/Week 06/Lecture 11/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java similarity index 100% rename from Week 06/Lecture 11/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java rename to Week 06/Lecture 11/Assignment 01/lecture_11/src/test/java/com/example/lecture_11/Lecture11ApplicationTests.java diff --git a/Week 06/Lecture 11/README.md b/Week 06/Lecture 11/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/Week 06/Lecture 11/lecture_11/src/main/resources/application.properties b/Week 06/Lecture 11/lecture_11/src/main/resources/application.properties deleted file mode 100644 index da416dd..0000000 --- a/Week 06/Lecture 11/lecture_11/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=lecture_11 From cbfc073f2434bb9a796284ee697ed23437d746e0 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 14:24:07 +0700 Subject: [PATCH 09/30] [Fix] Update identifier for composite keys --- .../com/example/lecture_11/data/model/DeptEmp.java | 9 +++------ .../example/lecture_11/data/model/DeptManager.java | 9 +++------ .../com/example/lecture_11/data/model/Salary.java | 10 +++------- .../com/example/lecture_11/data/model/Title.java | 13 +++---------- .../lecture_11/data/model/composite/DeptEmpId.java | 2 -- .../data/model/composite/DeptManagerId.java | 2 -- .../lecture_11/data/model/composite/SalaryId.java | 2 -- .../lecture_11/data/model/composite/TitleId.java | 2 -- 8 files changed, 12 insertions(+), 37 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java index 0fecc37..81e78a9 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java @@ -5,8 +5,8 @@ import com.example.lecture_11.data.model.composite.DeptEmpId; import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.Id; import jakarta.persistence.IdClass; import jakarta.persistence.Table; import jakarta.persistence.Temporal; @@ -25,11 +25,8 @@ @AllArgsConstructor public class DeptEmp { - @Id - private Integer empNo; - - @Id - private String deptNo; + @EmbeddedId + private DeptEmpId id; @Temporal(TemporalType.DATE) @Column(nullable = false) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java index 6729ee0..56d66c7 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java @@ -5,8 +5,8 @@ import com.example.lecture_11.data.model.composite.DeptManagerId; import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.Id; import jakarta.persistence.IdClass; import jakarta.persistence.Table; import jakarta.persistence.Temporal; @@ -25,11 +25,8 @@ @AllArgsConstructor public class DeptManager { - @Id - private Integer empNo; - - @Id - private String deptNo; + @EmbeddedId + private DeptManagerId id; @Temporal(TemporalType.DATE) @Column(nullable = false) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java index d3902b9..c67608f 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java @@ -5,8 +5,8 @@ import com.example.lecture_11.data.model.composite.SalaryId; import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.Id; import jakarta.persistence.IdClass; import jakarta.persistence.Table; import jakarta.persistence.Temporal; @@ -25,16 +25,12 @@ @AllArgsConstructor public class Salary { - @Id - private Integer empNo; + @EmbeddedId + private SalaryId id; @Column(nullable = false) private Integer salary; - @Id - @Temporal(TemporalType.DATE) - private LocalDate fromDate; - @Temporal(TemporalType.DATE) @Column(nullable = false) private LocalDate toDate; diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java index 5ef4e9a..212ff93 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java @@ -5,8 +5,8 @@ import com.example.lecture_11.data.model.composite.TitleId; import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.Id; import jakarta.persistence.IdClass; import jakarta.persistence.Table; import jakarta.persistence.Temporal; @@ -25,15 +25,8 @@ @AllArgsConstructor public class Title { - @Id - private Integer empNo; - - @Id - private String title; - - @Id - @Temporal(TemporalType.DATE) - private LocalDate fromDate; + @EmbeddedId + private TitleId id; @Temporal(TemporalType.DATE) @Column(nullable = false) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java index c3839a5..57f0297 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java @@ -3,12 +3,10 @@ import java.io.Serializable; import jakarta.persistence.Embeddable; -import lombok.AllArgsConstructor; import lombok.Data; @Data @Embeddable -@AllArgsConstructor public class DeptEmpId implements Serializable { private Integer empNo; private String deptNo; diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java index 95d2f64..b3efca9 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java @@ -3,12 +3,10 @@ import java.io.Serializable; import jakarta.persistence.Embeddable; -import lombok.AllArgsConstructor; import lombok.Data; @Data @Embeddable -@AllArgsConstructor public class DeptManagerId implements Serializable { private Integer empNo; private String deptNo; diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java index 1ca65dc..6001dd8 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java @@ -4,12 +4,10 @@ import java.time.LocalDate; import jakarta.persistence.Embeddable; -import lombok.AllArgsConstructor; import lombok.Data; @Data @Embeddable -@AllArgsConstructor public class SalaryId implements Serializable { private Integer empNo; private LocalDate fromDate; diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java index 2e717d6..07e1e85 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java @@ -4,12 +4,10 @@ import java.time.LocalDate; import jakarta.persistence.Embeddable; -import lombok.AllArgsConstructor; import lombok.Data; @Data @Embeddable -@AllArgsConstructor public class TitleId implements Serializable { private Integer empNo; private String title; From 0a4ca11a36f7aef71682b76ae06ddc8accd4584d Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 14:39:08 +0700 Subject: [PATCH 10/30] [Refactor] Update controller to handle mapping --- .../controllers/SalaryController.java | 43 ++++----------- .../controllers/TitleController.java | 54 ++++++------------- 2 files changed, 26 insertions(+), 71 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java index 47ab89c..65864ed 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java @@ -2,12 +2,9 @@ import java.util.Optional; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -27,31 +24,13 @@ public class SalaryController { private final SalaryService salaryService; /** - * This method retrieves {@link Page} of {@link Salary} from the database. + * This method retrieves a {@link Salary} from the database by its unique identifier. * - * @return ResponseEntity<List<Salary>> - A response entity containing a pages of {@link Salary}. - * If the pages is empty, it returns a HTTP status code 204 (No Content). - * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Salary}. - */ - @GetMapping - public ResponseEntity<Page<Salary>> findAll(Pageable pageable) { - Page<Salary> salaries = salaryService.findAll(pageable); - - if (salaries.isEmpty()) { - return ResponseEntity.noContent().build(); - } - - return ResponseEntity.ok(salaries); - } - - /** - * This method retrieves an {@link Salary} from the database by its id. - * - * @param id The unique identifier of the {@link Salary}. + * @param id The unique identifier of the {@link Salary} to be retrieved. * @return ResponseEntity<Salary> - A response entity containing the {@link Salary} if found, or a 404 Not Found status code if not found. */ - @GetMapping(value = "/{id}") - public ResponseEntity<Salary> findSalaryById(@PathVariable("id") SalaryId id) { + @GetMapping + public ResponseEntity<Salary> findSalaryById(@RequestBody SalaryId id) { Optional<Salary> salaryOpt= salaryService.findById(id); if(salaryOpt.isPresent()) { @@ -62,16 +41,14 @@ public ResponseEntity<Salary> findSalaryById(@PathVariable("id") SalaryId id) { } /** - * This method saves or updates a {@link Salary} to the database. + * This method deletes a {@link Salary} from the database by its unique identifier. * - * @param salary The salary object to be saved. - * @return ResponseEntity<Salary> - A response entity containing the saved {@link Salary}. - * If the {@link Salary} already exists in the database, it returns a HTTP status code 400 (Bad Request). + * @param id The unique identifier of the {@link Salary} to be deleted. + * @return ResponseEntity<Salary> - A response entity containing the deleted {@link Salary} if found and successfully deleted, or a 404 Not Found status code if not found. */ @PostMapping public ResponseEntity<Salary> saveOrUpdate(@RequestBody Salary salary) { - SalaryId salaryId = new SalaryId(salary.getEmpNo(), salary.getFromDate()); - Optional<Salary> salaryOpt = salaryService.findById(salaryId); + Optional<Salary> salaryOpt = salaryService.findById(salary.getId()); if(salaryOpt.isPresent()) { return ResponseEntity.badRequest().build(); @@ -86,8 +63,8 @@ public ResponseEntity<Salary> saveOrUpdate(@RequestBody Salary salary) { * @param id The unique identifier of the {@link Salary} to be deleted. * @return ResponseEntity<Salary> - A response entity containing the deleted {@link Salary} if found, or a 404 Not Found status code if not found. */ - @DeleteMapping(value = "/{id}") - public ResponseEntity<Salary> deleteSalary(@PathVariable(value = "id") SalaryId id) { + @DeleteMapping + public ResponseEntity<Salary> deleteSalary(@RequestBody SalaryId id) { Optional<Salary> salaryOpt = salaryService.findById(id); if(salaryOpt.isPresent()) { diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java index d8be89d..3dfc148 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java @@ -2,12 +2,9 @@ import java.util.Optional; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -27,31 +24,13 @@ public class TitleController { private final TitleService titleService; /** - * This method retrieves {@link Page} of {@link Title} from the database. + * This method retrieves a {@link Title} from the database by its unique identifier. * - * @return ResponseEntity<List<Title>> - A response entity containing a pages of {@link Title}. - * If the pages is empty, it returns a HTTP status code 204 (No Content). - * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Title}. - */ - @GetMapping - public ResponseEntity<Page<Title>> findAll(Pageable pageable) { - Page<Title> titles = titleService.findAll(pageable); - - if (titles.isEmpty()) { - return ResponseEntity.noContent().build(); - } - - return ResponseEntity.ok(titles); - } - - /** - * This method retrieves an {@link Title} from the database by its id. - * - * @param id The unique identifier of the {@link Title}. + * @param id The unique identifier of the {@link Title} to be retrieved. * @return ResponseEntity<Title> - A response entity containing the {@link Title} if found, or a 404 Not Found status code if not found. */ - @GetMapping(value = "/{id}") - public ResponseEntity<Title> findTitleById(@PathVariable("id") TitleId id) { + @GetMapping + public ResponseEntity<Title> findTitleById(@RequestBody TitleId id) { Optional<Title> titleOpt= titleService.findById(id); if(titleOpt.isPresent()) { @@ -61,19 +40,18 @@ public ResponseEntity<Title> findTitleById(@PathVariable("id") TitleId id) { return ResponseEntity.notFound().build(); } + /** - * This method saves or updates a {@link Title} to the database. + * This method saves or updates a {@link Title} in the database. * - * @param title The title object to be saved. - * @return ResponseEntity<Title> - A response entity containing the saved {@link Title}. - * If the {@link Title} already exists in the database, it returns a HTTP status code 400 (Bad Request). + * @param title The {@link Title} object to be saved or updated. + * @return ResponseEntity<Title> - A response entity containing the saved or updated {@link Title} if successful, or a 400 Bad Request status code if the {@link Title} with the same id already exists in the database. */ @PostMapping public ResponseEntity<Title> saveOrUpdate(@RequestBody Title title) { - TitleId titleId = new TitleId(title.getEmpNo(), title.getTitle(), title.getFromDate()); - Optional<Title> titleOpt = titleService.findById(titleId); - - if(titleOpt.isPresent()) { + Optional<Title> titleOpt = titleService.findById(title.getId()); + + if (titleOpt.isPresent()) { return ResponseEntity.badRequest().build(); } @@ -81,16 +59,16 @@ public ResponseEntity<Title> saveOrUpdate(@RequestBody Title title) { } /** - * This method deletes an {@link Title} from the database by its id. + * This method deletes a {@link Title} from the database by its unique identifier. * * @param id The unique identifier of the {@link Title} to be deleted. - * @return ResponseEntity<Title> - A response entity containing the deleted {@link Title} if found, or a 404 Not Found status code if not found. + * @return ResponseEntity<Title> - A response entity containing the deleted {@link Title} if found and successfully deleted, or a 404 Not Found status code if not found. */ - @DeleteMapping(value = "/{id}") - public ResponseEntity<Title> deleteTitle(@PathVariable(value = "id") TitleId id) { + @DeleteMapping + public ResponseEntity<Title> deleteTitle(@RequestBody TitleId id) { Optional<Title> titleOpt = titleService.findById(id); - if(titleOpt.isPresent()) { + if (titleOpt.isPresent()) { titleService.deleteById(id); return ResponseEntity.ok().build(); } From 964028970da38a281b8c53b24bb7e981146a7891 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 14:39:50 +0700 Subject: [PATCH 11/30] [Refactor] Update the services to exclude all --- .../example/lecture_11/services/SalaryService.java | 10 ++-------- .../example/lecture_11/services/TitleService.java | 10 ++-------- .../lecture_11/services/impl/SalaryServiceImpl.java | 13 ------------- .../lecture_11/services/impl/TitleServiceImpl.java | 13 ------------- 4 files changed, 4 insertions(+), 42 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java index a7e4fff..acc1762 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java @@ -1,17 +1,11 @@ package com.example.lecture_11.services; +import java.util.Optional; + import com.example.lecture_11.data.model.Salary; import com.example.lecture_11.data.model.composite.SalaryId; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -import java.util.Optional; - public interface SalaryService { - // Retrieves a paginated list of {@link Salary} entities. - Page<Salary> findAll(Pageable pageable); - // Retrieves an {@link Salary} entity by its unique identifier. Optional<Salary> findById(SalaryId id); diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java index 995f8e5..bab3633 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java @@ -1,17 +1,11 @@ package com.example.lecture_11.services; +import java.util.Optional; + import com.example.lecture_11.data.model.Title; import com.example.lecture_11.data.model.composite.TitleId; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -import java.util.Optional; - public interface TitleService { - // Retrieves a paginated list of {@link Title} entities. - Page<Title> findAll(Pageable pageable); - // Retrieves an {@link Title} entity by its unique identifier. Optional<Title> findById(TitleId id); diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java index 166cc35..d49b601 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java @@ -2,8 +2,6 @@ import java.util.Optional; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import com.example.lecture_11.data.model.Salary; @@ -19,17 +17,6 @@ public class SalaryServiceImpl implements SalaryService { private final SalaryRepository salaryRepository; - /** - * Retrieves a paginated list of {@link Salary} entities. - * - * @param pageable The pagination and sorting parameters. - * @return A {@link Page} of {@link Salary} entities. - */ - @Override - public Page<Salary> findAll(Pageable pageable) { - return salaryRepository.findAll(pageable); - } - /** * Retrieves an {@link Salary} entity by its unique identifier. * diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java index e8d06ad..7614985 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java @@ -2,8 +2,6 @@ import java.util.Optional; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import com.example.lecture_11.data.model.Title; @@ -19,17 +17,6 @@ public class TitleServiceImpl implements TitleService { private final TitleRepository titleRepository; - /** - * Retrieves a paginated list of {@link Title} entities. - * - * @param pageable The pagination and sorting parameters. - * @return A {@link Page} of {@link Title} entities. - */ - @Override - public Page<Title> findAll(Pageable pageable) { - return titleRepository.findAll(pageable); - } - /** * Retrieves an {@link Title} entity by its unique identifier. * From b7173c62920cd0c07303d358cef51a6fba284eb9 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 14:40:10 +0700 Subject: [PATCH 12/30] [Refactor] Updating to set out the class ref --- .../main/java/com/example/lecture_11/data/model/DeptEmp.java | 2 -- .../java/com/example/lecture_11/data/model/DeptManager.java | 2 -- .../src/main/java/com/example/lecture_11/data/model/Salary.java | 2 -- .../src/main/java/com/example/lecture_11/data/model/Title.java | 2 -- 4 files changed, 8 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java index 81e78a9..0a21e6b 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java @@ -7,7 +7,6 @@ import jakarta.persistence.Column; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.IdClass; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -19,7 +18,6 @@ @Data @Entity @Table(name = "dept_emp") -@IdClass(DeptEmpId.class) @EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java index 56d66c7..f33be0a 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java @@ -7,7 +7,6 @@ import jakarta.persistence.Column; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.IdClass; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -19,7 +18,6 @@ @Data @Entity @Table(name = "dept_manager") -@IdClass(DeptManagerId.class) @EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java index c67608f..db76024 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java @@ -7,7 +7,6 @@ import jakarta.persistence.Column; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.IdClass; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -19,7 +18,6 @@ @Data @Entity @Table(name = "salaries") -@IdClass(SalaryId.class) @EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java index 212ff93..88ee74d 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java @@ -7,7 +7,6 @@ import jakarta.persistence.Column; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.IdClass; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -19,7 +18,6 @@ @Data @Entity @Table(name = "titles") -@IdClass(TitleId.class) @EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor From 9da6d2b69de5e5b5595edd1c79bb8ef126a34f58 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 15:02:24 +0700 Subject: [PATCH 13/30] [Refactor] Separated save and edit on services --- .../java/com/example/lecture_11/services/DepartmentService.java | 2 +- .../java/com/example/lecture_11/services/EmployeeService.java | 2 +- .../java/com/example/lecture_11/services/SalaryService.java | 2 +- .../main/java/com/example/lecture_11/services/TitleService.java | 2 +- .../example/lecture_11/services/impl/DepartmentServiceImpl.java | 2 +- .../example/lecture_11/services/impl/EmployeeServiceImpl.java | 2 +- .../com/example/lecture_11/services/impl/SalaryServiceImpl.java | 2 +- .../com/example/lecture_11/services/impl/TitleServiceImpl.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java index d5ee19c..fccfc7e 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/DepartmentService.java @@ -15,7 +15,7 @@ public interface DepartmentService { Optional<Department> findById(String deptNo); // Saves or updates an {@link Department} entity in the database. - Department saveOrUpdate(Department department); + Department save(Department department); // Deletes an {@link Department} entity from the database by its unique identifier. void deleteById(String deptNo); diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java index 18d3108..6635aa4 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/EmployeeService.java @@ -15,7 +15,7 @@ public interface EmployeeService { Optional<Employee> findById(Integer empNo); // Saves or updates an {@link Employee} entity in the database. - Employee saveOrUpdate(Employee employee); + Employee save(Employee employee); // Deletes an {@link Employee} entity from the database by its unique identifier. void deleteById(Integer empNo); diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java index acc1762..d9464b3 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/SalaryService.java @@ -10,7 +10,7 @@ public interface SalaryService { Optional<Salary> findById(SalaryId id); // Saves or updates an {@link Salary} entity in the database. - Salary saveOrUpdate(Salary salary); + Salary save(Salary salary); // Deletes an {@link Salary} entity from the database by its unique identifier. void deleteById(SalaryId id); diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java index bab3633..32ef32e 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/TitleService.java @@ -10,7 +10,7 @@ public interface TitleService { Optional<Title> findById(TitleId id); // Saves or updates an {@link Title} entity in the database. - Title saveOrUpdate(Title title); + Title save(Title title); // Deletes an {@link Title} entity from the database by its unique identifier. void deleteById(TitleId id); diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java index 62c963c..d86da54 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/DepartmentServiceImpl.java @@ -47,7 +47,7 @@ public Optional<Department> findById(String deptNo) { * @return The saved or updated {@link Department} entity. */ @Override - public Department saveOrUpdate(Department department) { + public Department save(Department department) { return departmentRepository.save(department); } diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java index 9cca739..dba7659 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/EmployeeServiceImpl.java @@ -47,7 +47,7 @@ public Optional<Employee> findById(Integer empNo) { * @return The saved or updated {@link Employee} entity. */ @Override - public Employee saveOrUpdate(Employee employee) { + public Employee save(Employee employee) { return employeeRepository.save(employee); } diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java index d49b601..ce91410 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/SalaryServiceImpl.java @@ -35,7 +35,7 @@ public Optional<Salary> findById(SalaryId id) { * @return The saved or updated {@link Salary} entity. */ @Override - public Salary saveOrUpdate(Salary salary) { + public Salary save(Salary salary) { return salaryRepository.save(salary); } diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java index 7614985..fc733c1 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/services/impl/TitleServiceImpl.java @@ -35,7 +35,7 @@ public Optional<Title> findById(TitleId id) { * @return The saved or updated {@link Title} entity. */ @Override - public Title saveOrUpdate(Title title) { + public Title save(Title title) { return titleRepository.save(title); } From 765fdadfa671c79f1a31fb9a8ce43e3efa725e0e Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 15:02:33 +0700 Subject: [PATCH 14/30] [Refactor] Separated save and edit on controllers --- .../controllers/DepartmentController.java | 27 +++++++++++++--- .../controllers/EmployeeController.java | 28 +++++++++++++--- .../controllers/SalaryController.java | 32 +++++++++++++++---- .../controllers/TitleController.java | 32 +++++++++++++++---- 4 files changed, 99 insertions(+), 20 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java index 96e78fd..c87d876 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java @@ -9,6 +9,7 @@ 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -61,21 +62,39 @@ public ResponseEntity<Department> findDepartmentById(@PathVariable("deptNo") Str } /** - * This method saves or updates an {@link Department} to the database. + * This method saves a {@link Department} to the database. * * @param department The department object to be saved. * @return ResponseEntity<Department> - A response entity containing the saved {@link Department}. * If the {@link Department} already exists in the database, it returns a HTTP status code 400 (Bad Request). */ @PostMapping - public ResponseEntity<Department> saveOrUpdate(@RequestBody Department department) { + public ResponseEntity<Department> save(@RequestBody Department department) { Optional<Department> departmentOpt = departmentService.findById(department.getDeptNo()); - if(departmentOpt.isPresent()) { + if (departmentOpt.isPresent()) { return ResponseEntity.badRequest().build(); } - return ResponseEntity.ok(departmentService.saveOrUpdate(department)); + return ResponseEntity.ok(departmentService.save(department)); + } + + /** + * This method updates an existing {@link Department} in the database. + * + * @param department The department object to be updated. + * @return ResponseEntity<Department> - A response entity containing the updated {@link Department}. + * If the {@link Department} does not exist in the database, it returns a HTTP status code 404 (Not Found). + */ + @PutMapping(value = "/{deptNo}") + public ResponseEntity<Department> update(@PathVariable(value = "deptNo") String deptNo, @RequestBody Department department) { + Optional<Department> departmentOpt = departmentService.findById(deptNo); + + if (departmentOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok(departmentService.save(department)); } /** diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java index 870a275..9fb4594 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java @@ -9,6 +9,7 @@ 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -61,21 +62,40 @@ public ResponseEntity<Employee> findEmployeeById(@PathVariable("empNo") Integer } /** - * This method saves or updates an {@link Employee} to the database. + * This method saves an {@link Employee} to the database. * * @param employee The employee object to be saved. * @return ResponseEntity<Employee> - A response entity containing the saved {@link Employee}. * If the {@link Employee} already exists in the database, it returns a HTTP status code 400 (Bad Request). */ @PostMapping - public ResponseEntity<Employee> saveOrUpdate(@RequestBody Employee employee) { + public ResponseEntity<Employee> save(@RequestBody Employee employee) { Optional<Employee> employeeOpt = employeeService.findById(employee.getEmpNo()); - if(employeeOpt.isPresent()) { + if (employeeOpt.isPresent()) { return ResponseEntity.badRequest().build(); } - return ResponseEntity.ok(employeeService.saveOrUpdate(employee)); + return ResponseEntity.ok(employeeService.save(employee)); + } + + /** + * This method updates an existing {@link Employee} in the database. + * + * @param empNo The unique identifier of the {@link Employee} to be updated. + * @param employee The employee object to be updated. + * @return ResponseEntity<Employee> - A response entity containing the updated {@link Employee}. + * If the {@link Employee} does not exist in the database, it returns a HTTP status code 404 (Not Found). + */ + @PutMapping(value = "/{empNo}") + public ResponseEntity<Employee> update(@PathVariable(value = "/{empNo}") Integer empNo, @RequestBody Employee employee) { + Optional<Employee> employeeOpt = employeeService.findById(empNo); + + if (employeeOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok(employeeService.save(employee)); } /** diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java index 65864ed..92aa352 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -41,20 +42,39 @@ public ResponseEntity<Salary> findSalaryById(@RequestBody SalaryId id) { } /** - * This method deletes a {@link Salary} from the database by its unique identifier. + * This method saves a {@link Salary} to the database. * - * @param id The unique identifier of the {@link Salary} to be deleted. - * @return ResponseEntity<Salary> - A response entity containing the deleted {@link Salary} if found and successfully deleted, or a 404 Not Found status code if not found. + * @param salary The salary object to be saved. + * @return ResponseEntity<Salary> - A response entity containing the saved {@link Salary}. + * If the {@link Salary} already exists in the database, it returns a HTTP status code 400 (Bad Request). */ @PostMapping - public ResponseEntity<Salary> saveOrUpdate(@RequestBody Salary salary) { + public ResponseEntity<Salary> save(@RequestBody Salary salary) { Optional<Salary> salaryOpt = salaryService.findById(salary.getId()); - if(salaryOpt.isPresent()) { + if (salaryOpt.isPresent()) { return ResponseEntity.badRequest().build(); } - return ResponseEntity.ok(salaryService.saveOrUpdate(salary)); + return ResponseEntity.ok(salaryService.save(salary)); + } + + /** + * This method updates an existing {@link Salary} in the database. + * + * @param salary The salary object to be updated. + * @return ResponseEntity<Salary> - A response entity containing the updated {@link Salary}. + * If the {@link Salary} does not exist in the database, it returns a HTTP status code 404 (Not Found). + */ + @PutMapping + public ResponseEntity<Salary> update(@RequestBody Salary salary) { + Optional<Salary> salaryOpt = salaryService.findById(salary.getId()); + + if (salaryOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok(salaryService.save(salary)); } /** diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java index 3dfc148..d9df6c1 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -42,20 +43,39 @@ public ResponseEntity<Title> findTitleById(@RequestBody TitleId id) { /** - * This method saves or updates a {@link Title} in the database. + * This method saves a {@link Title} to the database. * - * @param title The {@link Title} object to be saved or updated. - * @return ResponseEntity<Title> - A response entity containing the saved or updated {@link Title} if successful, or a 400 Bad Request status code if the {@link Title} with the same id already exists in the database. + * @param title The title object to be saved. + * @return ResponseEntity<Title> - A response entity containing the saved {@link Title}. + * If the {@link Title} already exists in the database, it returns a HTTP status code 400 (Bad Request). */ @PostMapping - public ResponseEntity<Title> saveOrUpdate(@RequestBody Title title) { + public ResponseEntity<Title> save(@RequestBody Title title) { Optional<Title> titleOpt = titleService.findById(title.getId()); - + if (titleOpt.isPresent()) { return ResponseEntity.badRequest().build(); } - return ResponseEntity.ok(titleService.saveOrUpdate(title)); + return ResponseEntity.ok(titleService.save(title)); + } + + /** + * This method updates an existing {@link Title} in the database. + * + * @param title The title object to be updated. + * @return ResponseEntity<Title> - A response entity containing the updated {@link Title}. + * If the {@link Title} does not exist in the database, it returns a HTTP status code 404 (Not Found). + */ + @PutMapping + public ResponseEntity<Title> update(@RequestBody Title title) { + Optional<Title> titleOpt = titleService.findById(title.getId()); + + if (titleOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok(titleService.save(title)); } /** From 989e2f6638314472448580cae96ed5707563d5d3 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 16:45:54 +0700 Subject: [PATCH 15/30] [Refactor] Some adjustments on department and employee controller --- .../controllers/DepartmentController.java | 9 ++++++-- .../controllers/EmployeeController.java | 23 +++++++++---------- .../src/main/resources/application.properties | 3 +-- .../lecture_11/src/main/resources/data.sql | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java index c87d876..7b1a847 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/DepartmentController.java @@ -3,6 +3,7 @@ import java.util.Optional; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; @@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.example.lecture_11.data.model.Department; @@ -29,12 +31,15 @@ public class DepartmentController { /** * This method retrieves {@link Page} of {@link Department} from the database. * + * @param page The page number to retrieve (0-based index). + * @param size The number of elements per page. * @return ResponseEntity<List<Department>> - A response entity containing a pages of {@link Department}. * If the pages is empty, it returns a HTTP status code 204 (No Content). * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Department}. */ @GetMapping - public ResponseEntity<Page<Department>> findAll(Pageable pageable) { + public ResponseEntity<Page<Department>> findAll(@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { + Pageable pageable = PageRequest.of(page, size); Page<Department> departments = departmentService.findAll(pageable); if (departments.isEmpty()) { @@ -87,7 +92,7 @@ public ResponseEntity<Department> save(@RequestBody Department department) { * If the {@link Department} does not exist in the database, it returns a HTTP status code 404 (Not Found). */ @PutMapping(value = "/{deptNo}") - public ResponseEntity<Department> update(@PathVariable(value = "deptNo") String deptNo, @RequestBody Department department) { + public ResponseEntity<Department> update(@PathVariable("deptNo") String deptNo, @RequestBody Department department) { Optional<Department> departmentOpt = departmentService.findById(deptNo); if (departmentOpt.isEmpty()) { diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java index 9fb4594..8d1f9a8 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/EmployeeController.java @@ -3,6 +3,7 @@ import java.util.Optional; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; @@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.example.lecture_11.data.model.Employee; @@ -29,12 +31,15 @@ public class EmployeeController { /** * This method retrieves {@link Page} of {@link Employee} from the database. * - * @return ResponseEntity<List<Employee>> - A response entity containing a pages of {@link Employee}. - * If the pages is empty, it returns a HTTP status code 204 (No Content). - * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Employee}. + * @param page The page number to retrieve (0-based index). + * @param size The number of elements per page. + * @return ResponseEntity<Page<Employee>> - A response entity containing a page of {@link Employee}. + * If the page is empty, it returns a HTTP status code 204 (No Content). + * If the operation is successful, it returns a HTTP status code 200 (OK) with the page of {@link Employee}. */ @GetMapping - public ResponseEntity<Page<Employee>> findAll(Pageable pageable) { + public ResponseEntity<Page<Employee>> findAll(@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { + Pageable pageable = PageRequest.of(page, size); Page<Employee> employees = employeeService.findAll(pageable); if (employees.isEmpty()) { @@ -66,16 +71,9 @@ public ResponseEntity<Employee> findEmployeeById(@PathVariable("empNo") Integer * * @param employee The employee object to be saved. * @return ResponseEntity<Employee> - A response entity containing the saved {@link Employee}. - * If the {@link Employee} already exists in the database, it returns a HTTP status code 400 (Bad Request). */ @PostMapping public ResponseEntity<Employee> save(@RequestBody Employee employee) { - Optional<Employee> employeeOpt = employeeService.findById(employee.getEmpNo()); - - if (employeeOpt.isPresent()) { - return ResponseEntity.badRequest().build(); - } - return ResponseEntity.ok(employeeService.save(employee)); } @@ -88,13 +86,14 @@ public ResponseEntity<Employee> save(@RequestBody Employee employee) { * If the {@link Employee} does not exist in the database, it returns a HTTP status code 404 (Not Found). */ @PutMapping(value = "/{empNo}") - public ResponseEntity<Employee> update(@PathVariable(value = "/{empNo}") Integer empNo, @RequestBody Employee employee) { + public ResponseEntity<Employee> update(@PathVariable Integer empNo, @RequestBody Employee employee) { Optional<Employee> employeeOpt = employeeService.findById(empNo); if (employeeOpt.isEmpty()) { return ResponseEntity.notFound().build(); } + employee.setEmpNo(empNo); return ResponseEntity.ok(employeeService.save(employee)); } diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/application.properties b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/application.properties index af2c18f..7d70328 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/application.properties +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/application.properties @@ -4,5 +4,4 @@ spring.datasource.url=jdbc:mysql://localhost:3308/week6_lecture11?allowPublicKey spring.datasource.username=root spring.datasource.password=Michaeleon16606_ spring.datasource.driver-class-name=com.mysql.jdbc.Driver -spring.datasource.initialization-mode=always -spring.jpa.hibernate.ddl-auto=create-drop \ No newline at end of file +spring.jpa.hibernate.ddl-auto=update \ No newline at end of file diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/data.sql b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/data.sql index ff17cbb..7631c07 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/data.sql +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/resources/data.sql @@ -146,4 +146,4 @@ INSERT INTO titles (emp_no, title, from_date, to_date) VALUES (8, 'Marketing Specialist', '2011-03-22', '9999-01-01'), (9, 'Senior Engineer', '1996-06-12', '2006-06-12'), (9, 'Chief Engineer', '2006-06-12', '9999-01-01'), -(10, 'Senior Sales Representative', '2009-11-30', '9999-01-01'); \ No newline at end of file +(10, 'Senior Sales Representative', '2009-11-30', '9999-01-01'); From 2e0455db50c7a92ca770bc98c4e22f633976ef90 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 17:18:38 +0700 Subject: [PATCH 16/30] [Refactor] Update salary and title controller new instance handle --- ...11 - Assignment 01.postman_collection.json | 366 ++++++++++++++++++ .../controllers/SalaryController.java | 7 - .../controllers/TitleController.java | 6 - 3 files changed, 366 insertions(+), 13 deletions(-) create mode 100644 Week 06/Lecture 11/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json diff --git a/Week 06/Lecture 11/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json b/Week 06/Lecture 11/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json new file mode 100644 index 0000000..0075f54 --- /dev/null +++ b/Week 06/Lecture 11/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json @@ -0,0 +1,366 @@ +{ + "info": { + "_postman_id": "3093d6b8-a742-4d7c-b565-95715055e5d3", + "name": "Lecture 11 - Assignment 01", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", + "_exporter_id": "34693283" + }, + "item": [ + { + "name": "Employees", + "item": [ + { + "name": "All Employees", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/employees" + }, + "response": [] + }, + { + "name": "All Employees Configurable Pages", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees?page=1&size=5", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "size", + "value": "5" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee By EmpNo", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/employees/3" + }, + "response": [] + }, + { + "name": "New Employee", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"birthDate\": \"2003-08-12\",\r\n \"firstName\": \"Michael\",\r\n \"lastName\": \"Leon\",\r\n \"gender\": \"M\",\r\n \"hireDate\": \"2024-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/employees" + }, + "response": [] + }, + { + "name": "Edit Employee", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"birthDate\": \"2003-08-12\",\r\n \"firstName\": \"Leon\",\r\n \"lastName\": \"Michael\",\r\n \"gender\": \"M\",\r\n \"hireDate\": \"2024-07-18\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/employees/11" + }, + "response": [] + }, + { + "name": "Delete Employee", + "request": { + "method": "DELETE", + "header": [], + "url": "localhost:8080/api/v1/employees/11" + }, + "response": [] + } + ] + }, + { + "name": "Departments", + "item": [ + { + "name": "All Departments", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/departments" + }, + "response": [] + }, + { + "name": "All Departments Configurable Pages", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/departments?page=0&size=2", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "departments" + ], + "query": [ + { + "key": "page", + "value": "0" + }, + { + "key": "size", + "value": "2" + } + ] + } + }, + "response": [] + }, + { + "name": "Department By DeptNo", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/departments/d004" + }, + "response": [] + }, + { + "name": "New Department", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"deptNo\": \"d006\",\r\n \"deptName\": \"Research\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/departments" + }, + "response": [] + }, + { + "name": "Edit Employee", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"deptNo\": \"d006\",\r\n \"deptName\": \"New Research\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/departments/d006" + }, + "response": [] + }, + { + "name": "Delete Employee", + "request": { + "method": "DELETE", + "header": [], + "url": "localhost:8080/api/v1/departments/d006" + }, + "response": [] + } + ] + }, + { + "name": "Salaries", + "item": [ + { + "name": "Salary by ID", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2000-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "New Salary", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n },\r\n \"salary\": 60000,\r\n \"toDate\": \"2025-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "Edit Salary", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n },\r\n \"salary\": 65000,\r\n \"toDate\": \"2025-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "Delete Salary", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + } + ] + }, + { + "name": "Titles", + "item": [ + { + "name": "Title by ID", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"title\": \"Manager\",\r\n \"fromDate\": \"2000-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "New Title", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n },\r\n \"toDate\": \"2002-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "Edit Title", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n },\r\n \"toDate\": \"2020-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "Delete Title", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java index 92aa352..577d41d 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/SalaryController.java @@ -46,16 +46,9 @@ public ResponseEntity<Salary> findSalaryById(@RequestBody SalaryId id) { * * @param salary The salary object to be saved. * @return ResponseEntity<Salary> - A response entity containing the saved {@link Salary}. - * If the {@link Salary} already exists in the database, it returns a HTTP status code 400 (Bad Request). */ @PostMapping public ResponseEntity<Salary> save(@RequestBody Salary salary) { - Optional<Salary> salaryOpt = salaryService.findById(salary.getId()); - - if (salaryOpt.isPresent()) { - return ResponseEntity.badRequest().build(); - } - return ResponseEntity.ok(salaryService.save(salary)); } diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java index d9df6c1..e2d7409 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/controllers/TitleController.java @@ -51,12 +51,6 @@ public ResponseEntity<Title> findTitleById(@RequestBody TitleId id) { */ @PostMapping public ResponseEntity<Title> save(@RequestBody Title title) { - Optional<Title> titleOpt = titleService.findById(title.getId()); - - if (titleOpt.isPresent()) { - return ResponseEntity.badRequest().build(); - } - return ResponseEntity.ok(titleService.save(title)); } From 557185f70338a358a2b353e7475dad53f8aa40b8 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 17:21:25 +0700 Subject: [PATCH 17/30] [Feat] Update README.md --- Week 06/Lecture 11/Assignment 01/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/README.md b/Week 06/Lecture 11/Assignment 01/README.md index 60a97d5..e0daef0 100644 --- a/Week 06/Lecture 11/Assignment 01/README.md +++ b/Week 06/Lecture 11/Assignment 01/README.md @@ -232,7 +232,6 @@ spring.datasource.password=<your_password> and don't forget to add this ```java -spring.datasource.initialization-mode=always spring.jpa.hibernate.ddl-auto=update ``` to do database seeding using JPA Hibernate. @@ -257,4 +256,4 @@ If all the instruction is well executed, Open [localhost:8080](http://localhost: ### πŸ“¬ Postman Collection -Here is the [postman collection] you can use to demo the API functionality. \ No newline at end of file +Here is the [postman collection](/Week%2006/Lecture%2011/Assignment%2001/Lecture%2011%20-%20Assignment%2001.postman_collection.json) you can use to demo the API functionality. \ No newline at end of file From bf5c10143056802ce6d2e64d1e470c096b365539 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 21:28:54 +0700 Subject: [PATCH 18/30] [Feat] List of Endpoints --- Week 06/Lecture 11/Assignment 01/README.md | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Week 06/Lecture 11/Assignment 01/README.md b/Week 06/Lecture 11/Assignment 01/README.md index e0daef0..43719ed 100644 --- a/Week 06/Lecture 11/Assignment 01/README.md +++ b/Week 06/Lecture 11/Assignment 01/README.md @@ -254,6 +254,30 @@ to do database seeding using JPA Hibernate. If all the instruction is well executed, Open [localhost:8080](http://localhost:8080) to see that the REST APIs is now works. +### πŸ”‘ List of Endpoints +| Endpoint | Method | Description | +|-----------------------------------------|:--------: |---------------------------------------------------------------------------------------------| +| /api/v1/employees | GET | Retrieve all employees with default pagination (page 0 with size 20 elements/page). | +| /api/v1/employees?page=1&size=5 | GET | Retrieve employees with pagination (page 1 with size 5 elements/page). | +| /api/v1/employees/{empNo} | GET | Retrieve a specific employee by employee number. | +| /api/v1/employees | POST | Create a new employee. | +| /api/v1/employees/{empNo} | PUT | Update an existing employee by employee number. | +| /api/v1/employees/{empNo} | DELETE | Delete an employee by employee number. | +| /api/v1/departments | GET | Retrieve all departments with default pagination (page 0 with size 20 elements/page). | +| /api/v1/departments?page=0&size=2 | GET | Retrieve departments with pagination (page 0 with size 2 elements/page). | +| /api/v1/departments/{deptNo} | GET | Retrieve a specific department by department number. | +| /api/v1/departments | POST | Create a new department. | +| /api/v1/departments/{deptNo} | PUT | Update an existing department by department number. | +| /api/v1/departments/{deptNo} | DELETE | Delete a department by department number. | +| /api/v1/salaries | GET | Retrieve salary by ID. | +| /api/v1/salaries | POST | Create a new salary record. | +| /api/v1/salaries | PUT | Update an existing salary record. | +| /api/v1/salaries | DELETE | Delete a salary record by ID. | +| /api/v1/titles | GET | Retrieve title by ID. | +| /api/v1/titles | POST | Create a new title. | +| /api/v1/titles | PUT | Update an existing title. | +| /api/v1/titles | DELETE | Delete a title record by ID. | + ### πŸ“¬ Postman Collection Here is the [postman collection](/Week%2006/Lecture%2011/Assignment%2001/Lecture%2011%20-%20Assignment%2001.postman_collection.json) you can use to demo the API functionality. \ No newline at end of file From 65d3adc506c9a1a76bdbbe2655c8e318fabeb759 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 21:50:36 +0700 Subject: [PATCH 19/30] [Feat] Insert research over composite key in JPA --- Week 06/Lecture 11/Assignment 01/README.md | 92 ++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/Week 06/Lecture 11/Assignment 01/README.md b/Week 06/Lecture 11/Assignment 01/README.md index 43719ed..ba34cd6 100644 --- a/Week 06/Lecture 11/Assignment 01/README.md +++ b/Week 06/Lecture 11/Assignment 01/README.md @@ -2,6 +2,98 @@ > This repository is created as a part of assignment for Lecture 11 - Spring Data JPA ## πŸ“ Assignment 01 - Implementation of Model, JPA, Repositories, Services, and REST APIs + +### πŸ”Ž [Research] Composite Key in JPA + +Implementing a composite key in JPA (Java Persistence API) involves using an `@Embeddable` class to represent the composite key and embedding it into the entity class. Here’s a short explanation and steps to implement it: + +#### Steps to Implement Composite Key in JPA + +1. **Create the Embeddable Key Class**: + - Define a class to represent the composite key. + - Annotate the class with `@Embeddable`. + - Implement `Serializable` interface. + - Override `equals()` and `hashCode()` methods. In this case i'm using using `@Data` and `@EqualsAndHashCode` from Lombok to automatically generate it. + +2. **Embed the Key in the Entity Class**: + - Use `@EmbeddedId` annotation in the entity class to include the composite key. + - Annotate the entity class with `@Entity` and other necessary JPA annotations. + +3. **Map the Composite Key Columns**: + - Map the fields of the embeddable key class to the corresponding columns in the database. + +#### Example + +##### Embeddable Key Class +For this example i will use [SalaryId Class](/Week%2006/Lecture%2011/Assignment%2001/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java). + +```java +import java.io.Serializable; +import java.time.LocalDate; +import jakarta.persistence.Embeddable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@Embeddable +@EqualsAndHashCode +public class SalaryId implements Serializable { + private Integer empNo; + private LocalDate fromDate; +} +``` + +##### Entity Class +For this example i will use [Salary Class](/Week%2006/Lecture%2011/Assignment%2001/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java). +```java +import java.time.LocalDate; +import com.example.lecture_11.data.model.composite.SalaryId; +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "salaries") +@NoArgsConstructor +@AllArgsConstructor +public class Salary { + + @EmbeddedId + private SalaryId id; + + @Column(nullable = false) + private Integer salary; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} +``` + +#### Explanation + +1. **SalaryId Class**: + - Annotated with `@Embeddable`, indicating it is a composite key. + - Implements `Serializable`. + - Includes necessary fields (`empNo`, `fromDate`) that form the composite key. + - Uses Lombok's `@EqualsAndHashCode` to automatically generate `equals()` and `hashCode()` methods based on the fields of the class. + +2. **Salary Class**: + - Annotated with `@Entity` to indicate it is a JPA entity. + - Uses `@EmbeddedId` to include `SalaryId` as the primary key. + - Defines other entity attributes (`salary`, `toDate`). + +By following these steps, i successfully implement and use composite keys in the JPA entities. + +Using Lombok's `@EqualsAndHashCode` simplifies the code and ensures that the `equals()` and `hashCode()` methods are correctly implemented based on the fields of the composite key class. This approach reduces boilerplate code and makes the implementation cleaner and easier to maintain. + ### 🌳 Project Structure ```bash lecture_11 From b9381497f1052fb988312fa65eac11d94eed83b1 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Wed, 17 Jul 2024 22:00:50 +0700 Subject: [PATCH 20/30] [Refactor] Implementation of hash check using lombok --- .../main/java/com/example/lecture_11/data/model/DeptEmp.java | 2 -- .../java/com/example/lecture_11/data/model/DeptManager.java | 2 -- .../src/main/java/com/example/lecture_11/data/model/Salary.java | 2 -- .../src/main/java/com/example/lecture_11/data/model/Title.java | 2 -- .../com/example/lecture_11/data/model/composite/DeptEmpId.java | 2 ++ .../example/lecture_11/data/model/composite/DeptManagerId.java | 2 ++ .../com/example/lecture_11/data/model/composite/SalaryId.java | 2 ++ .../com/example/lecture_11/data/model/composite/TitleId.java | 2 ++ 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java index 0a21e6b..6ec4c7e 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptEmp.java @@ -12,13 +12,11 @@ import jakarta.persistence.TemporalType; import lombok.AllArgsConstructor; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Data @Entity @Table(name = "dept_emp") -@EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor public class DeptEmp { diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java index f33be0a..d088624 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/DeptManager.java @@ -12,13 +12,11 @@ import jakarta.persistence.TemporalType; import lombok.AllArgsConstructor; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Data @Entity @Table(name = "dept_manager") -@EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor public class DeptManager { diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java index db76024..86cd5bb 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java @@ -12,13 +12,11 @@ import jakarta.persistence.TemporalType; import lombok.AllArgsConstructor; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Data @Entity @Table(name = "salaries") -@EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor public class Salary { diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java index 88ee74d..8724490 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/Title.java @@ -12,13 +12,11 @@ import jakarta.persistence.TemporalType; import lombok.AllArgsConstructor; import lombok.Data; -import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Data @Entity @Table(name = "titles") -@EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor public class Title { diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java index 57f0297..d064349 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptEmpId.java @@ -3,10 +3,12 @@ import java.io.Serializable; import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; import lombok.Data; @Data @Embeddable +@EqualsAndHashCode public class DeptEmpId implements Serializable { private Integer empNo; private String deptNo; diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java index b3efca9..f8a82c6 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/DeptManagerId.java @@ -3,10 +3,12 @@ import java.io.Serializable; import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; import lombok.Data; @Data @Embeddable +@EqualsAndHashCode public class DeptManagerId implements Serializable { private Integer empNo; private String deptNo; diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java index 6001dd8..17a6a6b 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java @@ -4,10 +4,12 @@ import java.time.LocalDate; import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; import lombok.Data; @Data @Embeddable +@EqualsAndHashCode public class SalaryId implements Serializable { private Integer empNo; private LocalDate fromDate; diff --git a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java index 07e1e85..496ace7 100644 --- a/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java +++ b/Week 06/Lecture 11/Assignment 01/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/TitleId.java @@ -4,10 +4,12 @@ import java.time.LocalDate; import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; import lombok.Data; @Data @Embeddable +@EqualsAndHashCode public class TitleId implements Serializable { private Integer empNo; private String title; From 8423bb5c5aec3a0b88ffdd9632ba7719ae63ceb1 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Thu, 18 Jul 2024 17:08:37 +0700 Subject: [PATCH 21/30] [Init] Migrating task from Lecture 11 --- ...11 - Assignment 01.postman_collection.json | 366 +++++++++++++++++ Week 06/Lecture 12/Assignment 01/README.md | 375 ++++++++++++++++++ .../Assignment 01/lecture_12/.gitignore | 33 ++ .../.mvn/wrapper/maven-wrapper.properties | 19 + .../Lecture 12/Assignment 01/lecture_12/mvnw | 259 ++++++++++++ .../Assignment 01/lecture_12/mvnw.cmd | 149 +++++++ .../Assignment 01/lecture_12/pom.xml | 105 +++++ .../Assignment 01/lecture_12/run.bat | 3 + .../Assignment 01/lecture_12/run.sh | 3 + .../lecture_12/Lecture12Application.java | 13 + .../controllers/DepartmentController.java | 122 ++++++ .../controllers/EmployeeController.java | 117 ++++++ .../controllers/SalaryController.java | 90 +++++ .../controllers/TitleController.java | 92 +++++ .../lecture_12/data/model/Department.java | 24 ++ .../lecture_12/data/model/DeptEmp.java | 34 ++ .../lecture_12/data/model/DeptManager.java | 34 ++ .../lecture_12/data/model/Employee.java | 44 ++ .../example/lecture_12/data/model/Salary.java | 33 ++ .../example/lecture_12/data/model/Title.java | 30 ++ .../data/model/composite/DeptEmpId.java | 15 + .../data/model/composite/DeptManagerId.java | 15 + .../data/model/composite/SalaryId.java | 16 + .../data/model/composite/TitleId.java | 17 + .../data/repository/DepartmentRepository.java | 11 + .../data/repository/DeptEmpRepository.java | 10 + .../repository/DeptManagerRepository.java | 10 + .../data/repository/EmployeeRepository.java | 10 + .../data/repository/SalaryRepository.java | 9 + .../data/repository/TitleRepository.java | 9 + .../services/DepartmentService.java | 22 + .../lecture_12/services/EmployeeService.java | 22 + .../lecture_12/services/SalaryService.java | 17 + .../lecture_12/services/TitleService.java | 17 + .../services/impl/DepartmentServiceImpl.java | 64 +++ .../services/impl/EmployeeServiceImpl.java | 64 +++ .../services/impl/SalaryServiceImpl.java | 52 +++ .../services/impl/TitleServiceImpl.java | 52 +++ .../src/main/resources/application.properties | 7 + .../lecture_12/src/main/resources/data.sql | 149 +++++++ .../lecture_12/Lecture12ApplicationTests.java | 13 + 41 files changed, 2546 insertions(+) create mode 100644 Week 06/Lecture 12/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json create mode 100644 Week 06/Lecture 12/Assignment 01/README.md create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/.gitignore create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/.mvn/wrapper/maven-wrapper.properties create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/mvnw create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/mvnw.cmd create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/pom.xml create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/run.bat create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/run.sh create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/Lecture12Application.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/DepartmentController.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/SalaryController.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/TitleController.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Department.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/DeptEmp.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/DeptManager.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Employee.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Salary.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Title.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/DeptEmpId.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/DeptManagerId.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/SalaryId.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/TitleId.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DepartmentRepository.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DeptEmpRepository.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DeptManagerRepository.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/SalaryRepository.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/TitleRepository.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/DepartmentService.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/SalaryService.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/TitleService.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/DepartmentServiceImpl.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/SalaryServiceImpl.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/TitleServiceImpl.java create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/data.sql create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/test/java/com/example/lecture_12/Lecture12ApplicationTests.java diff --git a/Week 06/Lecture 12/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json b/Week 06/Lecture 12/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json new file mode 100644 index 0000000..0075f54 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json @@ -0,0 +1,366 @@ +{ + "info": { + "_postman_id": "3093d6b8-a742-4d7c-b565-95715055e5d3", + "name": "Lecture 11 - Assignment 01", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", + "_exporter_id": "34693283" + }, + "item": [ + { + "name": "Employees", + "item": [ + { + "name": "All Employees", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/employees" + }, + "response": [] + }, + { + "name": "All Employees Configurable Pages", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees?page=1&size=5", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "size", + "value": "5" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee By EmpNo", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/employees/3" + }, + "response": [] + }, + { + "name": "New Employee", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"birthDate\": \"2003-08-12\",\r\n \"firstName\": \"Michael\",\r\n \"lastName\": \"Leon\",\r\n \"gender\": \"M\",\r\n \"hireDate\": \"2024-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/employees" + }, + "response": [] + }, + { + "name": "Edit Employee", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"birthDate\": \"2003-08-12\",\r\n \"firstName\": \"Leon\",\r\n \"lastName\": \"Michael\",\r\n \"gender\": \"M\",\r\n \"hireDate\": \"2024-07-18\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/employees/11" + }, + "response": [] + }, + { + "name": "Delete Employee", + "request": { + "method": "DELETE", + "header": [], + "url": "localhost:8080/api/v1/employees/11" + }, + "response": [] + } + ] + }, + { + "name": "Departments", + "item": [ + { + "name": "All Departments", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/departments" + }, + "response": [] + }, + { + "name": "All Departments Configurable Pages", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/departments?page=0&size=2", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "departments" + ], + "query": [ + { + "key": "page", + "value": "0" + }, + { + "key": "size", + "value": "2" + } + ] + } + }, + "response": [] + }, + { + "name": "Department By DeptNo", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/departments/d004" + }, + "response": [] + }, + { + "name": "New Department", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"deptNo\": \"d006\",\r\n \"deptName\": \"Research\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/departments" + }, + "response": [] + }, + { + "name": "Edit Employee", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"deptNo\": \"d006\",\r\n \"deptName\": \"New Research\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/departments/d006" + }, + "response": [] + }, + { + "name": "Delete Employee", + "request": { + "method": "DELETE", + "header": [], + "url": "localhost:8080/api/v1/departments/d006" + }, + "response": [] + } + ] + }, + { + "name": "Salaries", + "item": [ + { + "name": "Salary by ID", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2000-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "New Salary", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n },\r\n \"salary\": 60000,\r\n \"toDate\": \"2025-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "Edit Salary", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n },\r\n \"salary\": 65000,\r\n \"toDate\": \"2025-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "Delete Salary", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + } + ] + }, + { + "name": "Titles", + "item": [ + { + "name": "Title by ID", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"title\": \"Manager\",\r\n \"fromDate\": \"2000-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "New Title", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n },\r\n \"toDate\": \"2002-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "Edit Title", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n },\r\n \"toDate\": \"2020-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "Delete Title", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/Week 06/Lecture 12/Assignment 01/README.md b/Week 06/Lecture 12/Assignment 01/README.md new file mode 100644 index 0000000..ba34cd6 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/README.md @@ -0,0 +1,375 @@ +# πŸ‘¨πŸ»β€πŸ« Lecture 11 - Spring Data JPA +> This repository is created as a part of assignment for Lecture 11 - Spring Data JPA + +## πŸ“ Assignment 01 - Implementation of Model, JPA, Repositories, Services, and REST APIs + +### πŸ”Ž [Research] Composite Key in JPA + +Implementing a composite key in JPA (Java Persistence API) involves using an `@Embeddable` class to represent the composite key and embedding it into the entity class. Here’s a short explanation and steps to implement it: + +#### Steps to Implement Composite Key in JPA + +1. **Create the Embeddable Key Class**: + - Define a class to represent the composite key. + - Annotate the class with `@Embeddable`. + - Implement `Serializable` interface. + - Override `equals()` and `hashCode()` methods. In this case i'm using using `@Data` and `@EqualsAndHashCode` from Lombok to automatically generate it. + +2. **Embed the Key in the Entity Class**: + - Use `@EmbeddedId` annotation in the entity class to include the composite key. + - Annotate the entity class with `@Entity` and other necessary JPA annotations. + +3. **Map the Composite Key Columns**: + - Map the fields of the embeddable key class to the corresponding columns in the database. + +#### Example + +##### Embeddable Key Class +For this example i will use [SalaryId Class](/Week%2006/Lecture%2011/Assignment%2001/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java). + +```java +import java.io.Serializable; +import java.time.LocalDate; +import jakarta.persistence.Embeddable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@Embeddable +@EqualsAndHashCode +public class SalaryId implements Serializable { + private Integer empNo; + private LocalDate fromDate; +} +``` + +##### Entity Class +For this example i will use [Salary Class](/Week%2006/Lecture%2011/Assignment%2001/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java). +```java +import java.time.LocalDate; +import com.example.lecture_11.data.model.composite.SalaryId; +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "salaries") +@NoArgsConstructor +@AllArgsConstructor +public class Salary { + + @EmbeddedId + private SalaryId id; + + @Column(nullable = false) + private Integer salary; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} +``` + +#### Explanation + +1. **SalaryId Class**: + - Annotated with `@Embeddable`, indicating it is a composite key. + - Implements `Serializable`. + - Includes necessary fields (`empNo`, `fromDate`) that form the composite key. + - Uses Lombok's `@EqualsAndHashCode` to automatically generate `equals()` and `hashCode()` methods based on the fields of the class. + +2. **Salary Class**: + - Annotated with `@Entity` to indicate it is a JPA entity. + - Uses `@EmbeddedId` to include `SalaryId` as the primary key. + - Defines other entity attributes (`salary`, `toDate`). + +By following these steps, i successfully implement and use composite keys in the JPA entities. + +Using Lombok's `@EqualsAndHashCode` simplifies the code and ensures that the `equals()` and `hashCode()` methods are correctly implemented based on the fields of the composite key class. This approach reduces boilerplate code and makes the implementation cleaner and easier to maintain. + +### 🌳 Project Structure +```bash +lecture_11 +β”œβ”€β”€ .mvn/wrapper/ +β”‚ └── maven-wrapper.properties +β”œβ”€β”€ src/main/ +β”‚ β”œβ”€β”€ java/com/example/lecture_11/ +β”‚ β”‚ β”œβ”€β”€ controller/ +β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentController.java +β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeController.java +β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryController.java +β”‚ β”‚ β”‚ └── TitleController.java +β”‚ β”‚ β”œβ”€β”€ data/ +β”‚ β”‚ β”‚ β”œβ”€β”€ model/ +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ composite/ +β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DeptEmpId.java +β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DeptManagerId.java +β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryId.java +β”‚ β”‚ β”‚ β”‚ β”‚ └── TitleId.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Department.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DeptEmp.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DeptManager.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Employee.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ Salary.java +β”‚ β”‚ β”‚ β”‚ └── Title.java +β”‚ β”‚ β”‚ └── repository/ +β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentRepository.java +β”‚ β”‚ β”‚ β”œβ”€β”€ DeptEmpRepository.java +β”‚ β”‚ β”‚ β”œβ”€β”€ DeptManagerRepository.java +β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeRepository.java +β”‚ β”‚ β”‚ β”œβ”€β”€ Salary.Repositoryjava +β”‚ β”‚ β”‚ └── TitleRepository.java +β”‚ β”‚ β”œβ”€β”€ service/ +β”‚ β”‚ β”‚ β”œβ”€β”€ impl/ +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentServiceImpl.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeServiceImpl.java +β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryServiceImpl.java +β”‚ β”‚ β”‚ β”‚ └── TitleServiceImpl.java +β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentService.java +β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeService.java +β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryService.java +β”‚ β”‚ β”‚ └── TitleService.java +β”‚ β”‚ └── Lecture11Application.java +β”‚ └── resources/ +β”‚ └── application.properties +β”œβ”€β”€ .gitignore +β”œβ”€β”€ mvnw +β”œβ”€β”€ mvnw.cmd +β”œβ”€β”€ pom.xml +β”œβ”€β”€ run.bat +└── run.sh +``` + +### 🧩 SQL Query Data +Here is the SQL query to create the database, table, and instantiate some data. +```sql +-- Create the database +CREATE DATABASE week6_lecture11; + +-- Use the database +USE week6_lecture11; + +-- Create employees table +CREATE TABLE employees ( + emp_no INT AUTO_INCREMENT PRIMARY KEY, + birth_date DATE NOT NULL, + first_name VARCHAR(14) NOT NULL, + last_name VARCHAR(16) NOT NULL, + gender ENUM('M', 'F') NOT NULL, + hire_date DATE NOT NULL +); + +-- Create departments table +CREATE TABLE departments ( + dept_no CHAR(4) PRIMARY KEY, + dept_name VARCHAR(40) NOT NULL UNIQUE +); + +-- Create dept_emp table +CREATE TABLE dept_emp ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, dept_no), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE +); + +-- Create dept_manager table +CREATE TABLE dept_manager ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, dept_no), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE +); + +-- Create salaries table +CREATE TABLE salaries ( + emp_no INT NOT NULL, + from_date DATE NOT NULL, + salary INT NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, from_date), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE +); + +-- Create titles table +CREATE TABLE titles ( + emp_no INT NOT NULL, + title VARCHAR(50) NOT NULL, + from_date DATE NOT NULL, + to_date DATE, + PRIMARY KEY (emp_no, title, from_date), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE +); +``` + +Here is the query to insert some generated dummy data +```sql +-- Insert employees +INSERT INTO employees (birth_date, first_name, last_name, gender, hire_date) VALUES +('1980-01-01', 'John', 'Doe', 'M', '2000-01-01'), +('1985-05-23', 'Jane', 'Smith', 'F', '2005-05-01'), +('1990-07-11', 'Alice', 'Johnson', 'F', '2010-06-01'), +('1975-02-14', 'Bob', 'Brown', 'M', '1995-03-01'), +('1988-12-25', 'Charlie', 'Davis', 'M', '2008-12-01'), +('1981-04-10', 'David', 'Evans', 'M', '2001-04-10'), +('1986-08-15', 'Laura', 'Wilson', 'F', '2006-08-15'), +('1991-03-22', 'Karen', 'Garcia', 'F', '2011-03-22'), +('1976-06-12', 'Paul', 'Martinez', 'M', '1996-06-12'), +('1989-11-30', 'Nancy', 'Rodriguez', 'F', '2009-11-30'); + +-- Insert departments +INSERT INTO departments (dept_no, dept_name) VALUES +('d001', 'Marketing'), +('d002', 'Finance'), +('d003', 'Human Resources'), +('d004', 'Engineering'), +('d005', 'Sales'); + +-- Insert dept_emp +INSERT INTO dept_emp (emp_no, dept_no, from_date, to_date) VALUES +(1, 'd001', '2000-01-01', '2002-01-01'), +(1, 'd002', '2002-01-01', '9999-01-01'), +(2, 'd002', '2005-05-01', '2010-05-01'), +(2, 'd003', '2010-05-01', '9999-01-01'), +(3, 'd003', '2010-06-01', '9999-01-01'), +(3, 'd004', '2011-01-01', '9999-01-01'), +(4, 'd004', '1995-03-01', '9999-01-01'), +(4, 'd005', '2000-01-01', '9999-01-01'), +(5, 'd001', '2008-12-01', '9999-01-01'), +(5, 'd005', '2010-01-01', '9999-01-01'), +(6, 'd002', '2001-04-10', '2003-04-10'), +(6, 'd003', '2003-04-10', '9999-01-01'), +(7, 'd003', '2006-08-15', '2011-08-15'), +(7, 'd004', '2011-08-15', '9999-01-01'), +(8, 'd001', '2011-03-22', '9999-01-01'), +(9, 'd004', '1996-06-12', '2006-06-12'), +(9, 'd005', '2006-06-12', '9999-01-01'), +(10, 'd005', '2009-11-30', '9999-01-01'); + +-- Insert dept_manager +INSERT INTO dept_manager (emp_no, dept_no, from_date, to_date) VALUES +(1, 'd001', '2000-01-01', '2002-01-01'), +(2, 'd002', '2005-05-01', '2010-05-01'), +(3, 'd003', '2010-06-01', '2011-01-01'); + +-- Insert salaries +INSERT INTO salaries (emp_no, salary, from_date, to_date) VALUES +(1, 60000, '2000-01-01', '2002-01-01'), +(1, 65000, '2002-01-01', '9999-01-01'), +(2, 75000, '2005-05-01', '2010-05-01'), +(2, 80000, '2010-05-01', '9999-01-01'), +(3, 80000, '2010-06-01', '2011-01-01'), +(3, 85000, '2011-01-01', '9999-01-01'), +(4, 90000, '1995-03-01', '2000-01-01'), +(4, 95000, '2000-01-01', '9999-01-01'), +(5, 85000, '2008-12-01', '2010-01-01'), +(5, 90000, '2010-01-01', '9999-01-01'), +(6, 65000, '2001-04-10', '2003-04-10'), +(6, 70000, '2003-04-10', '9999-01-01'), +(7, 70000, '2006-08-15', '2011-08-15'), +(7, 75000, '2011-08-15', '9999-01-01'), +(8, 72000, '2011-03-22', '9999-01-01'), +(9, 95000, '1996-06-12', '2006-06-12'), +(9, 100000, '2006-06-12', '9999-01-01'), +(10, 86000, '2009-11-30', '9999-01-01'); + +-- Insert titles +INSERT INTO titles (emp_no, title, from_date, to_date) VALUES +(1, 'Manager', '2000-01-01', '2002-01-01'), +(1, 'Senior Manager', '2002-01-01', '9999-01-01'), +(2, 'Analyst', '2005-05-01', '2010-05-01'), +(2, 'Senior Analyst', '2010-05-01', '9999-01-01'), +(3, 'HR Specialist', '2010-06-01', '2011-01-01'), +(3, 'HR Manager', '2011-01-01', '9999-01-01'), +(4, 'Engineer', '1995-03-01', '2000-01-01'), +(4, 'Senior Engineer', '2000-01-01', '9999-01-01'), +(5, 'Sales Representative', '2008-12-01', '2010-01-01'), +(5, 'Senior Sales Representative', '2010-01-01', '9999-01-01'), +(6, 'Finance Specialist', '2001-04-10', '2003-04-10'), +(6, 'Senior Finance Specialist', '2003-04-10', '9999-01-01'), +(7, 'HR Manager', '2006-08-15', '2011-08-15'), +(7, 'Senior HR Manager', '2011-08-15', '9999-01-01'), +(8, 'Marketing Specialist', '2011-03-22', '9999-01-01'), +(9, 'Senior Engineer', '1996-06-12', '2006-06-12'), +(9, 'Chief Engineer', '2006-06-12', '9999-01-01'), +(10, 'Senior Sales Representative', '2009-11-30', '9999-01-01'); +``` + +All the MySQL queries is available on [this file](/Week%2006/Lecture%2011/lecture_11/src/main/resources/data.sql). Here is the query to drop the database +```sql +-- Drop the database +DROP DATABASE IF EXISTS week6_lecture11; +``` + +Also don't forget to configure [application properties](/Week%2006/Lecture%2011/lecture_11/src/main/resources/application.propertiess) with this format +```java +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.url=jdbc:mysql://localhost:3306/<your_database> +spring.datasource.username=<your_user_name> +spring.datasource.password=<your_password> +``` + +and don't forget to add this +```java +spring.jpa.hibernate.ddl-auto=update +``` +to do database seeding using JPA Hibernate. + +### βš™οΈ How to run the program +1. Go to the `lecture_11` directory by using this command + ```bash + $ cd lecture_11 + ``` +2. Make sure you have maven installed on your computer, use `mvn -v` to check the version. +3. If you are using windows, you can run the program by using this command. + ```bash + $ ./run.bat + ``` + And if you are using Linux, you can run the program by using this command. + ```bash + $ chmod +x run.sh + $ ./run.sh + ``` + +If all the instruction is well executed, Open [localhost:8080](http://localhost:8080) to see that the REST APIs is now works. + +### πŸ”‘ List of Endpoints +| Endpoint | Method | Description | +|-----------------------------------------|:--------: |---------------------------------------------------------------------------------------------| +| /api/v1/employees | GET | Retrieve all employees with default pagination (page 0 with size 20 elements/page). | +| /api/v1/employees?page=1&size=5 | GET | Retrieve employees with pagination (page 1 with size 5 elements/page). | +| /api/v1/employees/{empNo} | GET | Retrieve a specific employee by employee number. | +| /api/v1/employees | POST | Create a new employee. | +| /api/v1/employees/{empNo} | PUT | Update an existing employee by employee number. | +| /api/v1/employees/{empNo} | DELETE | Delete an employee by employee number. | +| /api/v1/departments | GET | Retrieve all departments with default pagination (page 0 with size 20 elements/page). | +| /api/v1/departments?page=0&size=2 | GET | Retrieve departments with pagination (page 0 with size 2 elements/page). | +| /api/v1/departments/{deptNo} | GET | Retrieve a specific department by department number. | +| /api/v1/departments | POST | Create a new department. | +| /api/v1/departments/{deptNo} | PUT | Update an existing department by department number. | +| /api/v1/departments/{deptNo} | DELETE | Delete a department by department number. | +| /api/v1/salaries | GET | Retrieve salary by ID. | +| /api/v1/salaries | POST | Create a new salary record. | +| /api/v1/salaries | PUT | Update an existing salary record. | +| /api/v1/salaries | DELETE | Delete a salary record by ID. | +| /api/v1/titles | GET | Retrieve title by ID. | +| /api/v1/titles | POST | Create a new title. | +| /api/v1/titles | PUT | Update an existing title. | +| /api/v1/titles | DELETE | Delete a title record by ID. | + +### πŸ“¬ Postman Collection + +Here is the [postman collection](/Week%2006/Lecture%2011/Assignment%2001/Lecture%2011%20-%20Assignment%2001.postman_collection.json) you can use to demo the API functionality. \ No newline at end of file diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/.gitignore b/Week 06/Lecture 12/Assignment 01/lecture_12/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/.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/Week 06/Lecture 12/Assignment 01/lecture_12/.mvn/wrapper/maven-wrapper.properties b/Week 06/Lecture 12/Assignment 01/lecture_12/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..8f96f52 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,19 @@ +# 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. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/mvnw b/Week 06/Lecture 12/Assignment 01/lecture_12/mvnw new file mode 100644 index 0000000..d7c358e --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/mvnw @@ -0,0 +1,259 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + 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" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash> +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/mvnw.cmd b/Week 06/Lecture 12/Assignment 01/lecture_12/mvnw.cmd new file mode 100644 index 0000000..6f779cf --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@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 Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash> +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/pom.xml b/Week 06/Lecture 12/Assignment 01/lecture_12/pom.xml new file mode 100644 index 0000000..4d64216 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/pom.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>3.3.1</version> + <relativePath/> <!-- lookup parent from repository --> + </parent> + <groupId>com.example</groupId> + <artifactId>lecture_12</artifactId> + <version>1.0-SNAPSHOT</version> + <name>lecture_12</name> + <description>Demo project for Spring Boot</description> + <url/> + <licenses> + <license/> + </licenses> + <developers> + <developer/> + </developers> + <scm> + <connection/> + <developerConnection/> + <tag/> + <url/> + </scm> + <properties> + <java.version>21</java.version> + </properties> + <dependencies> + <!-- SpringBoot Starter --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web-services</artifactId> + </dependency> + + <!-- JPA and MySQL Starter --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-jpa</artifactId> + </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <version>8.0.33</version> + <scope>runtime</scope> + </dependency> + + <!-- Lombok Annotation --> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> + + <!-- Validator and Validation --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-validation</artifactId> + </dependency> + <dependency> + <groupId>org.hibernate.validator</groupId> + <artifactId>hibernate-validator</artifactId> + <version>8.0.0.Final</version> + </dependency> + <dependency> + <groupId>javax.validation</groupId> + <artifactId>validation-api</artifactId> + <version>2.0.1.Final</version> + </dependency> + + <!-- Mapper and struct --> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + <version>1.5.3.Final</version> + </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct-processor</artifactId> + <version>1.5.3.Final</version> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + +</project> diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/run.bat b/Week 06/Lecture 12/Assignment 01/lecture_12/run.bat new file mode 100644 index 0000000..d95af0e --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/run.bat @@ -0,0 +1,3 @@ +@echo off +echo Building the project with Maven... +mvn clean install && java -jar target/lecture_12-1.0-SNAPSHOT.jar \ No newline at end of file diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/run.sh b/Week 06/Lecture 12/Assignment 01/lecture_12/run.sh new file mode 100644 index 0000000..1fec79d --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo Building the project with Maven... +mvn clean install && java -jar target/lecture_12-1.0-SNAPSHOT.jar \ No newline at end of file diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/Lecture12Application.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/Lecture12Application.java new file mode 100644 index 0000000..3f704ca --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/Lecture12Application.java @@ -0,0 +1,13 @@ +package com.example.lecture_12; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Lecture12Application { + + public static void main(String[] args) { + SpringApplication.run(Lecture12Application.class, args); + } + +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/DepartmentController.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/DepartmentController.java new file mode 100644 index 0000000..b24de1a --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/DepartmentController.java @@ -0,0 +1,122 @@ +package com.example.lecture_12.controllers; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.lecture_12.data.model.Department; +import com.example.lecture_12.services.DepartmentService; + +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/departments") +@AllArgsConstructor +public class DepartmentController { + + private final DepartmentService departmentService; + + /** + * This method retrieves {@link Page} of {@link Department} from the database. + * + * @param page The page number to retrieve (0-based index). + * @param size The number of elements per page. + * @return ResponseEntity<List<Department>> - A response entity containing a pages of {@link Department}. + * If the pages is empty, it returns a HTTP status code 204 (No Content). + * If the operation is successful, it returns a HTTP status code 200 (OK) with the pages of {@link Department}. + */ + @GetMapping + public ResponseEntity<Page<Department>> findAll(@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { + Pageable pageable = PageRequest.of(page, size); + Page<Department> departments = departmentService.findAll(pageable); + + if (departments.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(departments); + } + + /** + * This method retrieves an {@link Department} from the database by its deptNo. + * + * @param deptNo The unique identifier of the {@link Department}. + * @return ResponseEntity<Department> - A response entity containing the {@link Department} if found, or a 404 Not Found status code if not found. + */ + @GetMapping(value = "/{deptNo}") + public ResponseEntity<Department> findDepartmentById(@PathVariable("deptNo") String deptNo) { + Optional<Department> departmentOpt= departmentService.findById(deptNo); + + if(departmentOpt.isPresent()) { + return ResponseEntity.ok(departmentOpt.get()); + } + + return ResponseEntity.notFound().build(); + } + + /** + * This method saves a {@link Department} to the database. + * + * @param department The department object to be saved. + * @return ResponseEntity<Department> - A response entity containing the saved {@link Department}. + * If the {@link Department} already exists in the database, it returns a HTTP status code 400 (Bad Request). + */ + @PostMapping + public ResponseEntity<Department> save(@RequestBody Department department) { + Optional<Department> departmentOpt = departmentService.findById(department.getDeptNo()); + + if (departmentOpt.isPresent()) { + return ResponseEntity.badRequest().build(); + } + + return ResponseEntity.ok(departmentService.save(department)); + } + + /** + * This method updates an existing {@link Department} in the database. + * + * @param department The department object to be updated. + * @return ResponseEntity<Department> - A response entity containing the updated {@link Department}. + * If the {@link Department} does not exist in the database, it returns a HTTP status code 404 (Not Found). + */ + @PutMapping(value = "/{deptNo}") + public ResponseEntity<Department> update(@PathVariable("deptNo") String deptNo, @RequestBody Department department) { + Optional<Department> departmentOpt = departmentService.findById(deptNo); + + if (departmentOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok(departmentService.save(department)); + } + + /** + * This method deletes an {@link Department} from the database by its deptNo. + * + * @param deptNo The unique identifier of the {@link Department} to be deleted. + * @return ResponseEntity<Department> - A response entity containing the deleted {@link Department} if found, or a 404 Not Found status code if not found. + */ + @DeleteMapping(value = "/{deptNo}") + public ResponseEntity<Department> deleteDepartment(@PathVariable(value = "deptNo") String deptNo) { + Optional<Department> departmentOpt = departmentService.findById(deptNo); + + if(departmentOpt.isPresent()) { + departmentService.deleteById(deptNo); + return ResponseEntity.ok().build(); + } + + return ResponseEntity.notFound().build(); + } +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java new file mode 100644 index 0000000..9b3404c --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java @@ -0,0 +1,117 @@ +package com.example.lecture_12.controllers; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.example.lecture_12.data.model.Employee; +import com.example.lecture_12.services.EmployeeService; + +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/employees") +@AllArgsConstructor +public class EmployeeController { + + private final EmployeeService employeeService; + + /** + * This method retrieves {@link Page} of {@link Employee} from the database. + * + * @param page The page number to retrieve (0-based index). + * @param size The number of elements per page. + * @return ResponseEntity<Page<Employee>> - A response entity containing a page of {@link Employee}. + * If the page is empty, it returns a HTTP status code 204 (No Content). + * If the operation is successful, it returns a HTTP status code 200 (OK) with the page of {@link Employee}. + */ + @GetMapping + public ResponseEntity<Page<Employee>> findAll(@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { + Pageable pageable = PageRequest.of(page, size); + Page<Employee> employees = employeeService.findAll(pageable); + + if (employees.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(employees); + } + + /** + * This method retrieves an {@link Employee} from the database by its empNo. + * + * @param empNo The unique identifier of the {@link Employee}. + * @return ResponseEntity<Employee> - A response entity containing the {@link Employee} if found, or a 404 Not Found status code if not found. + */ + @GetMapping(value = "/{empNo}") + public ResponseEntity<Employee> findEmployeeById(@PathVariable("empNo") Integer empNo) { + Optional<Employee> employeeOpt= employeeService.findById(empNo); + + if(employeeOpt.isPresent()) { + return ResponseEntity.ok(employeeOpt.get()); + } + + return ResponseEntity.notFound().build(); + } + + /** + * This method saves an {@link Employee} to the database. + * + * @param employee The employee object to be saved. + * @return ResponseEntity<Employee> - A response entity containing the saved {@link Employee}. + */ + @PostMapping + public ResponseEntity<Employee> save(@RequestBody Employee employee) { + return ResponseEntity.ok(employeeService.save(employee)); + } + + /** + * This method updates an existing {@link Employee} in the database. + * + * @param empNo The unique identifier of the {@link Employee} to be updated. + * @param employee The employee object to be updated. + * @return ResponseEntity<Employee> - A response entity containing the updated {@link Employee}. + * If the {@link Employee} does not exist in the database, it returns a HTTP status code 404 (Not Found). + */ + @PutMapping(value = "/{empNo}") + public ResponseEntity<Employee> update(@PathVariable Integer empNo, @RequestBody Employee employee) { + Optional<Employee> employeeOpt = employeeService.findById(empNo); + + if (employeeOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + employee.setEmpNo(empNo); + return ResponseEntity.ok(employeeService.save(employee)); + } + + /** + * This method deletes an {@link Employee} from the database by its empNo. + * + * @param empNo The unique identifier of the {@link Employee} to be deleted. + * @return ResponseEntity<Employee> - A response entity containing the deleted {@link Employee} if found, or a 404 Not Found status code if not found. + */ + @DeleteMapping(value = "/{empNo}") + public ResponseEntity<Employee> deleteEmployee(@PathVariable(value = "empNo") Integer empNo) { + Optional<Employee> employeeOpt = employeeService.findById(empNo); + + if(employeeOpt.isPresent()) { + employeeService.deleteById(empNo); + return ResponseEntity.ok().build(); + } + + return ResponseEntity.notFound().build(); + } +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/SalaryController.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/SalaryController.java new file mode 100644 index 0000000..b61880d --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/SalaryController.java @@ -0,0 +1,90 @@ +package com.example.lecture_12.controllers; + +import java.util.Optional; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.lecture_12.data.model.Salary; +import com.example.lecture_12.data.model.composite.SalaryId; +import com.example.lecture_12.services.SalaryService; + +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/salaries") +@AllArgsConstructor +public class SalaryController { + + private final SalaryService salaryService; + + /** + * This method retrieves a {@link Salary} from the database by its unique identifier. + * + * @param id The unique identifier of the {@link Salary} to be retrieved. + * @return ResponseEntity<Salary> - A response entity containing the {@link Salary} if found, or a 404 Not Found status code if not found. + */ + @GetMapping + public ResponseEntity<Salary> findSalaryById(@RequestBody SalaryId id) { + Optional<Salary> salaryOpt= salaryService.findById(id); + + if(salaryOpt.isPresent()) { + return ResponseEntity.ok(salaryOpt.get()); + } + + return ResponseEntity.notFound().build(); + } + + /** + * This method saves a {@link Salary} to the database. + * + * @param salary The salary object to be saved. + * @return ResponseEntity<Salary> - A response entity containing the saved {@link Salary}. + */ + @PostMapping + public ResponseEntity<Salary> save(@RequestBody Salary salary) { + return ResponseEntity.ok(salaryService.save(salary)); + } + + /** + * This method updates an existing {@link Salary} in the database. + * + * @param salary The salary object to be updated. + * @return ResponseEntity<Salary> - A response entity containing the updated {@link Salary}. + * If the {@link Salary} does not exist in the database, it returns a HTTP status code 404 (Not Found). + */ + @PutMapping + public ResponseEntity<Salary> update(@RequestBody Salary salary) { + Optional<Salary> salaryOpt = salaryService.findById(salary.getId()); + + if (salaryOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok(salaryService.save(salary)); + } + + /** + * This method deletes an {@link Salary} from the database by its id. + * + * @param id The unique identifier of the {@link Salary} to be deleted. + * @return ResponseEntity<Salary> - A response entity containing the deleted {@link Salary} if found, or a 404 Not Found status code if not found. + */ + @DeleteMapping + public ResponseEntity<Salary> deleteSalary(@RequestBody SalaryId id) { + Optional<Salary> salaryOpt = salaryService.findById(id); + + if(salaryOpt.isPresent()) { + salaryService.deleteById(id); + return ResponseEntity.ok().build(); + } + + return ResponseEntity.notFound().build(); + } +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/TitleController.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/TitleController.java new file mode 100644 index 0000000..59c8bfe --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/TitleController.java @@ -0,0 +1,92 @@ +package com.example.lecture_12.controllers; + +import java.util.Optional; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.lecture_12.data.model.Title; +import com.example.lecture_12.data.model.composite.TitleId; +import com.example.lecture_12.services.TitleService; + +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/titles") +@AllArgsConstructor +public class TitleController { + + private final TitleService titleService; + + /** + * This method retrieves a {@link Title} from the database by its unique identifier. + * + * @param id The unique identifier of the {@link Title} to be retrieved. + * @return ResponseEntity<Title> - A response entity containing the {@link Title} if found, or a 404 Not Found status code if not found. + */ + @GetMapping + public ResponseEntity<Title> findTitleById(@RequestBody TitleId id) { + Optional<Title> titleOpt= titleService.findById(id); + + if(titleOpt.isPresent()) { + return ResponseEntity.ok(titleOpt.get()); + } + + return ResponseEntity.notFound().build(); + } + + + /** + * This method saves a {@link Title} to the database. + * + * @param title The title object to be saved. + * @return ResponseEntity<Title> - A response entity containing the saved {@link Title}. + * If the {@link Title} already exists in the database, it returns a HTTP status code 400 (Bad Request). + */ + @PostMapping + public ResponseEntity<Title> save(@RequestBody Title title) { + return ResponseEntity.ok(titleService.save(title)); + } + + /** + * This method updates an existing {@link Title} in the database. + * + * @param title The title object to be updated. + * @return ResponseEntity<Title> - A response entity containing the updated {@link Title}. + * If the {@link Title} does not exist in the database, it returns a HTTP status code 404 (Not Found). + */ + @PutMapping + public ResponseEntity<Title> update(@RequestBody Title title) { + Optional<Title> titleOpt = titleService.findById(title.getId()); + + if (titleOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok(titleService.save(title)); + } + + /** + * This method deletes a {@link Title} from the database by its unique identifier. + * + * @param id The unique identifier of the {@link Title} to be deleted. + * @return ResponseEntity<Title> - A response entity containing the deleted {@link Title} if found and successfully deleted, or a 404 Not Found status code if not found. + */ + @DeleteMapping + public ResponseEntity<Title> deleteTitle(@RequestBody TitleId id) { + Optional<Title> titleOpt = titleService.findById(id); + + if (titleOpt.isPresent()) { + titleService.deleteById(id); + return ResponseEntity.ok().build(); + } + + return ResponseEntity.notFound().build(); + } +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Department.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Department.java new file mode 100644 index 0000000..4d2eb20 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Department.java @@ -0,0 +1,24 @@ +package com.example.lecture_12.data.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "departments") +@NoArgsConstructor +@AllArgsConstructor +public class Department { + + @Id + @Column(length = 4) + private String deptNo; + + @Column(length = 40, nullable = false, unique = true) + private String deptName; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/DeptEmp.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/DeptEmp.java new file mode 100644 index 0000000..e080649 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/DeptEmp.java @@ -0,0 +1,34 @@ +package com.example.lecture_12.data.model; + +import java.time.LocalDate; + +import com.example.lecture_12.data.model.composite.DeptEmpId; + +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "dept_emp") +@NoArgsConstructor +@AllArgsConstructor +public class DeptEmp { + + @EmbeddedId + private DeptEmpId id; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate fromDate; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/DeptManager.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/DeptManager.java new file mode 100644 index 0000000..689924c --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/DeptManager.java @@ -0,0 +1,34 @@ +package com.example.lecture_12.data.model; + +import java.time.LocalDate; + +import com.example.lecture_12.data.model.composite.DeptManagerId; + +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "dept_manager") +@NoArgsConstructor +@AllArgsConstructor +public class DeptManager { + + @EmbeddedId + private DeptManagerId id; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate fromDate; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Employee.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Employee.java new file mode 100644 index 0000000..f5c86b5 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Employee.java @@ -0,0 +1,44 @@ +package com.example.lecture_12.data.model; + +import java.time.LocalDate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "employees") +@NoArgsConstructor +@AllArgsConstructor +public class Employee { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer empNo; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate birthDate; + + @Column(length = 14, nullable = false) + private String firstName; + + @Column(length = 16, nullable = false) + private String lastName; + + @Column(columnDefinition = "enum('M','F')", nullable = false) + private String gender; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate hireDate; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Salary.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Salary.java new file mode 100644 index 0000000..993dc7f --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Salary.java @@ -0,0 +1,33 @@ +package com.example.lecture_12.data.model; + +import java.time.LocalDate; + +import com.example.lecture_12.data.model.composite.SalaryId; + +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "salaries") +@NoArgsConstructor +@AllArgsConstructor +public class Salary { + + @EmbeddedId + private SalaryId id; + + @Column(nullable = false) + private Integer salary; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Title.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Title.java new file mode 100644 index 0000000..3f3c4a8 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/Title.java @@ -0,0 +1,30 @@ +package com.example.lecture_12.data.model; + +import java.time.LocalDate; + +import com.example.lecture_12.data.model.composite.TitleId; + +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Entity +@Table(name = "titles") +@NoArgsConstructor +@AllArgsConstructor +public class Title { + + @EmbeddedId + private TitleId id; + + @Temporal(TemporalType.DATE) + @Column(nullable = false) + private LocalDate toDate; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/DeptEmpId.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/DeptEmpId.java new file mode 100644 index 0000000..6e5f5b0 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/DeptEmpId.java @@ -0,0 +1,15 @@ +package com.example.lecture_12.data.model.composite; + +import java.io.Serializable; + +import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; +import lombok.Data; + +@Data +@Embeddable +@EqualsAndHashCode +public class DeptEmpId implements Serializable { + private Integer empNo; + private String deptNo; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/DeptManagerId.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/DeptManagerId.java new file mode 100644 index 0000000..dae8580 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/DeptManagerId.java @@ -0,0 +1,15 @@ +package com.example.lecture_12.data.model.composite; + +import java.io.Serializable; + +import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; +import lombok.Data; + +@Data +@Embeddable +@EqualsAndHashCode +public class DeptManagerId implements Serializable { + private Integer empNo; + private String deptNo; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/SalaryId.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/SalaryId.java new file mode 100644 index 0000000..06e2d6d --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/SalaryId.java @@ -0,0 +1,16 @@ +package com.example.lecture_12.data.model.composite; + +import java.io.Serializable; +import java.time.LocalDate; + +import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; +import lombok.Data; + +@Data +@Embeddable +@EqualsAndHashCode +public class SalaryId implements Serializable { + private Integer empNo; + private LocalDate fromDate; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/TitleId.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/TitleId.java new file mode 100644 index 0000000..dd803d2 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/model/composite/TitleId.java @@ -0,0 +1,17 @@ +package com.example.lecture_12.data.model.composite; + +import java.io.Serializable; +import java.time.LocalDate; + +import jakarta.persistence.Embeddable; +import lombok.EqualsAndHashCode; +import lombok.Data; + +@Data +@Embeddable +@EqualsAndHashCode +public class TitleId implements Serializable { + private Integer empNo; + private String title; + private LocalDate fromDate; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DepartmentRepository.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DepartmentRepository.java new file mode 100644 index 0000000..19497ef --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DepartmentRepository.java @@ -0,0 +1,11 @@ +package com.example.lecture_12.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.example.lecture_12.data.model.Department; + +@Repository +public interface DepartmentRepository extends JpaRepository<Department, String> { +} + diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DeptEmpRepository.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DeptEmpRepository.java new file mode 100644 index 0000000..2be1e26 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DeptEmpRepository.java @@ -0,0 +1,10 @@ +package com.example.lecture_12.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.lecture_12.data.model.DeptEmp; +import com.example.lecture_12.data.model.composite.DeptEmpId; + +public interface DeptEmpRepository extends JpaRepository<DeptEmp, DeptEmpId> { +} + diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DeptManagerRepository.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DeptManagerRepository.java new file mode 100644 index 0000000..00abce8 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/DeptManagerRepository.java @@ -0,0 +1,10 @@ +package com.example.lecture_12.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.lecture_12.data.model.DeptManager; +import com.example.lecture_12.data.model.composite.DeptManagerId; + +public interface DeptManagerRepository extends JpaRepository<DeptManager, DeptManagerId> { +} + diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java new file mode 100644 index 0000000..4c38a40 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java @@ -0,0 +1,10 @@ +package com.example.lecture_12.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.example.lecture_12.data.model.Employee; + +@Repository +public interface EmployeeRepository extends JpaRepository<Employee, Integer> { +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/SalaryRepository.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/SalaryRepository.java new file mode 100644 index 0000000..6606448 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/SalaryRepository.java @@ -0,0 +1,9 @@ +package com.example.lecture_12.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.lecture_12.data.model.Salary; +import com.example.lecture_12.data.model.composite.SalaryId; + +public interface SalaryRepository extends JpaRepository<Salary, SalaryId> { +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/TitleRepository.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/TitleRepository.java new file mode 100644 index 0000000..c7cb171 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/TitleRepository.java @@ -0,0 +1,9 @@ +package com.example.lecture_12.data.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.example.lecture_12.data.model.Title; +import com.example.lecture_12.data.model.composite.TitleId; + +public interface TitleRepository extends JpaRepository<Title, TitleId> { +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/DepartmentService.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/DepartmentService.java new file mode 100644 index 0000000..a20d23b --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/DepartmentService.java @@ -0,0 +1,22 @@ +package com.example.lecture_12.services; + +import com.example.lecture_12.data.model.Department; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface DepartmentService { + // Retrieves a paginated list of {@link Department} entities. + Page<Department> findAll(Pageable pageable); + + // Retrieves an {@link Department} entity by its unique identifier. + Optional<Department> findById(String deptNo); + + // Saves or updates an {@link Department} entity in the database. + Department save(Department department); + + // Deletes an {@link Department} entity from the database by its unique identifier. + void deleteById(String deptNo); +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java new file mode 100644 index 0000000..e57cfec --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java @@ -0,0 +1,22 @@ +package com.example.lecture_12.services; + +import com.example.lecture_12.data.model.Employee; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface EmployeeService { + // Retrieves a paginated list of {@link Employee} entities. + Page<Employee> findAll(Pageable pageable); + + // Retrieves an {@link Employee} entity by its unique identifier. + Optional<Employee> findById(Integer empNo); + + // Saves or updates an {@link Employee} entity in the database. + Employee save(Employee employee); + + // Deletes an {@link Employee} entity from the database by its unique identifier. + void deleteById(Integer empNo); +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/SalaryService.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/SalaryService.java new file mode 100644 index 0000000..e1932ce --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/SalaryService.java @@ -0,0 +1,17 @@ +package com.example.lecture_12.services; + +import java.util.Optional; + +import com.example.lecture_12.data.model.Salary; +import com.example.lecture_12.data.model.composite.SalaryId; + +public interface SalaryService { + // Retrieves an {@link Salary} entity by its unique identifier. + Optional<Salary> findById(SalaryId id); + + // Saves or updates an {@link Salary} entity in the database. + Salary save(Salary salary); + + // Deletes an {@link Salary} entity from the database by its unique identifier. + void deleteById(SalaryId id); +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/TitleService.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/TitleService.java new file mode 100644 index 0000000..bda9cdb --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/TitleService.java @@ -0,0 +1,17 @@ +package com.example.lecture_12.services; + +import java.util.Optional; + +import com.example.lecture_12.data.model.Title; +import com.example.lecture_12.data.model.composite.TitleId; + +public interface TitleService { + // Retrieves an {@link Title} entity by its unique identifier. + Optional<Title> findById(TitleId id); + + // Saves or updates an {@link Title} entity in the database. + Title save(Title title); + + // Deletes an {@link Title} entity from the database by its unique identifier. + void deleteById(TitleId id); +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/DepartmentServiceImpl.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/DepartmentServiceImpl.java new file mode 100644 index 0000000..441b96d --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/DepartmentServiceImpl.java @@ -0,0 +1,64 @@ +package com.example.lecture_12.services.impl; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.example.lecture_12.data.model.Department; +import com.example.lecture_12.data.repository.DepartmentRepository; +import com.example.lecture_12.services.DepartmentService; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class DepartmentServiceImpl implements DepartmentService { + + private final DepartmentRepository departmentRepository; + + /** + * Retrieves a paginated list of {@link Department} entities. + * + * @param pageable The pagination and sorting parameters. + * @return A {@link Page} of {@link Department} entities. + */ + @Override + public Page<Department> findAll(Pageable pageable) { + return departmentRepository.findAll(pageable); + } + + /** + * Retrieves an {@link Department} entity by its unique identifier. + * + * @param deptNo The unique identifier of the {@link Department} entity to retrieve. + * @return An {@link Optional} containing the {@link Department} entity if found, or an empty {@link Optional} if not found. + */ + @Override + public Optional<Department> findById(String deptNo) { + return departmentRepository.findById(deptNo); + } + + /** + * Saves or updates an {@link Department} entity in the database. + * + * @param department The {@link Department} entity to be saved or updated. + * @return The saved or updated {@link Department} entity. + */ + @Override + public Department save(Department department) { + return departmentRepository.save(department); + } + + /** + * Deletes an {@link Department} entity from the database by its unique identifier. + * + * @param deptNo The unique identifier of the {@link Department} entity to be deleted. + * @return No return value, as the operation is void. + */ + @Override + public void deleteById(String deptNo) { + departmentRepository.deleteById(deptNo); + } +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java new file mode 100644 index 0000000..36dc1d4 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java @@ -0,0 +1,64 @@ +package com.example.lecture_12.services.impl; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.example.lecture_12.data.model.Employee; +import com.example.lecture_12.data.repository.EmployeeRepository; +import com.example.lecture_12.services.EmployeeService; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class EmployeeServiceImpl implements EmployeeService { + + private final EmployeeRepository employeeRepository; + + /** + * Retrieves a paginated list of {@link Employee} entities. + * + * @param pageable The pagination and sorting parameters. + * @return A {@link Page} of {@link Employee} entities. + */ + @Override + public Page<Employee> findAll(Pageable pageable) { + return employeeRepository.findAll(pageable); + } + + /** + * Retrieves an {@link Employee} entity by its unique identifier. + * + * @param empNo The unique identifier of the {@link Employee} entity to retrieve. + * @return An {@link Optional} containing the {@link Employee} entity if found, or an empty {@link Optional} if not found. + */ + @Override + public Optional<Employee> findById(Integer empNo) { + return employeeRepository.findById(empNo); + } + + /** + * Saves or updates an {@link Employee} entity in the database. + * + * @param employee The {@link Employee} entity to be saved or updated. + * @return The saved or updated {@link Employee} entity. + */ + @Override + public Employee save(Employee employee) { + return employeeRepository.save(employee); + } + + /** + * Deletes an {@link Employee} entity from the database by its unique identifier. + * + * @param empNo The unique identifier of the {@link Employee} entity to be deleted. + * @return No return value, as the operation is void. + */ + @Override + public void deleteById(Integer empNo) { + employeeRepository.deleteById(empNo); + } +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/SalaryServiceImpl.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/SalaryServiceImpl.java new file mode 100644 index 0000000..63e5541 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/SalaryServiceImpl.java @@ -0,0 +1,52 @@ +package com.example.lecture_12.services.impl; + +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.lecture_12.data.model.Salary; +import com.example.lecture_12.data.model.composite.SalaryId; +import com.example.lecture_12.data.repository.SalaryRepository; +import com.example.lecture_12.services.SalaryService; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class SalaryServiceImpl implements SalaryService { + + private final SalaryRepository salaryRepository; + + /** + * Retrieves an {@link Salary} entity by its unique identifier. + * + * @param id The unique identifier of the {@link Salary} entity to retrieve. + * @return An {@link Optional} containing the {@link Salary} entity if found, or an empty {@link Optional} if not found. + */ + @Override + public Optional<Salary> findById(SalaryId id) { + return salaryRepository.findById(id); + } + + /** + * Saves or updates an {@link Salary} entity in the database. + * + * @param salary The {@link Salary} entity to be saved or updated. + * @return The saved or updated {@link Salary} entity. + */ + @Override + public Salary save(Salary salary) { + return salaryRepository.save(salary); + } + + /** + * Deletes an {@link Salary} entity from the database by its unique identifier. + * + * @param id The unique identifier of the {@link Salary} entity to be deleted. + * @return No return value, as the operation is void. + */ + @Override + public void deleteById(SalaryId id) { + salaryRepository.deleteById(id); + } +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/TitleServiceImpl.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/TitleServiceImpl.java new file mode 100644 index 0000000..fb6357a --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/TitleServiceImpl.java @@ -0,0 +1,52 @@ +package com.example.lecture_12.services.impl; + +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.example.lecture_12.data.model.Title; +import com.example.lecture_12.data.model.composite.TitleId; +import com.example.lecture_12.data.repository.TitleRepository; +import com.example.lecture_12.services.TitleService; + +import lombok.AllArgsConstructor; + +@Service +@AllArgsConstructor +public class TitleServiceImpl implements TitleService { + + private final TitleRepository titleRepository; + + /** + * Retrieves an {@link Title} entity by its unique identifier. + * + * @param id The unique identifier of the {@link Title} entity to retrieve. + * @return An {@link Optional} containing the {@link Title} entity if found, or an empty {@link Optional} if not found. + */ + @Override + public Optional<Title> findById(TitleId id) { + return titleRepository.findById(id); + } + + /** + * Saves or updates an {@link Title} entity in the database. + * + * @param title The {@link Title} entity to be saved or updated. + * @return The saved or updated {@link Title} entity. + */ + @Override + public Title save(Title title) { + return titleRepository.save(title); + } + + /** + * Deletes an {@link Title} entity from the database by its unique identifier. + * + * @param id The unique identifier of the {@link Title} entity to be deleted. + * @return No return value, as the operation is void. + */ + @Override + public void deleteById(TitleId id) { + titleRepository.deleteById(id); + } +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties new file mode 100644 index 0000000..6ef52ce --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.application.name=lecture_12 + +spring.datasource.url=jdbc:mysql://localhost:3308/week6_lecture11?allowPublicKeyRetrieval=true&useSSL=false +spring.datasource.username=root +spring.datasource.password=Michaeleon16606_ +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.jpa.hibernate.ddl-auto=update \ No newline at end of file diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/data.sql b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/data.sql new file mode 100644 index 0000000..7631c07 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/data.sql @@ -0,0 +1,149 @@ +-- Database schema initializer +-- Create employees table +CREATE TABLE employees ( + emp_no INT AUTO_INCREMENT PRIMARY KEY, + birth_date DATE NOT NULL, + first_name VARCHAR(14) NOT NULL, + last_name VARCHAR(16) NOT NULL, + gender ENUM('M', 'F') NOT NULL, + hire_date DATE NOT NULL +); + +-- Create departments table +CREATE TABLE departments ( + dept_no CHAR(4) PRIMARY KEY, + dept_name VARCHAR(40) NOT NULL UNIQUE +); + +-- Create dept_emp table +CREATE TABLE dept_emp ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, dept_no), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE +); + +-- Create dept_manager table +CREATE TABLE dept_manager ( + emp_no INT NOT NULL, + dept_no CHAR(4) NOT NULL, + from_date DATE NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, dept_no), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE, + FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE +); + +-- Create salaries table +CREATE TABLE salaries ( + emp_no INT NOT NULL, + from_date DATE NOT NULL, + salary INT NOT NULL, + to_date DATE NOT NULL, + PRIMARY KEY (emp_no, from_date), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE +); + +-- Create titles table +CREATE TABLE titles ( + emp_no INT NOT NULL, + title VARCHAR(50) NOT NULL, + from_date DATE NOT NULL, + to_date DATE, + PRIMARY KEY (emp_no, title, from_date), + FOREIGN KEY (emp_no) REFERENCES employees(emp_no) ON DELETE CASCADE +); + +-- Database Initial Seeding +-- Insert employees +INSERT INTO employees (birth_date, first_name, last_name, gender, hire_date) VALUES +('1980-01-01', 'John', 'Doe', 'M', '2000-01-01'), +('1985-05-23', 'Jane', 'Smith', 'F', '2005-05-01'), +('1990-07-11', 'Alice', 'Johnson', 'F', '2010-06-01'), +('1975-02-14', 'Bob', 'Brown', 'M', '1995-03-01'), +('1988-12-25', 'Charlie', 'Davis', 'M', '2008-12-01'), +('1981-04-10', 'David', 'Evans', 'M', '2001-04-10'), +('1986-08-15', 'Laura', 'Wilson', 'F', '2006-08-15'), +('1991-03-22', 'Karen', 'Garcia', 'F', '2011-03-22'), +('1976-06-12', 'Paul', 'Martinez', 'M', '1996-06-12'), +('1989-11-30', 'Nancy', 'Rodriguez', 'F', '2009-11-30'); + +-- Insert departments +INSERT INTO departments (dept_no, dept_name) VALUES +('d001', 'Marketing'), +('d002', 'Finance'), +('d003', 'Human Resources'), +('d004', 'Engineering'), +('d005', 'Sales'); + +-- Insert dept_emp +INSERT INTO dept_emp (emp_no, dept_no, from_date, to_date) VALUES +(1, 'd001', '2000-01-01', '2002-01-01'), +(1, 'd002', '2002-01-01', '9999-01-01'), +(2, 'd002', '2005-05-01', '2010-05-01'), +(2, 'd003', '2010-05-01', '9999-01-01'), +(3, 'd003', '2010-06-01', '9999-01-01'), +(3, 'd004', '2011-01-01', '9999-01-01'), +(4, 'd004', '1995-03-01', '9999-01-01'), +(4, 'd005', '2000-01-01', '9999-01-01'), +(5, 'd001', '2008-12-01', '9999-01-01'), +(5, 'd005', '2010-01-01', '9999-01-01'), +(6, 'd002', '2001-04-10', '2003-04-10'), +(6, 'd003', '2003-04-10', '9999-01-01'), +(7, 'd003', '2006-08-15', '2011-08-15'), +(7, 'd004', '2011-08-15', '9999-01-01'), +(8, 'd001', '2011-03-22', '9999-01-01'), +(9, 'd004', '1996-06-12', '2006-06-12'), +(9, 'd005', '2006-06-12', '9999-01-01'), +(10, 'd005', '2009-11-30', '9999-01-01'); + +-- Insert dept_manager +INSERT INTO dept_manager (emp_no, dept_no, from_date, to_date) VALUES +(1, 'd001', '2000-01-01', '2002-01-01'), +(2, 'd002', '2005-05-01', '2010-05-01'), +(3, 'd003', '2010-06-01', '2011-01-01'); + +-- Insert salaries +INSERT INTO salaries (emp_no, salary, from_date, to_date) VALUES +(1, 60000, '2000-01-01', '2002-01-01'), +(1, 65000, '2002-01-01', '9999-01-01'), +(2, 75000, '2005-05-01', '2010-05-01'), +(2, 80000, '2010-05-01', '9999-01-01'), +(3, 80000, '2010-06-01', '2011-01-01'), +(3, 85000, '2011-01-01', '9999-01-01'), +(4, 90000, '1995-03-01', '2000-01-01'), +(4, 95000, '2000-01-01', '9999-01-01'), +(5, 85000, '2008-12-01', '2010-01-01'), +(5, 90000, '2010-01-01', '9999-01-01'), +(6, 65000, '2001-04-10', '2003-04-10'), +(6, 70000, '2003-04-10', '9999-01-01'), +(7, 70000, '2006-08-15', '2011-08-15'), +(7, 75000, '2011-08-15', '9999-01-01'), +(8, 72000, '2011-03-22', '9999-01-01'), +(9, 95000, '1996-06-12', '2006-06-12'), +(9, 100000, '2006-06-12', '9999-01-01'), +(10, 86000, '2009-11-30', '9999-01-01'); + +-- Insert titles +INSERT INTO titles (emp_no, title, from_date, to_date) VALUES +(1, 'Manager', '2000-01-01', '2002-01-01'), +(1, 'Senior Manager', '2002-01-01', '9999-01-01'), +(2, 'Analyst', '2005-05-01', '2010-05-01'), +(2, 'Senior Analyst', '2010-05-01', '9999-01-01'), +(3, 'HR Specialist', '2010-06-01', '2011-01-01'), +(3, 'HR Manager', '2011-01-01', '9999-01-01'), +(4, 'Engineer', '1995-03-01', '2000-01-01'), +(4, 'Senior Engineer', '2000-01-01', '9999-01-01'), +(5, 'Sales Representative', '2008-12-01', '2010-01-01'), +(5, 'Senior Sales Representative', '2010-01-01', '9999-01-01'), +(6, 'Finance Specialist', '2001-04-10', '2003-04-10'), +(6, 'Senior Finance Specialist', '2003-04-10', '9999-01-01'), +(7, 'HR Manager', '2006-08-15', '2011-08-15'), +(7, 'Senior HR Manager', '2011-08-15', '9999-01-01'), +(8, 'Marketing Specialist', '2011-03-22', '9999-01-01'), +(9, 'Senior Engineer', '1996-06-12', '2006-06-12'), +(9, 'Chief Engineer', '2006-06-12', '9999-01-01'), +(10, 'Senior Sales Representative', '2009-11-30', '9999-01-01'); diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/test/java/com/example/lecture_12/Lecture12ApplicationTests.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/test/java/com/example/lecture_12/Lecture12ApplicationTests.java new file mode 100644 index 0000000..fb1c433 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/test/java/com/example/lecture_12/Lecture12ApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.lecture_12; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class Lecture12ApplicationTests { + + @Test + void contextLoads() { + } + +} From 60cc25ca2f81b6fb002f6e24af6dea49c2be151b Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Thu, 18 Jul 2024 18:20:05 +0700 Subject: [PATCH 22/30] [Feat] Implementation of dynamic search queries --- .../controllers/EmployeeController.java | 22 +++++++++++++++ .../data/repository/EmployeeRepository.java | 5 ++++ .../dto/EmployeeSearchCriteriaDTO.java | 14 ++++++++++ .../lecture_12/services/EmployeeService.java | 8 ++++-- .../services/impl/EmployeeServiceImpl.java | 27 +++++++++++++++++++ 5 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java index 9b3404c..d4b562e 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java @@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController; import com.example.lecture_12.data.model.Employee; +import com.example.lecture_12.dto.EmployeeSearchCriteriaDTO; import com.example.lecture_12.services.EmployeeService; import lombok.AllArgsConstructor; @@ -49,6 +50,27 @@ public ResponseEntity<Page<Employee>> findAll(@RequestParam(defaultValue = "0") return ResponseEntity.ok(employees); } + /** + * Endpoint to search for {@link Employee} entities based on the provided search criteria. + * Supports pagination and sorting. + * + * @param criteria The criteria object of {@link EmployeeSearchCriteriaDTO} containing fields to filter the search. + * @param page The page number to retrieve (default is 0). + * @param size The number of elements per page (default is 20). + * @return ResponseEntity containing a {@link Page} of {@link Employee} entities that match the criteria, + */ + @GetMapping("/search") + public ResponseEntity<Page<Employee>> searchEmployees(EmployeeSearchCriteriaDTO criteria, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { + Pageable pageable = PageRequest.of(page, size); + Page<Employee> employees = employeeService.findByCriteria(criteria, pageable); + + if (employees.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(employees); + } + /** * This method retrieves an {@link Employee} from the database by its empNo. * diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java index 4c38a40..9376b7d 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java @@ -1,5 +1,8 @@ package com.example.lecture_12.data.repository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -7,4 +10,6 @@ @Repository public interface EmployeeRepository extends JpaRepository<Employee, Integer> { + // Define a custom query method using Specification and Pageable + Page<Employee> findAll(Specification<Employee> spec, Pageable pageable); } diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java new file mode 100644 index 0000000..4f4d93e --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java @@ -0,0 +1,14 @@ +package com.example.lecture_12.dto; + +import java.time.LocalDate; + +import lombok.Data; + +@Data +public class EmployeeSearchCriteriaDTO { + private LocalDate birthDate; + private String firstName; + private String lastName; + private String gender; + private LocalDate hireDate; +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java index e57cfec..02c89c0 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java @@ -1,16 +1,20 @@ package com.example.lecture_12.services; -import com.example.lecture_12.data.model.Employee; +import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import java.util.Optional; +import com.example.lecture_12.data.model.Employee; +import com.example.lecture_12.dto.EmployeeSearchCriteriaDTO; public interface EmployeeService { // Retrieves a paginated list of {@link Employee} entities. Page<Employee> findAll(Pageable pageable); + // Retrieves a paginated list of {@link Employee} entities based on the provided search criteria. + Page<Employee> findByCriteria(EmployeeSearchCriteriaDTO criteria, Pageable pageable); + // Retrieves an {@link Employee} entity by its unique identifier. Optional<Employee> findById(Integer empNo); diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java index 36dc1d4..ec4e7da 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java @@ -1,15 +1,20 @@ package com.example.lecture_12.services.impl; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import com.example.lecture_12.data.model.Employee; import com.example.lecture_12.data.repository.EmployeeRepository; +import com.example.lecture_12.dto.EmployeeSearchCriteriaDTO; import com.example.lecture_12.services.EmployeeService; +import jakarta.persistence.criteria.Predicate; import lombok.AllArgsConstructor; @Service @@ -29,6 +34,28 @@ public Page<Employee> findAll(Pageable pageable) { return employeeRepository.findAll(pageable); } + /** + * Retrieves a paginated list of {@link Employee} entities based on the provided search criteria. + * + * @param criteria The criteria object containing fields to filter the search. + * @param pageable Pagination and sorting parameters. + * @return A {@link Page} of {@link Employee} entities that match the specified criteria. + */ + @Override + public Page<Employee> findByCriteria(EmployeeSearchCriteriaDTO criteria, Pageable pageable) { + return employeeRepository.findAll((Specification<Employee>) (root, query, cb) -> { + List<Predicate> predicates = new ArrayList<>(); + + if (criteria.getBirthDate() != null) { predicates.add(cb.equal(root.get("birthDate"), criteria.getBirthDate())); } + if (criteria.getFirstName() != null) { predicates.add(cb.equal(root.get("firstName"), criteria.getFirstName())); } + if (criteria.getLastName() != null) { predicates.add(cb.equal(root.get("lastName"), criteria.getLastName())); } + if (criteria.getGender() != null) { predicates.add(cb.equal(root.get("gender"), criteria.getGender())); } + if (criteria.getHireDate() != null) { predicates.add(cb.equal(root.get("hireDate"), criteria.getHireDate())); } + + return cb.and(predicates.toArray(Predicate[]::new)); + }, pageable); + } + /** * Retrieves an {@link Employee} entity by its unique identifier. * From fc43715ff0ef55d599d667503f9275828cb91baa Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Thu, 18 Jul 2024 18:34:11 +0700 Subject: [PATCH 23/30] [Feat] Explanation on dynamic search queries --- Week 06/Lecture 12/Assignment 01/README.md | 251 +++++++++++++++------ 1 file changed, 179 insertions(+), 72 deletions(-) diff --git a/Week 06/Lecture 12/Assignment 01/README.md b/Week 06/Lecture 12/Assignment 01/README.md index ba34cd6..77c1daf 100644 --- a/Week 06/Lecture 12/Assignment 01/README.md +++ b/Week 06/Lecture 12/Assignment 01/README.md @@ -1,98 +1,203 @@ -# πŸ‘¨πŸ»β€πŸ« Lecture 11 - Spring Data JPA -> This repository is created as a part of assignment for Lecture 11 - Spring Data JPA +# πŸ‘©πŸ»β€πŸ« Lecture 12 - Spring Data JPA +> This repository is created as a part of assignment for Lecture 12 - Spring Data JPA -## πŸ“ Assignment 01 - Implementation of Model, JPA, Repositories, Services, and REST APIs +## ⚑ Assignment 01 - Adding Dynamic Criteria for Employee Search -### πŸ”Ž [Research] Composite Key in JPA +### πŸ”Ž Dynamic Search Criteria 😡😡 -Implementing a composite key in JPA (Java Persistence API) involves using an `@Embeddable` class to represent the composite key and embedding it into the entity class. Here’s a short explanation and steps to implement it: +To implement dynamic criteria search APIs for every attribute on my `Employee` model, i'll need to enhance my existing codebase to support filtering based on various attributes. Here's a detailed approach: -#### Steps to Implement Composite Key in JPA +### πŸ‘£ Step-by-Step Explanation -1. **Create the Embeddable Key Class**: - - Define a class to represent the composite key. - - Annotate the class with `@Embeddable`. - - Implement `Serializable` interface. - - Override `equals()` and `hashCode()` methods. In this case i'm using using `@Data` and `@EqualsAndHashCode` from Lombok to automatically generate it. +1. **Define Search Criteria**: I decided how i want to pass search criteria to my API. Common approaches include query parameters (`/api/v1/employees?firstName=John&gender=M`) or a JSON object in the request body (`POST` request with a JSON body containing search criteria). In this implementation, i choose the query parameters. -2. **Embed the Key in the Entity Class**: - - Use `@EmbeddedId` annotation in the entity class to include the composite key. - - Annotate the entity class with `@Entity` and other necessary JPA annotations. +2. **DTO (Data Transfer Object)**: I used DTOs to transfer data between layers (controller, service, repository). This helps in decoupling my API contract from my entity structure and provides flexibility in handling incoming requests. -3. **Map the Composite Key Columns**: - - Map the fields of the embeddable key class to the corresponding columns in the database. +3. **Service Layer Modification**: I enhanced my service layer to handle dynamic filtering using specifications or query methods. Specifications are particularly useful for complex queries involving multiple criteria. -#### Example +4. **Controller Layer Modification**: I also modified my controller to accept dynamic search criteria and delegate the search to the service layer. -##### Embeddable Key Class -For this example i will use [SalaryId Class](/Week%2006/Lecture%2011/Assignment%2001/lecture_11/src/main/java/com/example/lecture_11/data/model/composite/SalaryId.java). +5. **Implementation Considerations**: I also not forget to handle various scenarios such as no search criteria provided, pagination, sorting, and proper error handling for invalid queries. + +### πŸ‘¨πŸ»β€πŸ’» Implementation: + +#### 1. Create a DTO for Search Criteria ([EmployeeSearchCriteriaDTO.java](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java)) ```java -import java.io.Serializable; +package com.example.lecture_12.dto; + import java.time.LocalDate; -import jakarta.persistence.Embeddable; import lombok.Data; -import lombok.EqualsAndHashCode; @Data -@Embeddable -@EqualsAndHashCode -public class SalaryId implements Serializable { - private Integer empNo; - private LocalDate fromDate; +public class EmployeeSearchCriteriaDTO { + private LocalDate birthDate; + private String firstName; + private String lastName; + private String gender; + private LocalDate hireDate; } ``` -##### Entity Class -For this example i will use [Salary Class](/Week%2006/Lecture%2011/Assignment%2001/lecture_11/src/main/java/com/example/lecture_11/data/model/Salary.java). +#### 2. Update Employee Repository ([EmployeeRepository.java](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/java/com/example/lecture_12/data/repository/EmployeeRepository.java)) + ```java -import java.time.LocalDate; -import com.example.lecture_11.data.model.composite.SalaryId; -import jakarta.persistence.Column; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; -import jakarta.persistence.Temporal; -import jakarta.persistence.TemporalType; +package com.example.lecture_12.data.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.example.lecture_12.data.model.Employee; + +@Repository +public interface EmployeeRepository extends JpaRepository<Employee, Integer> { + // Define a custom query method using Specification and Pageable + Page<Employee> findAll(Specification<Employee> spec, Pageable pageable); +} +``` + +#### 3. Modify Employee Service Interface ([EmployeeService.java](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/java/com/example/lecture_12/services/EmployeeService.java)) + +```java +package com.example.lecture_12.services; + +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import com.example.lecture_12.data.model.Employee; +import com.example.lecture_12.dto.EmployeeSearchCriteriaDTO; + +public interface EmployeeService { + .... + + // Retrieves a paginated list of {@link Employee} entities based on the provided search criteria. + Page<Employee> findByCriteria(EmployeeSearchCriteriaDTO criteria, Pageable pageable); + + .... +} +``` + +#### 4. Implement Employee Service ([EmployeeServiceImpl.java](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java)) + +```java +package com.example.lecture_12.services.impl; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import com.example.lecture_12.data.model.Employee; +import com.example.lecture_12.data.repository.EmployeeRepository; +import com.example.lecture_12.dto.EmployeeSearchCriteriaDTO; +import com.example.lecture_12.services.EmployeeService; +import jakarta.persistence.criteria.Predicate; import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -@Data -@Entity -@Table(name = "salaries") -@NoArgsConstructor +@Service @AllArgsConstructor -public class Salary { +public class EmployeeServiceImpl implements EmployeeService { - @EmbeddedId - private SalaryId id; + private final EmployeeRepository employeeRepository; + + .... + + /** + * Retrieves a paginated list of {@link Employee} entities based on the provided search criteria. + * + * @param criteria The criteria object containing fields to filter the search. + * @param pageable Pagination and sorting parameters. + * @return A {@link Page} of {@link Employee} entities that match the specified criteria. + */ + @Override + public Page<Employee> findByCriteria(EmployeeSearchCriteriaDTO criteria, Pageable pageable) { + return employeeRepository.findAll((Specification<Employee>) (root, query, cb) -> { + List<Predicate> predicates = new ArrayList<>(); + + if (criteria.getBirthDate() != null) { predicates.add(cb.equal(root.get("birthDate"), criteria.getBirthDate())); } + if (criteria.getFirstName() != null) { predicates.add(cb.equal(root.get("firstName"), criteria.getFirstName())); } + if (criteria.getLastName() != null) { predicates.add(cb.equal(root.get("lastName"), criteria.getLastName())); } + if (criteria.getGender() != null) { predicates.add(cb.equal(root.get("gender"), criteria.getGender())); } + if (criteria.getHireDate() != null) { predicates.add(cb.equal(root.get("hireDate"), criteria.getHireDate())); } + + return cb.and(predicates.toArray(Predicate[]::new)); + }, pageable); + } + + .... +} +``` - @Column(nullable = false) - private Integer salary; +#### 5. Update Employee Controller ([EmployeeController.java](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java)) - @Temporal(TemporalType.DATE) - @Column(nullable = false) - private LocalDate toDate; +```java +package com.example.lecture_12.controllers; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import com.example.lecture_12.data.model.Employee; +import com.example.lecture_12.dto.EmployeeSearchCriteriaDTO; +import com.example.lecture_12.services.EmployeeService; +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/employees") +@AllArgsConstructor +public class EmployeeController { + + private final EmployeeService employeeService; + + .... + + /** + * Endpoint to search for {@link Employee} entities based on the provided search criteria. + * Supports pagination and sorting. + * + * @param criteria The criteria object of {@link EmployeeSearchCriteriaDTO} containing fields to filter the search. + * @param page The page number to retrieve (default is 0). + * @param size The number of elements per page (default is 20). + * @return ResponseEntity containing a {@link Page} of {@link Employee} entities that match the criteria, + */ + @GetMapping("/search") + public ResponseEntity<Page<Employee>> searchEmployees(EmployeeSearchCriteriaDTO criteria, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { + Pageable pageable = PageRequest.of(page, size); + Page<Employee> employees = employeeService.findByCriteria(criteria, pageable); + + if (employees.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(employees); + } + + .... } ``` -#### Explanation +### πŸ“’ Explanation of Code +Here is the detail explanation on what i already done throughout the code. -1. **SalaryId Class**: - - Annotated with `@Embeddable`, indicating it is a composite key. - - Implements `Serializable`. - - Includes necessary fields (`empNo`, `fromDate`) that form the composite key. - - Uses Lombok's `@EqualsAndHashCode` to automatically generate `equals()` and `hashCode()` methods based on the fields of the class. +- **DTO**: `EmployeeSearchCriteriaDTO` is a simple class to hold search criteria. Each attribute corresponds to a field in the `Employee` entity. +- **Database Layer (JPA)**: adding `findAll` with Specification and Pageable on `EmployeeRepository` to handle spesific search query criteria dynamically from the database also to implement pagination easily. +- **Service Layer**: `EmployeeServiceImpl` implements `findByCriteria` method using JPA Specifications to dynamically build predicates based on provided criteria. +- **Controller Layer**: `EmployeeController` exposes a `GET` endpoint `/api/v1/employees/search` to accept search criteria as query parameters and returns a list of matching `Employee` entities. -2. **Salary Class**: - - Annotated with `@Entity` to indicate it is a JPA entity. - - Uses `@EmbeddedId` to include `SalaryId` as the primary key. - - Defines other entity attributes (`salary`, `toDate`). +### πŸ“ Some Notable Mentions -By following these steps, i successfully implement and use composite keys in the JPA entities. +- **Security**: I'm ensuring to validate and sanitize input to prevent injection attacks. +- **Performance**: Instead of just showing all the filtered criteria, i also use pagination (`Pageable`) to handle large result sets efficiently. +- **Flexibility**: In the program i implemented, i expand the approach by handling more complex queries using JPA `Specifications` which makes the execution more smooth and dynamic. -Using Lombok's `@EqualsAndHashCode` simplifies the code and ensures that the `equals()` and `hashCode()` methods are correctly implemented based on the fields of the composite key class. This approach reduces boilerplate code and makes the implementation cleaner and easier to maintain. +This approach ensures my API to be flexible, maintainable, and follows best practices for handling dynamic search criteria in a Spring Boot application using JPA. ### 🌳 Project Structure ```bash @@ -126,6 +231,8 @@ lecture_11 β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeRepository.java β”‚ β”‚ β”‚ β”œβ”€β”€ Salary.Repositoryjava β”‚ β”‚ β”‚ └── TitleRepository.java +β”‚ β”‚ β”œβ”€β”€ dto/ +β”‚ β”‚ β”‚ └── EmployeeSearchCriteriaDTO.java β”‚ β”‚ β”œβ”€β”€ service/ β”‚ β”‚ β”‚ β”œβ”€β”€ impl/ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentServiceImpl.java @@ -151,10 +258,10 @@ lecture_11 Here is the SQL query to create the database, table, and instantiate some data. ```sql -- Create the database -CREATE DATABASE week6_lecture11; +CREATE DATABASE week6_lecture12; -- Use the database -USE week6_lecture11; +USE week6_lecture12; -- Create employees table CREATE TABLE employees ( @@ -311,15 +418,15 @@ INSERT INTO titles (emp_no, title, from_date, to_date) VALUES All the MySQL queries is available on [this file](/Week%2006/Lecture%2011/lecture_11/src/main/resources/data.sql). Here is the query to drop the database ```sql -- Drop the database -DROP DATABASE IF EXISTS week6_lecture11; +DROP DATABASE IF EXISTS week6_lecture12; ``` Also don't forget to configure [application properties](/Week%2006/Lecture%2011/lecture_11/src/main/resources/application.propertiess) with this format ```java spring.datasource.driver-class-name=com.mysql.jdbc.Driver -spring.datasource.url=jdbc:mysql://localhost:3306/<your_database> -spring.datasource.username=<your_user_name> -spring.datasource.password=<your_password> +spring.datasource.url=jdbc:mysql://localhost:3306/<my_database> +spring.datasource.username=<my_user_name> +spring.datasource.password=<my_password> ``` and don't forget to add this @@ -329,11 +436,11 @@ spring.jpa.hibernate.ddl-auto=update to do database seeding using JPA Hibernate. ### βš™οΈ How to run the program -1. Go to the `lecture_11` directory by using this command +1. Go to the `lecture_12` directory by using this command ```bash - $ cd lecture_11 + $ cd lecture_12 ``` -2. Make sure you have maven installed on your computer, use `mvn -v` to check the version. +2. Make sure you have maven installed on my computer, use `mvn -v` to check the version. 3. If you are using windows, you can run the program by using this command. ```bash $ ./run.bat From 8462791918555dd3482e4605b1679e97834aa165 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Thu, 18 Jul 2024 19:23:47 +0700 Subject: [PATCH 24/30] [Feat] Explanation on dynamic search queries --- Week 06/Lecture 12/Assignment 01/README.md | 105 ++---------------- .../src/main/resources/application.properties | 2 +- 2 files changed, 12 insertions(+), 95 deletions(-) diff --git a/Week 06/Lecture 12/Assignment 01/README.md b/Week 06/Lecture 12/Assignment 01/README.md index 77c1daf..f90b7a5 100644 --- a/Week 06/Lecture 12/Assignment 01/README.md +++ b/Week 06/Lecture 12/Assignment 01/README.md @@ -322,100 +322,7 @@ CREATE TABLE titles ( ); ``` -Here is the query to insert some generated dummy data -```sql --- Insert employees -INSERT INTO employees (birth_date, first_name, last_name, gender, hire_date) VALUES -('1980-01-01', 'John', 'Doe', 'M', '2000-01-01'), -('1985-05-23', 'Jane', 'Smith', 'F', '2005-05-01'), -('1990-07-11', 'Alice', 'Johnson', 'F', '2010-06-01'), -('1975-02-14', 'Bob', 'Brown', 'M', '1995-03-01'), -('1988-12-25', 'Charlie', 'Davis', 'M', '2008-12-01'), -('1981-04-10', 'David', 'Evans', 'M', '2001-04-10'), -('1986-08-15', 'Laura', 'Wilson', 'F', '2006-08-15'), -('1991-03-22', 'Karen', 'Garcia', 'F', '2011-03-22'), -('1976-06-12', 'Paul', 'Martinez', 'M', '1996-06-12'), -('1989-11-30', 'Nancy', 'Rodriguez', 'F', '2009-11-30'); - --- Insert departments -INSERT INTO departments (dept_no, dept_name) VALUES -('d001', 'Marketing'), -('d002', 'Finance'), -('d003', 'Human Resources'), -('d004', 'Engineering'), -('d005', 'Sales'); - --- Insert dept_emp -INSERT INTO dept_emp (emp_no, dept_no, from_date, to_date) VALUES -(1, 'd001', '2000-01-01', '2002-01-01'), -(1, 'd002', '2002-01-01', '9999-01-01'), -(2, 'd002', '2005-05-01', '2010-05-01'), -(2, 'd003', '2010-05-01', '9999-01-01'), -(3, 'd003', '2010-06-01', '9999-01-01'), -(3, 'd004', '2011-01-01', '9999-01-01'), -(4, 'd004', '1995-03-01', '9999-01-01'), -(4, 'd005', '2000-01-01', '9999-01-01'), -(5, 'd001', '2008-12-01', '9999-01-01'), -(5, 'd005', '2010-01-01', '9999-01-01'), -(6, 'd002', '2001-04-10', '2003-04-10'), -(6, 'd003', '2003-04-10', '9999-01-01'), -(7, 'd003', '2006-08-15', '2011-08-15'), -(7, 'd004', '2011-08-15', '9999-01-01'), -(8, 'd001', '2011-03-22', '9999-01-01'), -(9, 'd004', '1996-06-12', '2006-06-12'), -(9, 'd005', '2006-06-12', '9999-01-01'), -(10, 'd005', '2009-11-30', '9999-01-01'); - --- Insert dept_manager -INSERT INTO dept_manager (emp_no, dept_no, from_date, to_date) VALUES -(1, 'd001', '2000-01-01', '2002-01-01'), -(2, 'd002', '2005-05-01', '2010-05-01'), -(3, 'd003', '2010-06-01', '2011-01-01'); - --- Insert salaries -INSERT INTO salaries (emp_no, salary, from_date, to_date) VALUES -(1, 60000, '2000-01-01', '2002-01-01'), -(1, 65000, '2002-01-01', '9999-01-01'), -(2, 75000, '2005-05-01', '2010-05-01'), -(2, 80000, '2010-05-01', '9999-01-01'), -(3, 80000, '2010-06-01', '2011-01-01'), -(3, 85000, '2011-01-01', '9999-01-01'), -(4, 90000, '1995-03-01', '2000-01-01'), -(4, 95000, '2000-01-01', '9999-01-01'), -(5, 85000, '2008-12-01', '2010-01-01'), -(5, 90000, '2010-01-01', '9999-01-01'), -(6, 65000, '2001-04-10', '2003-04-10'), -(6, 70000, '2003-04-10', '9999-01-01'), -(7, 70000, '2006-08-15', '2011-08-15'), -(7, 75000, '2011-08-15', '9999-01-01'), -(8, 72000, '2011-03-22', '9999-01-01'), -(9, 95000, '1996-06-12', '2006-06-12'), -(9, 100000, '2006-06-12', '9999-01-01'), -(10, 86000, '2009-11-30', '9999-01-01'); - --- Insert titles -INSERT INTO titles (emp_no, title, from_date, to_date) VALUES -(1, 'Manager', '2000-01-01', '2002-01-01'), -(1, 'Senior Manager', '2002-01-01', '9999-01-01'), -(2, 'Analyst', '2005-05-01', '2010-05-01'), -(2, 'Senior Analyst', '2010-05-01', '9999-01-01'), -(3, 'HR Specialist', '2010-06-01', '2011-01-01'), -(3, 'HR Manager', '2011-01-01', '9999-01-01'), -(4, 'Engineer', '1995-03-01', '2000-01-01'), -(4, 'Senior Engineer', '2000-01-01', '9999-01-01'), -(5, 'Sales Representative', '2008-12-01', '2010-01-01'), -(5, 'Senior Sales Representative', '2010-01-01', '9999-01-01'), -(6, 'Finance Specialist', '2001-04-10', '2003-04-10'), -(6, 'Senior Finance Specialist', '2003-04-10', '9999-01-01'), -(7, 'HR Manager', '2006-08-15', '2011-08-15'), -(7, 'Senior HR Manager', '2011-08-15', '9999-01-01'), -(8, 'Marketing Specialist', '2011-03-22', '9999-01-01'), -(9, 'Senior Engineer', '1996-06-12', '2006-06-12'), -(9, 'Chief Engineer', '2006-06-12', '9999-01-01'), -(10, 'Senior Sales Representative', '2009-11-30', '9999-01-01'); -``` - -All the MySQL queries is available on [this file](/Week%2006/Lecture%2011/lecture_11/src/main/resources/data.sql). Here is the query to drop the database +There are also query to insert some generated dummy data. All the MySQL queries is available on [this file](/Week%2006/Lecture%2011/lecture_11/src/main/resources/data.sql). Here is the query to drop the database ```sql -- Drop the database DROP DATABASE IF EXISTS week6_lecture12; @@ -477,6 +384,16 @@ If all the instruction is well executed, Open [localhost:8080](http://localhost: | /api/v1/titles | PUT | Update an existing title. | | /api/v1/titles | DELETE | Delete a title record by ID. | +#### Additional Dynamic Search Queries +Here is some demo on how to search employees based on dynamic search queries. All the method used are `GET`. + +| Endpoint | Description | +|-----------------------------------------|---------------------------------------------------------------------------------------------| +| /api/v1/employees/search?firstName=Paul&gender=M | Retrieve all male employees who the first name is Paul. | +| /api/v1/employees/search?birthDate=1991-03-22&lastName=Garcia | Retrieve all employees who the birth date is March 22nd, 1991 and the last name is Garcia. | +| api/v1/employees/search?gender=F&page=1&size=3 | Retrieve all the female employees with pagination (page 1 with size 3 elements/page). | +| api/v1/employees/search | Retrieve all the employees data with default pagination. | + ### πŸ“¬ Postman Collection Here is the [postman collection](/Week%2006/Lecture%2011/Assignment%2001/Lecture%2011%20-%20Assignment%2001.postman_collection.json) you can use to demo the API functionality. \ No newline at end of file diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties index 6ef52ce..3c002b1 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties @@ -1,6 +1,6 @@ spring.application.name=lecture_12 -spring.datasource.url=jdbc:mysql://localhost:3308/week6_lecture11?allowPublicKeyRetrieval=true&useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3308/week6_lecture12?allowPublicKeyRetrieval=true&useSSL=false spring.datasource.username=root spring.datasource.password=Michaeleon16606_ spring.datasource.driver-class-name=com.mysql.jdbc.Driver From 734f43f76623f776bb00816f6d4a05c5986f03b3 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Thu, 18 Jul 2024 19:53:36 +0700 Subject: [PATCH 25/30] [Feat] add "/" --- Week 06/Lecture 12/Assignment 01/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Week 06/Lecture 12/Assignment 01/README.md b/Week 06/Lecture 12/Assignment 01/README.md index f90b7a5..055d9db 100644 --- a/Week 06/Lecture 12/Assignment 01/README.md +++ b/Week 06/Lecture 12/Assignment 01/README.md @@ -391,8 +391,8 @@ Here is some demo on how to search employees based on dynamic search queries. Al |-----------------------------------------|---------------------------------------------------------------------------------------------| | /api/v1/employees/search?firstName=Paul&gender=M | Retrieve all male employees who the first name is Paul. | | /api/v1/employees/search?birthDate=1991-03-22&lastName=Garcia | Retrieve all employees who the birth date is March 22nd, 1991 and the last name is Garcia. | -| api/v1/employees/search?gender=F&page=1&size=3 | Retrieve all the female employees with pagination (page 1 with size 3 elements/page). | -| api/v1/employees/search | Retrieve all the employees data with default pagination. | +| /api/v1/employees/search?gender=F&page=1&size=3 | Retrieve all the female employees with pagination (page 1 with size 3 elements/page). | +| /api/v1/employees/search | Retrieve all the employees data with default pagination. | ### πŸ“¬ Postman Collection From 333aa6e5b41eb5afeeea66e794368b26860d7e7c Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Thu, 18 Jul 2024 22:09:47 +0700 Subject: [PATCH 26/30] [Feat] Implementation of Bonus --- .../controllers/EmployeeController.java | 7 +- .../dto/EmployeeSearchCriteriaDTO.java | 11 ++ .../services/impl/EmployeeServiceImpl.java | 85 +++++++++++++-- .../lecture_12/src/main/resources/data.sql | 100 +++++++++++++++++- 4 files changed, 192 insertions(+), 11 deletions(-) diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java index d4b562e..f7abdde 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/controllers/EmployeeController.java @@ -52,12 +52,13 @@ public ResponseEntity<Page<Employee>> findAll(@RequestParam(defaultValue = "0") /** * Endpoint to search for {@link Employee} entities based on the provided search criteria. - * Supports pagination and sorting. + * Supports pagination, sorting, and various operations. * - * @param criteria The criteria object of {@link EmployeeSearchCriteriaDTO} containing fields to filter the search. + * @param criteria The criteria object containing fields to filter the search. * @param page The page number to retrieve (default is 0). * @param size The number of elements per page (default is 20). - * @return ResponseEntity containing a {@link Page} of {@link Employee} entities that match the criteria, + * @return ResponseEntity containing a {@link Page} of {@link Employee} entities that match the criteria, + * or HTTP status code 204 (No Content) if no employees match the criteria. */ @GetMapping("/search") public ResponseEntity<Page<Employee>> searchEmployees(EmployeeSearchCriteriaDTO criteria, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java index 4f4d93e..b35accd 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/dto/EmployeeSearchCriteriaDTO.java @@ -7,8 +7,19 @@ @Data public class EmployeeSearchCriteriaDTO { private LocalDate birthDate; + private Integer birthMonth; + private Integer birthYear; + private String birthDateOperation; private String firstName; + private String firstNameOperation; private String lastName; + private String lastNameOperation; private String gender; + private String genderOperation; private LocalDate hireDate; + private Integer hireMonth; + private Integer hireYear; + private String hireDateOperation; + private String sortBy; + private String sortOrder; } diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java index ec4e7da..62ee440 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/services/impl/EmployeeServiceImpl.java @@ -5,7 +5,9 @@ import java.util.Optional; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @@ -38,22 +40,91 @@ public Page<Employee> findAll(Pageable pageable) { * Retrieves a paginated list of {@link Employee} entities based on the provided search criteria. * * @param criteria The criteria object containing fields to filter the search. + * - firstName: Filter by first name with the specified operation (eq, like). + * - lastName: Filter by last name with the specified operation (eq, like). + * - gender: Filter by gender with the specified operation (eq). + * - birthDate: Filter by birth date with the specified operation (eq, gt, lt, geq, leq). + * - hireDate: Filter by hire date with the specified operation (eq, gt, lt, geq, leq). + * - birthMonth: Filter by birth month. + * - birthYear: Filter by birth year. + * - hireMonth: Filter by hire month. + * - hireYear: Filter by hire year. + * - sortBy: Field to sort by. + * - sortOrder: Sort order (asc, desc). * @param pageable Pagination and sorting parameters. * @return A {@link Page} of {@link Employee} entities that match the specified criteria. */ @Override public Page<Employee> findByCriteria(EmployeeSearchCriteriaDTO criteria, Pageable pageable) { - return employeeRepository.findAll((Specification<Employee>) (root, query, cb) -> { + Specification<Employee> specification = (root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); - if (criteria.getBirthDate() != null) { predicates.add(cb.equal(root.get("birthDate"), criteria.getBirthDate())); } - if (criteria.getFirstName() != null) { predicates.add(cb.equal(root.get("firstName"), criteria.getFirstName())); } - if (criteria.getLastName() != null) { predicates.add(cb.equal(root.get("lastName"), criteria.getLastName())); } - if (criteria.getGender() != null) { predicates.add(cb.equal(root.get("gender"), criteria.getGender())); } - if (criteria.getHireDate() != null) { predicates.add(cb.equal(root.get("hireDate"), criteria.getHireDate())); } + // First name handling + if (criteria.getFirstName() != null) { + if ("like".equalsIgnoreCase(criteria.getFirstNameOperation())) { + predicates.add(cb.like(root.get("firstName"), "%" + criteria.getFirstName() + "%")); + } else { + predicates.add(cb.equal(root.get("firstName"), criteria.getFirstName())); + } + } + + // Last name handling + if (criteria.getLastName() != null) { + if ("like".equalsIgnoreCase(criteria.getLastNameOperation())) { + predicates.add(cb.like(root.get("lastName"), "%" + criteria.getLastName() + "%")); + } else { + predicates.add(cb.equal(root.get("lastName"), criteria.getLastName())); + } + } + + // Gender handling + if (criteria.getGender() != null) { + predicates.add(cb.equal(root.get("gender"), criteria.getGender())); + } + + // Birth date handling + if (criteria.getBirthDate() != null) { + switch (criteria.getBirthDateOperation()) { + case "gt" -> predicates.add(cb.greaterThan(root.get("birthDate"), criteria.getBirthDate())); + case "lt" -> predicates.add(cb.lessThan(root.get("birthDate"), criteria.getBirthDate())); + case "geq" -> predicates.add(cb.greaterThanOrEqualTo(root.get("birthDate"), criteria.getBirthDate())); + case "leq" -> predicates.add(cb.lessThanOrEqualTo(root.get("birthDate"), criteria.getBirthDate())); + default -> predicates.add(cb.equal(root.get("birthDate"), criteria.getBirthDate())); + } + } + if (criteria.getBirthMonth() != null) { + predicates.add(cb.equal(cb.function("MONTH", Integer.class, root.get("birthDate")), criteria.getBirthMonth())); + } + if (criteria.getBirthYear() != null) { + predicates.add(cb.equal(cb.function("YEAR", Integer.class, root.get("birthDate")), criteria.getBirthYear())); + } + + // Hire date handling + if (criteria.getHireDate() != null) { + switch (criteria.getHireDateOperation()) { + case "gt" -> predicates.add(cb.greaterThan(root.get("hireDate"), criteria.getHireDate())); + case "lt" -> predicates.add(cb.lessThan(root.get("hireDate"), criteria.getHireDate())); + case "geq" -> predicates.add(cb.greaterThanOrEqualTo(root.get("hireDate"), criteria.getHireDate())); + case "leq" -> predicates.add(cb.lessThanOrEqualTo(root.get("hireDate"), criteria.getHireDate())); + default -> predicates.add(cb.equal(root.get("hireDate"), criteria.getHireDate())); + } + } + if (criteria.getHireMonth() != null) { + predicates.add(cb.equal(cb.function("MONTH", Integer.class, root.get("hireDate")), criteria.getHireMonth())); + } + if (criteria.getHireYear() != null) { + predicates.add(cb.equal(cb.function("YEAR", Integer.class, root.get("hireDate")), criteria.getHireYear())); + } return cb.and(predicates.toArray(Predicate[]::new)); - }, pageable); + }; + + if (criteria.getSortBy() != null && criteria.getSortOrder() != null) { + Sort sort = Sort.by(Sort.Direction.fromString(criteria.getSortOrder()), criteria.getSortBy()); + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort); + } + + return employeeRepository.findAll(specification, pageable); } /** diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/data.sql b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/data.sql index 7631c07..e2981dd 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/data.sql +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/data.sql @@ -69,7 +69,105 @@ INSERT INTO employees (birth_date, first_name, last_name, gender, hire_date) VAL ('1986-08-15', 'Laura', 'Wilson', 'F', '2006-08-15'), ('1991-03-22', 'Karen', 'Garcia', 'F', '2011-03-22'), ('1976-06-12', 'Paul', 'Martinez', 'M', '1996-06-12'), -('1989-11-30', 'Nancy', 'Rodriguez', 'F', '2009-11-30'); +('1989-11-30', 'Nancy', 'Rodriguez', 'F', '2009-11-30'), +('1977-09-05', 'Michael', 'Clark', 'M', '1997-09-05'), +('1982-11-02', 'Barbara', 'Lewis', 'F', '2002-11-02'), +('1987-10-18', 'James', 'Lee', 'M', '2007-10-18'), +('1992-01-26', 'Susan', 'Walker', 'F', '2012-01-26'), +('1978-04-17', 'Brian', 'Hall', 'M', '1998-04-17'), +('1983-03-30', 'Sarah', 'Allen', 'F', '2003-03-30'), +('1988-07-14', 'Christopher', 'Young', 'M', '2008-07-14'), +('1993-02-20', 'Patricia', 'King', 'F', '2013-02-20'), +('1979-11-23', 'George', 'Wright', 'M', '1999-11-23'), +('1984-08-09', 'Linda', 'Scott', 'F', '2004-08-09'), +('1989-06-15', 'Thomas', 'Green', 'M', '2009-06-15'), +('1994-09-29', 'Donna', 'Adams', 'F', '2014-09-29'), +('1975-12-31', 'Daniel', 'Baker', 'M', '1995-12-31'), +('1980-10-23', 'Betty', 'Gonzalez', 'F', '2000-10-23'), +('1985-05-05', 'Steven', 'Nelson', 'M', '2005-05-05'), +('1990-11-08', 'Sandra', 'Carter', 'F', '2010-11-08'), +('1976-07-15', 'Eric', 'Mitchell', 'M', '1996-07-15'), +('1981-02-27', 'Sharon', 'Perez', 'F', '2001-02-27'), +('1986-12-10', 'Kevin', 'Roberts', 'M', '2006-12-10'), +('1991-08-21', 'Carol', 'Turner', 'F', '2011-08-21'), +('1977-05-03', 'Edward', 'Phillips', 'M', '1997-05-03'), +('1982-03-11', 'Martha', 'Campbell', 'F', '2002-03-11'), +('1987-09-25', 'Joshua', 'Parker', 'M', '2007-09-25'), +('1992-06-30', 'Rebecca', 'Evans', 'F', '2012-06-30'), +('1978-12-08', 'Gregory', 'Edwards', 'M', '1998-12-08'), +('1983-07-22', 'Virginia', 'Collins', 'F', '2003-07-22'), +('1988-05-29', 'Andrew', 'Stewart', 'M', '2008-05-29'), +('1993-11-13', 'Kathleen', 'Sanchez', 'F', '2013-11-13'), +('1979-04-19', 'Henry', 'Morris', 'M', '1999-04-19'), +('1984-10-01', 'Diane', 'Rogers', 'F', '2004-10-01'), +('1989-02-16', 'Patrick', 'Reed', 'M', '2009-02-16'), +('1994-12-22', 'Deborah', 'Cook', 'F', '2014-12-22'), +('1975-03-28', 'Adam', 'Morgan', 'M', '1995-03-28'), +('1980-09-14', 'Frances', 'Bell', 'F', '2000-09-14'), +('1985-04-24', 'Raymond', 'Murphy', 'M', '2005-04-24'), +('1990-03-09', 'Jacqueline', 'Bailey', 'F', '2010-03-09'), +('1976-01-30', 'Jack', 'Rivera', 'M', '1996-01-30'), +('1981-11-18', 'Janet', 'Cooper', 'F', '2001-11-18'), +('1986-08-03', 'Walter', 'Richardson', 'M', '2006-08-03'), +('1991-04-26', 'Christine', 'Cox', 'F', '2011-04-26'), +('1977-06-06', 'Peter', 'Howard', 'M', '1997-06-06'), +('1982-12-31', 'Kathryn', 'Ward', 'F', '2002-12-31'), +('1987-05-21', 'Harold', 'Torres', 'M', '2007-05-21'), +('1992-10-15', 'Maria', 'Peterson', 'F', '2012-10-15'), +('1978-08-07', 'Douglas', 'Gray', 'M', '1998-08-07'), +('1983-01-14', 'Evelyn', 'Ramirez', 'F', '2003-01-14'), +('1988-11-27', 'Jerry', 'James', 'M', '2008-11-27'), +('1993-05-19', 'Janice', 'Watson', 'F', '2013-05-19'), +('1979-07-31', 'Ryan', 'Brooks', 'M', '1999-07-31'), +('1984-02-05', 'Heather', 'Kelly', 'F', '2004-02-05'), +('1989-10-22', 'Lawrence', 'Sanders', 'M', '2009-10-22'), +('1994-03-15', 'Judith', 'Price', 'F', '2014-03-15'), +('1975-11-29', 'Albert', 'Bennett', 'M', '1995-11-29'), +('1980-06-04', 'Ann', 'Wood', 'F', '2000-06-04'), +('1985-07-08', 'Joe', 'Barnes', 'M', '2005-07-08'), +('1990-02-03', 'Rachel', 'Ross', 'F', '2010-02-03'), +('1976-03-20', 'Arthur', 'Henderson', 'M', '1996-03-20'), +('1981-09-09', 'Julia', 'Coleman', 'F', '2001-09-09'), +('1986-11-30', 'Bruce', 'Jenkins', 'M', '2006-11-30'), +('1991-07-17', 'Hannah', 'Perry', 'F', '2011-07-17'), +('1977-02-12', 'Philip', 'Powell', 'M', '1997-02-12'), +('1982-04-29', 'Catherine', 'Long', 'F', '2002-04-29'), +('1987-03-06', 'Chris', 'Patterson', 'M', '2007-03-06'), +('1992-08-11', 'Kathy', 'Hughes', 'F', '2012-08-11'), +('1978-10-28', 'Jonathan', 'Flores', 'M', '1998-10-28'), +('1983-06-01', 'Megan', 'Washington', 'F', '2003-06-01'), +('1988-09-13', 'Albert', 'Butler', 'M', '2008-09-13'), +('1993-01-05', 'Katherine', 'Simmons', 'F', '2013-01-05'), +('1979-05-15', 'Anthony', 'Foster', 'M', '1999-05-15'), +('1984-08-25', 'Diana', 'Gonzales', 'F', '2004-08-25'), +('1989-12-02', 'Johnny', 'Bryant', 'M', '2009-12-02'), +('1994-11-14', 'Theresa', 'Alexander', 'F', '2014-11-14'), +('1975-04-07', 'Randy', 'Russell', 'M', '1995-04-07'), +('1980-01-19', 'Stephanie', 'Griffin', 'F', '2000-01-19'), +('1985-12-08', 'Jesse', 'Diaz', 'M', '2005-12-08'), +('1990-04-25', 'Angela', 'Hayes', 'F', '2010-04-25'), +('1976-08-18', 'Billy', 'Myers', 'M', '1996-08-18'), +('1981-07-04', 'Helen', 'Ford', 'F', '2001-07-04'), +('1986-05-10', 'Ralph', 'Hamilton', 'M', '2006-05-10'), +('1991-10-03', 'Frances', 'Graham', 'F', '2011-10-03'), +('1977-11-23', 'Roy', 'Sullivan', 'M', '1997-11-23'), +('1982-02-16', 'Virginia', 'Wallace', 'F', '2002-02-16'), +('1987-01-29', 'Bobby', 'Woods', 'M', '2007-01-29'), +('1992-07-20', 'Janet', 'Cole', 'F', '2012-07-20'), +('1978-06-24', 'Terry', 'West', 'M', '1998-06-24'), +('1983-09-06', 'Maria', 'Jordan', 'F', '2003-09-06'), +('1988-04-03', 'Bruce', 'Owens', 'M', '2008-04-03'), +('1993-10-30', 'Paula', 'Reynolds', 'F', '2013-10-30'), +('1979-03-18', 'Scott', 'Fisher', 'M', '1999-03-18'), +('1984-12-26', 'Kelly', 'Ellis', 'F', '2004-12-26'), +('1989-08-14', 'Sean', 'Harrison', 'M', '2009-08-14'), +('1994-05-09', 'Anne', 'Gibson', 'F', '2014-05-09'), +('1975-10-20', 'Walter', 'Mcdonald', 'M', '1995-10-20'), +('1980-05-17', 'Denise', 'Cruz', 'F', '2000-05-17'), +('1985-01-03', 'Eugene', 'Marshall', 'M', '2005-01-03'), +('1990-07-28', 'Judith', 'Ortiz', 'F', '2010-07-28'), +('1976-04-05', 'Jesse', 'Gomez', 'M', '1996-04-05'), +('1981-10-12', 'Jacqueline', 'Murray', 'F', '2001-10-12'); -- Insert departments INSERT INTO departments (dept_no, dept_name) VALUES From dd2c92fe8ca77099447122c5b139c7ba40884bd2 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Thu, 18 Jul 2024 23:08:43 +0700 Subject: [PATCH 27/30] [Feat] README and postman documentation --- ...11 - Assignment 01.postman_collection.json | 366 -------- ...12 - Assignment 01.postman_collection.json | 835 ++++++++++++++++++ Week 06/Lecture 12/Assignment 01/README.md | 98 +- 3 files changed, 929 insertions(+), 370 deletions(-) delete mode 100644 Week 06/Lecture 12/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json create mode 100644 Week 06/Lecture 12/Assignment 01/Lecture 12 - Assignment 01.postman_collection.json diff --git a/Week 06/Lecture 12/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json b/Week 06/Lecture 12/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json deleted file mode 100644 index 0075f54..0000000 --- a/Week 06/Lecture 12/Assignment 01/Lecture 11 - Assignment 01.postman_collection.json +++ /dev/null @@ -1,366 +0,0 @@ -{ - "info": { - "_postman_id": "3093d6b8-a742-4d7c-b565-95715055e5d3", - "name": "Lecture 11 - Assignment 01", - "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", - "_exporter_id": "34693283" - }, - "item": [ - { - "name": "Employees", - "item": [ - { - "name": "All Employees", - "request": { - "method": "GET", - "header": [], - "url": "localhost:8080/api/v1/employees" - }, - "response": [] - }, - { - "name": "All Employees Configurable Pages", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "localhost:8080/api/v1/employees?page=1&size=5", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "api", - "v1", - "employees" - ], - "query": [ - { - "key": "page", - "value": "1" - }, - { - "key": "size", - "value": "5" - } - ] - } - }, - "response": [] - }, - { - "name": "Employee By EmpNo", - "request": { - "method": "GET", - "header": [], - "url": "localhost:8080/api/v1/employees/3" - }, - "response": [] - }, - { - "name": "New Employee", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"birthDate\": \"2003-08-12\",\r\n \"firstName\": \"Michael\",\r\n \"lastName\": \"Leon\",\r\n \"gender\": \"M\",\r\n \"hireDate\": \"2024-07-17\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/employees" - }, - "response": [] - }, - { - "name": "Edit Employee", - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"birthDate\": \"2003-08-12\",\r\n \"firstName\": \"Leon\",\r\n \"lastName\": \"Michael\",\r\n \"gender\": \"M\",\r\n \"hireDate\": \"2024-07-18\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/employees/11" - }, - "response": [] - }, - { - "name": "Delete Employee", - "request": { - "method": "DELETE", - "header": [], - "url": "localhost:8080/api/v1/employees/11" - }, - "response": [] - } - ] - }, - { - "name": "Departments", - "item": [ - { - "name": "All Departments", - "request": { - "method": "GET", - "header": [], - "url": "localhost:8080/api/v1/departments" - }, - "response": [] - }, - { - "name": "All Departments Configurable Pages", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "localhost:8080/api/v1/departments?page=0&size=2", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "api", - "v1", - "departments" - ], - "query": [ - { - "key": "page", - "value": "0" - }, - { - "key": "size", - "value": "2" - } - ] - } - }, - "response": [] - }, - { - "name": "Department By DeptNo", - "request": { - "method": "GET", - "header": [], - "url": "localhost:8080/api/v1/departments/d004" - }, - "response": [] - }, - { - "name": "New Department", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"deptNo\": \"d006\",\r\n \"deptName\": \"Research\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/departments" - }, - "response": [] - }, - { - "name": "Edit Employee", - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"deptNo\": \"d006\",\r\n \"deptName\": \"New Research\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/departments/d006" - }, - "response": [] - }, - { - "name": "Delete Employee", - "request": { - "method": "DELETE", - "header": [], - "url": "localhost:8080/api/v1/departments/d006" - }, - "response": [] - } - ] - }, - { - "name": "Salaries", - "item": [ - { - "name": "Salary by ID", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2000-01-01\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/salaries" - }, - "response": [] - }, - { - "name": "New Salary", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": {\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n },\r\n \"salary\": 60000,\r\n \"toDate\": \"2025-07-17\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/salaries" - }, - "response": [] - }, - { - "name": "Edit Salary", - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": {\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n },\r\n \"salary\": 65000,\r\n \"toDate\": \"2025-07-17\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/salaries" - }, - "response": [] - }, - { - "name": "Delete Salary", - "request": { - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/salaries" - }, - "response": [] - } - ] - }, - { - "name": "Titles", - "item": [ - { - "name": "Title by ID", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"empNo\": 1,\r\n \"title\": \"Manager\",\r\n \"fromDate\": \"2000-01-01\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/titles" - }, - "response": [] - }, - { - "name": "New Title", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": {\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n },\r\n \"toDate\": \"2002-01-01\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/titles" - }, - "response": [] - }, - { - "name": "Edit Title", - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": {\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n },\r\n \"toDate\": \"2020-01-01\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/titles" - }, - "response": [] - }, - { - "name": "Delete Title", - "request": { - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": "localhost:8080/api/v1/salaries" - }, - "response": [] - } - ] - } - ] -} \ No newline at end of file diff --git a/Week 06/Lecture 12/Assignment 01/Lecture 12 - Assignment 01.postman_collection.json b/Week 06/Lecture 12/Assignment 01/Lecture 12 - Assignment 01.postman_collection.json new file mode 100644 index 0000000..3da82dd --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/Lecture 12 - Assignment 01.postman_collection.json @@ -0,0 +1,835 @@ +{ + "info": { + "_postman_id": "3093d6b8-a742-4d7c-b565-95715055e5d3", + "name": "Lecture 12 - Assignment 01", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", + "_exporter_id": "34693283" + }, + "item": [ + { + "name": "Employees", + "item": [ + { + "name": "All Employees", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/employees" + }, + "response": [] + }, + { + "name": "All Employees Configurable Pages", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees?page=1&size=5", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "size", + "value": "5" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee By EmpNo", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/employees/3" + }, + "response": [] + }, + { + "name": "New Employee", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"birthDate\": \"2003-08-12\",\r\n \"firstName\": \"Michael\",\r\n \"lastName\": \"Leon\",\r\n \"gender\": \"M\",\r\n \"hireDate\": \"2024-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/employees" + }, + "response": [] + }, + { + "name": "Edit Employee", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"birthDate\": \"2003-08-12\",\r\n \"firstName\": \"Leon\",\r\n \"lastName\": \"Michael\",\r\n \"gender\": \"M\",\r\n \"hireDate\": \"2024-07-18\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/employees/12" + }, + "response": [] + }, + { + "name": "Delete Employee", + "request": { + "method": "DELETE", + "header": [], + "url": "localhost:8080/api/v1/employees/12" + }, + "response": [] + } + ] + }, + { + "name": "Departments", + "item": [ + { + "name": "All Departments", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/departments" + }, + "response": [] + }, + { + "name": "All Departments Configurable Pages", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/departments?page=0&size=2", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "departments" + ], + "query": [ + { + "key": "page", + "value": "0" + }, + { + "key": "size", + "value": "2" + } + ] + } + }, + "response": [] + }, + { + "name": "Department By DeptNo", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/departments/d004" + }, + "response": [] + }, + { + "name": "New Department", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"deptNo\": \"d006\",\r\n \"deptName\": \"Research\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/departments" + }, + "response": [] + }, + { + "name": "Edit Employee", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"deptNo\": \"d006\",\r\n \"deptName\": \"New Research\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/departments/d006" + }, + "response": [] + }, + { + "name": "Delete Employee", + "request": { + "method": "DELETE", + "header": [], + "url": "localhost:8080/api/v1/departments/d006" + }, + "response": [] + } + ] + }, + { + "name": "Salaries", + "item": [ + { + "name": "Salary by ID", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2000-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "New Salary", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n },\r\n \"salary\": 60000,\r\n \"toDate\": \"2025-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "Edit Salary", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n },\r\n \"salary\": 65000,\r\n \"toDate\": \"2025-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + }, + { + "name": "Delete Salary", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"fromDate\": \"2024-07-17\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/salaries" + }, + "response": [] + } + ] + }, + { + "name": "Titles", + "item": [ + { + "name": "Title by ID", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 1,\r\n \"title\": \"Manager\",\r\n \"fromDate\": \"2000-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "New Title", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n },\r\n \"toDate\": \"2002-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "Edit Title", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": {\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n },\r\n \"toDate\": \"2020-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + }, + { + "name": "Delete Title", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"empNo\": 8,\r\n \"title\": \"Engineer\",\r\n \"fromDate\": \"2000-01-01\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "localhost:8080/api/v1/titles" + }, + "response": [] + } + ] + }, + { + "name": "Advance Employees Search", + "item": [ + { + "name": "Employee Dynamic Search (1)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?firstName=Paul&gender=M", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "firstName", + "value": "Paul" + }, + { + "key": "gender", + "value": "M" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Dynamic Search (2)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?birthDate=1991-03-22&birthDateOperation=eq&lastName=Garcia", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "birthDate", + "value": "1991-03-22" + }, + { + "key": "birthDateOperation", + "value": "eq" + }, + { + "key": "lastName", + "value": "Garcia" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Dynamic Search (3)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?gender=F&page=1&size=3", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "gender", + "value": "F" + }, + { + "key": "page", + "value": "1" + }, + { + "key": "size", + "value": "3" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Dynamic Search (4)", + "request": { + "method": "GET", + "header": [], + "url": "localhost:8080/api/v1/employees/search" + }, + "response": [] + }, + { + "name": "Employee Advance Search (1)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?firstName=John&firstNameOperation=like&sortBy=lastName&sortOrder=asc", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "firstName", + "value": "John" + }, + { + "key": "firstNameOperation", + "value": "like" + }, + { + "key": "sortBy", + "value": "lastName" + }, + { + "key": "sortOrder", + "value": "asc" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (2)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?hireDate=2020-06-01&hireDateOperation=lt&size=40&page=2", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "hireDate", + "value": "2020-06-01" + }, + { + "key": "hireDateOperation", + "value": "lt" + }, + { + "key": "size", + "value": "40" + }, + { + "key": "page", + "value": "2" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (3)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?birthDate=1990-01-01&birthDateOperation=gt&page=1", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "birthDate", + "value": "1990-01-01" + }, + { + "key": "birthDateOperation", + "value": "gt" + }, + { + "key": "page", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (4)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?lastName=Smith&hireYear=2015", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "lastName", + "value": "Smith" + }, + { + "key": "hireYear", + "value": "2015" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (5)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?birthMonth=2", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "birthMonth", + "value": "2" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (6)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?firstName=Albert&lastName=B&lastNameOperation=like&gender=M", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "firstName", + "value": "Albert" + }, + { + "key": "lastName", + "value": "B" + }, + { + "key": "lastNameOperation", + "value": "like" + }, + { + "key": "gender", + "value": "M" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (7)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?firstName=Bobby&birthDate=1985-01-01&birthDateOperation=geq", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "firstName", + "value": "Bobby" + }, + { + "key": "birthDate", + "value": "1985-01-01" + }, + { + "key": "birthDateOperation", + "value": "geq" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (8)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?hireDate=2010-12-31&hireDateOperation=leq&sortBy=hireDate&sortOrder=desc&page=4", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "hireDate", + "value": "2010-12-31" + }, + { + "key": "hireDateOperation", + "value": "leq" + }, + { + "key": "sortBy", + "value": "hireDate" + }, + { + "key": "sortOrder", + "value": "desc" + }, + { + "key": "page", + "value": "4" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (9)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?birthMonth=8&hireYear=2011", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "birthMonth", + "value": "8" + }, + { + "key": "hireYear", + "value": "2011" + } + ] + } + }, + "response": [] + }, + { + "name": "Employee Advance Search (10)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8080/api/v1/employees/search?firstName=Michael&birthDate=1977-09-05&birthDateOperation=eq", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "employees", + "search" + ], + "query": [ + { + "key": "firstName", + "value": "Michael" + }, + { + "key": "birthDate", + "value": "1977-09-05" + }, + { + "key": "birthDateOperation", + "value": "eq" + } + ] + } + }, + "response": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/Week 06/Lecture 12/Assignment 01/README.md b/Week 06/Lecture 12/Assignment 01/README.md index 055d9db..4d2d18d 100644 --- a/Week 06/Lecture 12/Assignment 01/README.md +++ b/Week 06/Lecture 12/Assignment 01/README.md @@ -199,6 +199,8 @@ Here is the detail explanation on what i already done throughout the code. This approach ensures my API to be flexible, maintainable, and follows best practices for handling dynamic search criteria in a Spring Boot application using JPA. +--- + ### 🌳 Project Structure ```bash lecture_11 @@ -364,13 +366,13 @@ If all the instruction is well executed, Open [localhost:8080](http://localhost: | Endpoint | Method | Description | |-----------------------------------------|:--------: |---------------------------------------------------------------------------------------------| | /api/v1/employees | GET | Retrieve all employees with default pagination (page 0 with size 20 elements/page). | -| /api/v1/employees?page=1&size=5 | GET | Retrieve employees with pagination (page 1 with size 5 elements/page). | +| /api/v1/employees?page=1&size=5 | GET | Retrieve employees with pagination (page 2 with size 5 elements/page). | | /api/v1/employees/{empNo} | GET | Retrieve a specific employee by employee number. | | /api/v1/employees | POST | Create a new employee. | | /api/v1/employees/{empNo} | PUT | Update an existing employee by employee number. | | /api/v1/employees/{empNo} | DELETE | Delete an employee by employee number. | | /api/v1/departments | GET | Retrieve all departments with default pagination (page 0 with size 20 elements/page). | -| /api/v1/departments?page=0&size=2 | GET | Retrieve departments with pagination (page 0 with size 2 elements/page). | +| /api/v1/departments?page=0&size=2 | GET | Retrieve departments with pagination (page 1 with size 2 elements/page). | | /api/v1/departments/{deptNo} | GET | Retrieve a specific department by department number. | | /api/v1/departments | POST | Create a new department. | | /api/v1/departments/{deptNo} | PUT | Update an existing department by department number. | @@ -391,9 +393,97 @@ Here is some demo on how to search employees based on dynamic search queries. Al |-----------------------------------------|---------------------------------------------------------------------------------------------| | /api/v1/employees/search?firstName=Paul&gender=M | Retrieve all male employees who the first name is Paul. | | /api/v1/employees/search?birthDate=1991-03-22&lastName=Garcia | Retrieve all employees who the birth date is March 22nd, 1991 and the last name is Garcia. | -| /api/v1/employees/search?gender=F&page=1&size=3 | Retrieve all the female employees with pagination (page 1 with size 3 elements/page). | +| /api/v1/employees/search?gender=F&page=1&size=3 | Retrieve all the female employees with pagination (page 2 with size 3 elements/page). | | /api/v1/employees/search | Retrieve all the employees data with default pagination. | +--- + +### πŸ”₯ Bonus - Advanced Query Functionality +#### Overview + +This part of assignment implements advanced query functionality for the `Employee` entity. The main motivation was to allow users to perform flexible and complex searches based on various criteria, including sorting and advanced operations like greater than, less than, and querying by specific date parts (e.g., month, year). The implementation uses Spring Data JPA's Specification and Criteria API to dynamically construct queries based on the provided search criteria (basically just modifying what i already made previously). + +#### Features +1. **Dynamic Filtering**: Allows filtering based on multiple criteria. +2. **Advanced Operations**: Supports operations like equals, greater than, less than, greater than or equal to, and less than or equal to. +3. **Date Part Queries**: Enables querying by specific parts of dates, such as month and year. +4. **Sorting**: Supports sorting by any field in ascending or descending order. +5. **Pagination**: Handles large datasets efficiently by providing pagination. + +#### Functionalities + +- Filter by first name, last name, gender, birth date, and hire date. +- Perform advanced operations (`eq`, `gt`, `lt`, `geq`, `leq`) on dates. +- Query by specific date parts (month, year). +- Sort results by any field. +- Paginate results to handle large datasets. + +The updated endpoint for searching employees with advanced criteria will look like this: + +##### Endpoint: `GET /api/v1/employees/search` + +##### Parameters + +- **page** (optional): The page number to retrieve (default is 0). +- **size** (optional): The number of elements per page (default is 20). +- **sortBy** (optional): The field to sort by (e.g., "firstName", "hireDate"). +- **sortOrder** (optional): The sort order ("asc" or "desc"). +- **firstName** (optional): The first name to filter by. +- **firstNameOperation** (optional): The operation for first name ("eq" for equals, "like" for like). +- **lastName** (optional): The last name to filter by. +- **lastNameOperation** (optional): The operation for last name ("eq" for equals, "like" for like). +- **gender** (optional): The gender to filter by. +- **genderOperation** (optional): The operation for gender ("eq" for equals). +- **birthDate** (optional): The birth date to filter by (format: YYYY-MM-DD). +- **birthDateOperation** (optional): The operation for birth date ("eq", "gt", "lt", "geq", "leq"). +- **hireDate** (optional): The hire date to filter by (format: YYYY-MM-DD). +- **hireDateOperation** (optional): The operation for hire date ("eq", "gt", "lt", "geq", "leq"). +- **birthMonth** (optional): The birth month to filter by (1-12). +- **birthYear** (optional): The birth year to filter by (e.g., 1970). +- **hireMonth** (optional): The hire month to filter by (1-12). +- **hireYear** (optional): The hire year to filter by (e.g., 2020). + +##### Example Request +Here is the URL `GET` request query to do this: + +Find all employees with the first name containing "John", hired after January 1st, 2020, sorted by last name in ascending order. The result will be paginated with custom pagination where maximum 10 employees/page and show the employees data on page 1 (0-based index). +```http +GET /api/v1/employees/search?page=0&size=10&sortBy=lastName&sortOrder=asc&firstName=John&firstNameOperation=like&hireDate=2020-01-01&hireDateOperation=gt +``` + +#### Advanced Query Examples + +Here are 10 examples of advanced queries you can perform with this implementation. All the method used are `GET`. + +| Endpoint | Description | +|-----------------------------------------|---------------------------------------------------------------------------------------------| +| /api/v1/employees/search?firstName=John&firstNameOperation=like&sortBy=lastName&sortOrder=asc | Find employees with first name containing "John" and sort by last name ascending. | +| /api/v1/employees/search?hireDate=2020-06-01&hireDateOperation=lt&size=40&page=2 | Find employees hired before June 1st, 2020 with custom pagination (page 3 with size 40 elements/page). | +| /api/v1/employees/search?birthDate=1990-01-01&birthDateOperation=gt&page=1 | Find employees born after January 1st, 1990 with default pagination, show employees on page 2. | +| /api/v1/employees/search?lastName=Smith&hireYear=2015 | Find employees with last name "Smith" and hired in the year 2015. | +| /api/v1/employees/search?birthMonth=2 | Find employees born in February. | +| /api/v1/employees/search?firstName=Albert&lastName=B&lastNameOperation=like&gender=M | Find male employees with first name "Albert" and last name starting with "B". | +| /api/v1/employees/search?firstName=Bobby&birthDate=1985-01-01&birthDateOperation=geq | Find employees with first name "Bobby" and birth date greater than or equal to January 1st, 1985. | +| /api/v1/employees/search?hireDate=2010-12-31&hireDateOperation=leq&sortBy=hireDate&sortOrder=desc&page=4 | Find employees hired on or before December 31st, 2010, and sort by hire date descending with default pagination, show employees on page 5. | +| /api/v1/employees/search?birthMonth=8&hireYear=2011 | Find employees born in August and hired in the year 2011. | +| /api/v1/employees/search?firstName=Michael&birthDate=1977-09-05&birthDateOperation=eq | Find employees with first name "Michael" and birth date equal to September 5th, 1977. | + +#### Remarks and Conclusion + +In the industry, building flexible and advanced search functionality for REST APIs is a common practice, especially for applications that handle complex data retrieval requirements. The approach i made is in line with how such functionality is typically implemented. Here are a few points that highlight common practices in the industry: + +1. **Specification and Criteria API**: Using the JPA Specification and Criteria API is a standard approach to build dynamic queries in a type-safe way. This allows for complex query construction based on various criteria, which is a common requirement in many applications. + +2. **Pagination and Sorting**: Providing support for pagination and sorting in API endpoints is essential for handling large datasets. This is typically done using Spring Data's `Pageable` and `Sort` interfaces, which i've included in the `findByCriteria` method. + +3. **DTOs for Search Criteria**: Using Data Transfer Objects (DTOs) to encapsulate search criteria is a common practice. This helps in structuring the input parameters and makes the API more maintainable and understandable. + +4. **Combining Filters and Operations**: Allowing different operations (e.g., equality, greater than, less than) and combining them with logical operators (AND, OR) is a typical requirement for advanced search functionality. The use of predicates in the Specification API facilitates this. + +5. **Documentation and Consistency**: Documenting the API endpoints and ensuring consistent parameter naming conventions is crucial. This helps other developers understand and use the API correctly. + +This advanced query functionality significantly enhances the flexibility and usability of the `Employee` API. By supporting complex query operations, sorting, and pagination, it caters to a wide range of search requirements, making it a robust solution for applications that need sophisticated data retrieval capabilities. + ### πŸ“¬ Postman Collection -Here is the [postman collection](/Week%2006/Lecture%2011/Assignment%2001/Lecture%2011%20-%20Assignment%2001.postman_collection.json) you can use to demo the API functionality. \ No newline at end of file +Here is the [postman collection](/Week%2006/Lecture%2012/Assignment%2001/Lecture%2012%20-%20Assignment%2001.postman_collection.json) you can use to demo the API functionality, including the bonus part i already made on the separate APIs folder. \ No newline at end of file From c95778c818f80211bd3fd10d06f7eb474d7d191d Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Thu, 18 Jul 2024 23:24:30 +0700 Subject: [PATCH 28/30] [Refactor] Update README.md --- Week 06/Lecture 12/Assignment 01/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Week 06/Lecture 12/Assignment 01/README.md b/Week 06/Lecture 12/Assignment 01/README.md index 4d2d18d..1c488f5 100644 --- a/Week 06/Lecture 12/Assignment 01/README.md +++ b/Week 06/Lecture 12/Assignment 01/README.md @@ -324,13 +324,13 @@ CREATE TABLE titles ( ); ``` -There are also query to insert some generated dummy data. All the MySQL queries is available on [this file](/Week%2006/Lecture%2011/lecture_11/src/main/resources/data.sql). Here is the query to drop the database +There are also query to insert some generated dummy data. All the MySQL queries is available on [this file](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/resources/data.sql). Here is the query to drop the database ```sql -- Drop the database DROP DATABASE IF EXISTS week6_lecture12; ``` -Also don't forget to configure [application properties](/Week%2006/Lecture%2011/lecture_11/src/main/resources/application.propertiess) with this format +Also don't forget to configure [application properties](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/resources/application.properties) with this format ```java spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/<my_database> @@ -365,13 +365,13 @@ If all the instruction is well executed, Open [localhost:8080](http://localhost: ### πŸ”‘ List of Endpoints | Endpoint | Method | Description | |-----------------------------------------|:--------: |---------------------------------------------------------------------------------------------| -| /api/v1/employees | GET | Retrieve all employees with default pagination (page 0 with size 20 elements/page). | +| /api/v1/employees | GET | Retrieve all employees with default pagination (page 1 with size 20 elements/page). | | /api/v1/employees?page=1&size=5 | GET | Retrieve employees with pagination (page 2 with size 5 elements/page). | | /api/v1/employees/{empNo} | GET | Retrieve a specific employee by employee number. | | /api/v1/employees | POST | Create a new employee. | | /api/v1/employees/{empNo} | PUT | Update an existing employee by employee number. | | /api/v1/employees/{empNo} | DELETE | Delete an employee by employee number. | -| /api/v1/departments | GET | Retrieve all departments with default pagination (page 0 with size 20 elements/page). | +| /api/v1/departments | GET | Retrieve all departments with default pagination (page 1 with size 20 elements/page). | | /api/v1/departments?page=0&size=2 | GET | Retrieve departments with pagination (page 1 with size 2 elements/page). | | /api/v1/departments/{deptNo} | GET | Retrieve a specific department by department number. | | /api/v1/departments | POST | Create a new department. | From 16affc43bba2c33427248709bab7a09c0d982383 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Fri, 19 Jul 2024 10:02:15 +0700 Subject: [PATCH 29/30] [Feat] Page serialization + SQL logging --- .../java/com/example/lecture_12/config/WebConfig.java | 9 +++++++++ .../lecture_12/src/main/resources/application.properties | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/config/WebConfig.java diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/config/WebConfig.java b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/config/WebConfig.java new file mode 100644 index 0000000..606e495 --- /dev/null +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/java/com/example/lecture_12/config/WebConfig.java @@ -0,0 +1,9 @@ +package com.example.lecture_12.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.web.config.EnableSpringDataWebSupport; + +@Configuration +@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO) +public class WebConfig { +} diff --git a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties index 3c002b1..2ad9b31 100644 --- a/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties +++ b/Week 06/Lecture 12/Assignment 01/lecture_12/src/main/resources/application.properties @@ -1,7 +1,14 @@ spring.application.name=lecture_12 +# Datasorce connection data spring.datasource.url=jdbc:mysql://localhost:3308/week6_lecture12?allowPublicKeyRetrieval=true&useSSL=false spring.datasource.username=root spring.datasource.password=Michaeleon16606_ spring.datasource.driver-class-name=com.mysql.jdbc.Driver -spring.jpa.hibernate.ddl-auto=update \ No newline at end of file +spring.jpa.hibernate.ddl-auto=update + +# Enable SQL logging and show the statements and params + formatting +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE \ No newline at end of file From 0f8f3534803b52501f779a62eb0a2fdac2ae3843 Mon Sep 17 00:00:00 2001 From: mikeleo03 <leonmichael463@gmail.com> Date: Fri, 19 Jul 2024 10:14:22 +0700 Subject: [PATCH 30/30] [Feat] SQL hibernate logging --- Week 06/Lecture 12/Assignment 01/README.md | 28 +++++++++++++----- .../Assignment 01/img/hibernate.png | Bin 0 -> 28446 bytes 2 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 Week 06/Lecture 12/Assignment 01/img/hibernate.png diff --git a/Week 06/Lecture 12/Assignment 01/README.md b/Week 06/Lecture 12/Assignment 01/README.md index 1c488f5..b3b1153 100644 --- a/Week 06/Lecture 12/Assignment 01/README.md +++ b/Week 06/Lecture 12/Assignment 01/README.md @@ -203,11 +203,13 @@ This approach ensures my API to be flexible, maintainable, and follows best prac ### 🌳 Project Structure ```bash -lecture_11 +lecture_12 β”œβ”€β”€ .mvn/wrapper/ β”‚ └── maven-wrapper.properties β”œβ”€β”€ src/main/ -β”‚ β”œβ”€β”€ java/com/example/lecture_11/ +β”‚ β”œβ”€β”€ java/com/example/lecture_12/ +β”‚ β”‚ β”œβ”€β”€ config/ +β”‚ β”‚ β”‚ └── WebConfig.java β”‚ β”‚ β”œβ”€β”€ controller/ β”‚ β”‚ β”‚ β”œβ”€β”€ DepartmentController.java β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeController.java @@ -245,7 +247,7 @@ lecture_11 β”‚ β”‚ β”‚ β”œβ”€β”€ EmployeeService.java β”‚ β”‚ β”‚ β”œβ”€β”€ SalaryService.java β”‚ β”‚ β”‚ └── TitleService.java -β”‚ β”‚ └── Lecture11Application.java +β”‚ β”‚ └── Lecture12Application.java β”‚ └── resources/ β”‚ └── application.properties β”œβ”€β”€ .gitignore @@ -324,13 +326,13 @@ CREATE TABLE titles ( ); ``` -There are also query to insert some generated dummy data. All the MySQL queries is available on [this file](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/resources/data.sql). Here is the query to drop the database +There are also query to insert some generated dummy data. All the MySQL queries is available on [this file](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/resources/data.sql). Here is the query to drop the database. ```sql -- Drop the database DROP DATABASE IF EXISTS week6_lecture12; ``` -Also don't forget to configure [application properties](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/resources/application.properties) with this format +Also don't forget to configure [application properties](/Week%2006/Lecture%2012/Assignment%2001/lecture_12/src/main/resources/application.properties) with this format. ```java spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/<my_database> @@ -338,11 +340,19 @@ spring.datasource.username=<my_user_name> spring.datasource.password=<my_password> ``` -and don't forget to add this +and don't forget to add this to re-update the SQL DDL queries. ```java spring.jpa.hibernate.ddl-auto=update ``` -to do database seeding using JPA Hibernate. + +finally, don't forget to add this for hibernate SQL logging. +```java +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +``` + ### βš™οΈ How to run the program 1. Go to the `lecture_12` directory by using this command @@ -451,6 +461,10 @@ Find all employees with the first name containing "John", hired after January 1s GET /api/v1/employees/search?page=0&size=10&sortBy=lastName&sortOrder=asc&firstName=John&firstNameOperation=like&hireDate=2020-01-01&hireDateOperation=gt ``` +and here's the query used through hibernate logging: + +![Screenshot](img/hibernate.png) + #### Advanced Query Examples Here are 10 examples of advanced queries you can perform with this implementation. All the method used are `GET`. diff --git a/Week 06/Lecture 12/Assignment 01/img/hibernate.png b/Week 06/Lecture 12/Assignment 01/img/hibernate.png new file mode 100644 index 0000000000000000000000000000000000000000..75081df93692750023a3f553cb6214583b0fceb9 GIT binary patch literal 28446 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 literal 0 HcmV?d00001