diff --git a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java index 44c85a62a..0e91b44b4 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -74,6 +74,7 @@ class OwnerController { @PostMapping("/owners/new") public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) { + System.out.println("owner/new:"+owner); if (result.hasErrors()) { redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner."); return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; diff --git a/src/main/java/org/springframework/samples/petclinic/vet/SpecialtyFormatter.java b/src/main/java/org/springframework/samples/petclinic/vet/SpecialtyFormatter.java new file mode 100644 index 000000000..333b2ff04 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/vet/SpecialtyFormatter.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vet; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.Formatter; +import org.springframework.samples.petclinic.owner.OwnerRepository; +import org.springframework.samples.petclinic.owner.PetType; +import org.springframework.stereotype.Component; + +import java.text.ParseException; +import java.util.Collection; +import java.util.Locale; + +/** + * Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting + * from Spring 3.0, Formatters have come as an improvement in comparison to legacy + * PropertyEditors. See the following links for more details: - The Spring ref doc: + * https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#format + * + * @author Mark Fisher + * @author Juergen Hoeller + * @author Michael Isvy + */ +@Component +public class SpecialtyFormatter implements Formatter { + + + VetRepository vetRepository; + + @Autowired + public SpecialtyFormatter(VetRepository vetRepository) { + this.vetRepository = vetRepository; + } + + + public String print(Specialty specialty) { + return specialty.getName(); + } + + @Override + public Specialty parse(String text, Locale locale) throws ParseException { + Collection specialties = this.vetRepository.findSpecialties(); + for (Specialty specialty : specialties) { + if (specialty.getName().equals(text)) { + return specialty; + } + } + throw new ParseException("specialty not found: " + text, 0); + } + + @Override + public String print(Specialty object, Locale locale) { + return object.toString(); + } +} diff --git a/src/main/java/org/springframework/samples/petclinic/vet/SpecialtyRepository.java b/src/main/java/org/springframework/samples/petclinic/vet/SpecialtyRepository.java new file mode 100644 index 000000000..7b851070d --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/vet/SpecialtyRepository.java @@ -0,0 +1,17 @@ +package org.springframework.samples.petclinic.vet; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.samples.petclinic.vet.Specialty; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Repository +public interface SpecialtyRepository extends JpaRepository { + + @Query("SELECT s FROM Specialty s ORDER BY s.name") + @Transactional(readOnly = true) + List findSpecialties(); +} diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java index d8a1fc84d..0da411631 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java @@ -15,11 +15,7 @@ */ package org.springframework.samples.petclinic.vet; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import org.springframework.beans.support.MutableSortDefinition; import org.springframework.beans.support.PropertyComparator; @@ -45,12 +41,14 @@ import jakarta.xml.bind.annotation.XmlElement; @Table(name = "vets") public class Vet extends Person { + + @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"), inverseJoinColumns = @JoinColumn(name = "specialty_id")) - private Set specialties; + private Collection specialties; - protected Set getSpecialtiesInternal() { + protected Collection getSpecialtiesInternal() { if (this.specialties == null) { this.specialties = new HashSet<>(); } @@ -63,6 +61,9 @@ public class Vet extends Person { PropertyComparator.sort(sortedSpecs, new MutableSortDefinition("name", true, true)); return Collections.unmodifiableList(sortedSpecs); } + public void setSpecialties(Collection specialties) { + this.specialties = specialties; + } public int getNrOfSpecialties() { return getSpecialtiesInternal().size(); diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java index 3240814a6..64b5bff9c 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java @@ -15,16 +15,22 @@ */ package org.springframework.samples.petclinic.vet; +import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.samples.petclinic.model.NamedEntity; +import org.springframework.samples.petclinic.owner.Owner; +import org.springframework.samples.petclinic.owner.PetType; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; /** * @author Juergen Hoeller @@ -36,9 +42,16 @@ import org.springframework.web.bind.annotation.ResponseBody; class VetController { private final VetRepository vetRepository; + private final SpecialtyRepository specialtyRepository; - public VetController(VetRepository clinicService) { + @Autowired + public VetController(VetRepository clinicService, SpecialtyRepository specialtyRepository) { this.vetRepository = clinicService; + this.specialtyRepository = specialtyRepository; + } + @ModelAttribute("specialties") + public Collection populateSpecialties() { + return this.specialtyRepository.findSpecialties(); } @GetMapping("/vets.html") @@ -50,7 +63,32 @@ class VetController { vets.getVetList().addAll(paginated.toList()); return addPaginationModel(page, paginated, model); } + @GetMapping("/vets/find") + public String initFindForm() { + return "vets/findVets"; + } + @ModelAttribute("vet") + public Vet findVet(@PathVariable(name = "vetId", required = false) Integer vetId) { + return vetId == null ? new Vet() + : this.vetRepository.findById(vetId) + .orElseThrow(() -> new IllegalArgumentException("Vet not found with id: " + vetId + + ". Please ensure the ID is correct " + "and the owner exists in the database.")); + } + static final String VIEWS_VETS_CREATE_OR_UPDATE_FORM = "vets/createOrUpdateVetForm"; + @GetMapping("/vets/new") + public String initCreationForm() { + System.out.println("Creating a new vet"); + return VIEWS_VETS_CREATE_OR_UPDATE_FORM; + } + @PostMapping("/vets/new") + public String processCreationForm(@Valid Vet vet, BindingResult result, Model model) { + if (result.hasErrors()) { + return VIEWS_VETS_CREATE_OR_UPDATE_FORM; + } + this.vetRepository.save(vet); + return "redirect:/vets.html"; + } private String addPaginationModel(int page, Page paginated, Model model) { List listVets = paginated.getContent(); model.addAttribute("currentPage", page); @@ -59,6 +97,37 @@ class VetController { model.addAttribute("listVets", listVets); return "vets/vetList"; } + @GetMapping("/vets") + public String processFindForm(@RequestParam(defaultValue = "1") int page, Vet vet, BindingResult result, + Model model) { + // allow parameterless GET request for /owners to return all records + System.out.println("Find vets:"+vet); + if (vet.getLastName() == null) { + vet.setLastName(""); // empty string signifies broadest possible search + } + + // find owners by last name + Page vetsResults = findPaginatedForVetsLastName(page, vet.getLastName()); + if (vetsResults.isEmpty()) { + // no owners found + result.rejectValue("lastName", "notFound", "not found"); + return "vets/findVets"; + } + + /*if (vetsResults.getTotalElements() == 1) { + // 1 owner found + vet = vetsResults.iterator().next(); + return "redirect:/vets/" + vet.getId(); + }*/ + + // multiple owners found + return addPaginationModel(page, vetsResults, model); + } + private Page findPaginatedForVetsLastName(int page, String lastname) { + int pageSize = 5; + Pageable pageable = PageRequest.of(page - 1, pageSize); + return vetRepository.findByLastNameStartingWith(lastname, pageable); + } private Page findPaginated(int page) { int pageSize = 5; @@ -66,13 +135,13 @@ class VetController { return vetRepository.findAll(pageable); } - @GetMapping({ "/vets" }) - public @ResponseBody Vets showResourcesVetList() { - // Here we are returning an object of type 'Vets' rather than a collection of Vet - // objects so it is simpler for JSon/Object mapping - Vets vets = new Vets(); - vets.getVetList().addAll(this.vetRepository.findAll()); - return vets; - } +// @GetMapping({ "/vets" }) +// public @ResponseBody Vets showResourcesVetList() { +// // Here we are returning an object of type 'Vets' rather than a collection of Vet +// // objects so it is simpler for JSon/Object mapping +// Vets vets = new Vets(); +// vets.getVetList().addAll(this.vetRepository.findAll()); +// return vets; +// } } diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java b/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java index 8b9e0823c..107bfe3e7 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java @@ -15,14 +15,23 @@ */ package org.springframework.samples.petclinic.vet; +import jakarta.annotation.Nonnull; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.validation.Valid; import org.springframework.cache.annotation.Cacheable; import org.springframework.dao.DataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; +import org.springframework.samples.petclinic.owner.Owner; +import org.springframework.samples.petclinic.owner.PetType; import org.springframework.transaction.annotation.Transactional; -import java.util.Collection; +import java.util.*; /** * Repository class for Vet domain objects All method names are compliant @@ -35,24 +44,21 @@ import java.util.Collection; * @author Sam Brannen * @author Michael Isvy */ -public interface VetRepository extends Repository { +public interface VetRepository extends JpaRepository { /** - * Retrieve all Vets from the data store. - * @return a Collection of Vets + * Retrieve {@link Vet}s from the data store by last name, returning all owners + * whose last name starts with the given name. + * @param lastName Value to search for + * @return a Collection of matching {@link Vet}s (or an empty Collection if none + * found) */ - @Transactional(readOnly = true) - @Cacheable("vets") - Collection findAll() throws DataAccessException; + Page findByLastNameStartingWith(String lastName, Pageable pageable); - /** - * Retrieve all Vets from data store in Pages - * @param pageable - * @return - * @throws DataAccessException - */ + + + @Query("SELECT s FROM Specialty s ORDER BY s.name") @Transactional(readOnly = true) - @Cacheable("vets") - Page findAll(Pageable pageable) throws DataAccessException; + List findSpecialties(); } diff --git a/src/main/resources/db/h2/data.sql b/src/main/resources/db/h2/data.sql index f232b1361..4a3b53305 100644 --- a/src/main/resources/db/h2/data.sql +++ b/src/main/resources/db/h2/data.sql @@ -21,6 +21,8 @@ INSERT INTO types VALUES (default, 'lizard'); INSERT INTO types VALUES (default, 'snake'); INSERT INTO types VALUES (default, 'bird'); INSERT INTO types VALUES (default, 'hamster'); +INSERT INTO types VALUES (default, 'panda'); + INSERT INTO owners VALUES (default, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023'); INSERT INTO owners VALUES (default, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749'); diff --git a/src/main/resources/templates/fragments/layout.html b/src/main/resources/templates/fragments/layout.html index 3e9bc398b..a1b0adbd7 100644 --- a/src/main/resources/templates/fragments/layout.html +++ b/src/main/resources/templates/fragments/layout.html @@ -59,6 +59,10 @@ Veterinarians +
  • + + Find Veterinarians +
  • diff --git a/src/main/resources/templates/vets/createOrUpdateVetForm.html b/src/main/resources/templates/vets/createOrUpdateVetForm.html new file mode 100644 index 000000000..ade5d4e7f --- /dev/null +++ b/src/main/resources/templates/vets/createOrUpdateVetForm.html @@ -0,0 +1,25 @@ + + + + +

    Vet

    +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    + + diff --git a/src/main/resources/templates/vets/findVets.html b/src/main/resources/templates/vets/findVets.html new file mode 100644 index 000000000..1b53ff25a --- /dev/null +++ b/src/main/resources/templates/vets/findVets.html @@ -0,0 +1,34 @@ + + + + +

    Find Vets

    + +
    +
    +
    + +
    +
    +

    Error

    +
    +
    +
    +
    +
    +
    + +
    +
    + + Add Vet + +
    + + +