From 085a76b261f07294fcebc8830aa390960e9a828a Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Mar 2026 15:11:59 +0500 Subject: [PATCH 1/3] Add bulk delete functionality for contacts - Implement `deleteMany` method in `ContactService` to handle bulk deletion logic. - Add `@DeleteMapping` endpoint in `ContactController` to support bulk contact removal via slugs. - Extend `ContactRepository` with `findAllBySlugIn` query for fetching multiple contacts. --- .../codes/yam/contacts/ContactController.java | 20 +++++++++++-------- .../codes/yam/contacts/ContactRepository.java | 7 +++++-- .../codes/yam/contacts/ContactService.java | 9 ++++++++- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/main/java/codes/yam/contacts/ContactController.java b/src/main/java/codes/yam/contacts/ContactController.java index 7349628..93252a3 100644 --- a/src/main/java/codes/yam/contacts/ContactController.java +++ b/src/main/java/codes/yam/contacts/ContactController.java @@ -1,18 +1,19 @@ package codes.yam.contacts; import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; import java.net.URI; +import java.util.List; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; @@ -56,9 +57,7 @@ public String editContact(@PathVariable String slug, Model model) { @PostMapping("/contacts/{slug}/edit") public String updateContact( - @PathVariable String slug, - @Valid @ModelAttribute Contact contact, - BindingResult result) { + @PathVariable String slug, @Valid @ModelAttribute Contact contact, BindingResult result) { if (result.hasErrors()) { return "contacts/edit"; } @@ -67,9 +66,7 @@ public String updateContact( } @PostMapping("/contacts") - public String createContact( - @Valid @ModelAttribute Contact contact, - BindingResult result) { + public String createContact(@Valid @ModelAttribute Contact contact, BindingResult result) { if (result.hasErrors()) { return "contacts/new"; } @@ -84,6 +81,13 @@ public ResponseEntity deleteContact(@PathVariable String slug) { return ResponseEntity.status(HttpStatus.SEE_OTHER).location(URI.create("/contacts")).build(); } + @DeleteMapping("/contacts") + public ResponseEntity deleteManyContacts(@RequestParam List slugs) { + contactService.deleteMany(slugs); + // Not sure about this return... + return ResponseEntity.status(HttpStatus.SEE_OTHER).location(URI.create("/contacts")).build(); + } + @ExceptionHandler(ContactNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public String handleNotFound(ContactNotFoundException ex, Model model) { diff --git a/src/main/java/codes/yam/contacts/ContactRepository.java b/src/main/java/codes/yam/contacts/ContactRepository.java index 5888a27..8b5f7a5 100644 --- a/src/main/java/codes/yam/contacts/ContactRepository.java +++ b/src/main/java/codes/yam/contacts/ContactRepository.java @@ -7,6 +7,9 @@ public interface ContactRepository extends JpaRepository { Contact findBySlug(String slug); - List findByFirstContainingIgnoreCaseOrLastContainingIgnoreCaseOrEmailContainingIgnoreCase( - String first, String last, String email); + List findAllBySlugIn(List slugs); + + List + findByFirstContainingIgnoreCaseOrLastContainingIgnoreCaseOrEmailContainingIgnoreCase( + String first, String last, String email); } diff --git a/src/main/java/codes/yam/contacts/ContactService.java b/src/main/java/codes/yam/contacts/ContactService.java index 269b010..6dd73a1 100644 --- a/src/main/java/codes/yam/contacts/ContactService.java +++ b/src/main/java/codes/yam/contacts/ContactService.java @@ -14,7 +14,9 @@ public List findAll() { } public List search(String q) { - return contactRepository.findByFirstContainingIgnoreCaseOrLastContainingIgnoreCaseOrEmailContainingIgnoreCase(q, q, q); + return contactRepository + .findByFirstContainingIgnoreCaseOrLastContainingIgnoreCaseOrEmailContainingIgnoreCase( + q, q, q); } public Contact findBySlug(String slug) { @@ -34,6 +36,11 @@ public void delete(String slug) { contactRepository.delete(contact); } + public void deleteMany(List slugs) { + var contacts = contactRepository.findAllBySlugIn(slugs); + contactRepository.deleteAll(contacts); + } + public Contact update(String slug, Contact updated) { var contact = contactRepository.findBySlug(slug); if (contact == null) throw new ContactNotFoundException(); From 8ea8233f5e72ff653021e754ec3ec3b40e6825c6 Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Mar 2026 15:13:30 +0500 Subject: [PATCH 2/3] Update `findAllBySlugIn` to accept `Collection` instead of `List` in `ContactRepository` --- src/main/java/codes/yam/contacts/ContactRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/codes/yam/contacts/ContactRepository.java b/src/main/java/codes/yam/contacts/ContactRepository.java index 8b5f7a5..d2ab084 100644 --- a/src/main/java/codes/yam/contacts/ContactRepository.java +++ b/src/main/java/codes/yam/contacts/ContactRepository.java @@ -1,5 +1,6 @@ package codes.yam.contacts; +import java.util.Collection; import java.util.List; import java.util.UUID; import org.springframework.data.jpa.repository.JpaRepository; @@ -7,7 +8,7 @@ public interface ContactRepository extends JpaRepository { Contact findBySlug(String slug); - List findAllBySlugIn(List slugs); + List findAllBySlugIn(Collection slugs); List findByFirstContainingIgnoreCaseOrLastContainingIgnoreCaseOrEmailContainingIgnoreCase( From 944beff6c0a2b6c1c8a72d70c3a133057b89b89c Mon Sep 17 00:00:00 2001 From: Yam Borodetsky Date: Sat, 14 Mar 2026 15:22:48 +0500 Subject: [PATCH 3/3] Refactor contact search to use JPQL query in `ContactRepository` for improved readability and flexibility --- .../java/codes/yam/contacts/ContactRepository.java | 10 +++++++--- .../java/codes/yam/contacts/ContactService.java | 14 +------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/main/java/codes/yam/contacts/ContactRepository.java b/src/main/java/codes/yam/contacts/ContactRepository.java index ea7e32e..89809da 100644 --- a/src/main/java/codes/yam/contacts/ContactRepository.java +++ b/src/main/java/codes/yam/contacts/ContactRepository.java @@ -5,6 +5,8 @@ import java.util.Optional; import java.util.UUID; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface ContactRepository extends JpaRepository { Optional findBySlug(String slug); @@ -13,7 +15,9 @@ public interface ContactRepository extends JpaRepository { List findAllBySlugIn(Collection slugs); - List - findByFirstContainingIgnoreCaseOrLastContainingIgnoreCaseOrEmailContainingIgnoreCase( - String first, String last, String email); + @Query("SELECT c FROM Contact c WHERE " + + "LOWER(c.first) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(c.last) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(c.email) LIKE LOWER(CONCAT('%', :q, '%'))") + List search(@Param("q") String q); } diff --git a/src/main/java/codes/yam/contacts/ContactService.java b/src/main/java/codes/yam/contacts/ContactService.java index fce5e4b..6809674 100644 --- a/src/main/java/codes/yam/contacts/ContactService.java +++ b/src/main/java/codes/yam/contacts/ContactService.java @@ -14,9 +14,7 @@ public List findAll() { } public List search(String q) { - return contactRepository - .findByFirstContainingIgnoreCaseOrLastContainingIgnoreCaseOrEmailContainingIgnoreCase( - q, q, q); + return contactRepository.search(q); } public Contact findBySlug(String slug) { @@ -53,16 +51,6 @@ private String generateSlug(Contact contact) { return contact.getFirst().toLowerCase() + "-" + contact.getLast().toLowerCase(); } - /** - * Check if an email exists. - * - * @param email - the email to check - * @return true or false - */ - public boolean isEmailTaken(String email) { - return contactRepository.findByEmail(email).isPresent(); - } - /** * Check if an email exists (for other slugs than the supplied slug). *