mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-16 12:45:48 +00:00
Assignment Changes added for same.
Assignment completed to committed the changes.
This commit is contained in:
parent
1b6436f80e
commit
456a13430e
14 changed files with 402 additions and 1 deletions
|
@ -30,6 +30,7 @@ import jakarta.persistence.FetchType;
|
|||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.OrderBy;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
|
@ -57,6 +58,21 @@ public class Pet extends NamedEntity {
|
|||
@JoinColumn(name = "pet_id")
|
||||
@OrderBy("date ASC")
|
||||
private final Set<Visit> visits = new LinkedHashSet<>();
|
||||
|
||||
@OneToOne(mappedBy ="pet",cascade = CascadeType.ALL)
|
||||
private PetAttribute petAttribute;
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@JoinColumn(name="owner_id")
|
||||
private Owner owner;
|
||||
|
||||
public Owner getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(Owner owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public void setBirthDate(LocalDate birthDate) {
|
||||
this.birthDate = birthDate;
|
||||
|
@ -82,4 +98,13 @@ public class Pet extends NamedEntity {
|
|||
getVisits().add(visit);
|
||||
}
|
||||
|
||||
public PetAttribute getPetAttribute() {
|
||||
return petAttribute;
|
||||
}
|
||||
|
||||
public void setPetAttribute(PetAttribute petAttribute) {
|
||||
this.petAttribute = petAttribute;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package org.springframework.samples.petclinic.owner;
|
||||
|
||||
import org.springframework.samples.petclinic.model.BaseEntity;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table (name="pet_attributes")
|
||||
public class PetAttribute extends BaseEntity{
|
||||
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name="pet_id")
|
||||
private Pet pet;
|
||||
|
||||
//@NotBlank(message="Temperament is required")
|
||||
private String temperament;
|
||||
|
||||
private Double length;
|
||||
|
||||
private Double weight;
|
||||
|
||||
public Pet getPet() {
|
||||
return pet;
|
||||
}
|
||||
|
||||
public void setPet(Pet pet) {
|
||||
this.pet = pet;
|
||||
}
|
||||
|
||||
public String getTemperament() {
|
||||
return temperament;
|
||||
}
|
||||
|
||||
public void setTemperament(String temperament) {
|
||||
this.temperament = temperament;
|
||||
}
|
||||
|
||||
public Double getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(Double length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public Double getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public void setWeight(Double weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -95,12 +95,15 @@ class PetController {
|
|||
@GetMapping("/pets/new")
|
||||
public String initCreationForm(Owner owner, ModelMap model) {
|
||||
Pet pet = new Pet();
|
||||
PetAttribute petAttribute= new PetAttribute();
|
||||
pet.setPetAttribute(petAttribute);
|
||||
pet.getPetAttribute().setPet(pet);
|
||||
owner.addPet(pet);
|
||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
@PostMapping("/pets/new")
|
||||
public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result,
|
||||
public String processCreationForm(Owner owner, @Valid Pet pet,BindingResult result,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
|
||||
if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null)
|
||||
|
@ -116,6 +119,7 @@ class PetController {
|
|||
}
|
||||
|
||||
owner.addPet(pet);
|
||||
pet.getPetAttribute().setPet(pet);
|
||||
this.owners.save(owner);
|
||||
redirectAttributes.addFlashAttribute("message", "New Pet has been Added");
|
||||
return "redirect:/owners/{ownerId}";
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package org.springframework.samples.petclinic.owner;
|
||||
|
||||
import java.util.List;
|
||||
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.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@Controller
|
||||
public class PetViewController {
|
||||
|
||||
private final PetViewRepository petViewRepository;
|
||||
|
||||
public PetViewController(PetViewRepository petViewRepository) {
|
||||
this.petViewRepository = petViewRepository;
|
||||
}
|
||||
|
||||
@ModelAttribute("pet")
|
||||
public Pet findPet(@PathVariable(name = "petId", required = false) Integer petId) {
|
||||
return petId == null ? new Pet()
|
||||
: this.petViewRepository.findById(petId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Pet not found with id: " + petId
|
||||
+ ". Please ensure the ID is correct " + "and the pet exists in the database."));
|
||||
}
|
||||
|
||||
@GetMapping("/pets/find")
|
||||
public String initFindForm() {
|
||||
return "pets/findPets";
|
||||
}
|
||||
|
||||
@GetMapping("/pets")
|
||||
public String processFindForm(@RequestParam(defaultValue = "1") int page, Pet pet, BindingResult result,
|
||||
Model model) {
|
||||
// allow parameterless GET request for /pets to return all records
|
||||
if (pet.getName() == null) {
|
||||
pet.setName(""); // empty string signifies broadest possible search
|
||||
}
|
||||
|
||||
// find owners by last name
|
||||
Page<Pet> petsResults = findPaginatedForPetsName(page, pet.getName());
|
||||
if (petsResults.isEmpty()) {
|
||||
// no owners found
|
||||
result.rejectValue("Name", "notFound", "not found");
|
||||
return "pets/findPets";
|
||||
}
|
||||
|
||||
if (petsResults.getTotalElements() == 1) {
|
||||
// 1 owner found
|
||||
pet = petsResults.iterator().next();
|
||||
return "redirect:/pets/" + pet.getId();
|
||||
}
|
||||
|
||||
// multiple owners found
|
||||
return addPaginationModel(page, model, petsResults);
|
||||
}
|
||||
|
||||
private String addPaginationModel(int page, Model model, Page<Pet> paginated) {
|
||||
List<Pet> listPets = paginated.getContent();
|
||||
model.addAttribute("currentPage", page);
|
||||
model.addAttribute("totalPages", paginated.getTotalPages());
|
||||
model.addAttribute("totalItems", paginated.getTotalElements());
|
||||
model.addAttribute("listPets", listPets);
|
||||
return "pets/petsList";
|
||||
}
|
||||
private Page<Pet> findPaginatedForPetsName(int page, String name) {
|
||||
int pageSize = 5;
|
||||
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
||||
return petViewRepository.findByNameStartingWith(name, pageable);
|
||||
}
|
||||
|
||||
@GetMapping("/pets/{petId}")
|
||||
public ModelAndView showPet(@PathVariable("petId") int petId) {
|
||||
ModelAndView mav = new ModelAndView("pets/petDetails");
|
||||
Optional<Pet> optionalPet = this.petViewRepository.findPetByWithOwnerAndAttribute(petId);
|
||||
Pet pet = optionalPet.orElseThrow(() -> new IllegalArgumentException(
|
||||
"Pet not found with id: " + petId + ". Please ensure the ID is correct "));
|
||||
|
||||
mav.addObject(pet);
|
||||
mav.addObject(pet.getOwner());
|
||||
//mav.addObject(pet.getPetAttribute());
|
||||
return mav;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package org.springframework.samples.petclinic.owner;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
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.query.Param;
|
||||
|
||||
public interface PetViewRepository extends JpaRepository<Pet, Integer>{
|
||||
|
||||
Page<Pet> findByNameStartingWith(String name, Pageable pageable);
|
||||
|
||||
|
||||
@Query("SELECT p from Pet p LEFT JOIN FETCH p.owner LEFT JOIN FETCH p.petAttribute WHERE p.id= :id")
|
||||
Optional<Pet> findPetByWithOwnerAndAttribute(@Param("id") int id);
|
||||
}
|
|
@ -51,3 +51,8 @@ INSERT INTO visits VALUES (default, 7, '2013-01-01', 'rabies shot');
|
|||
INSERT INTO visits VALUES (default, 8, '2013-01-02', 'rabies shot');
|
||||
INSERT INTO visits VALUES (default, 8, '2013-01-03', 'neutered');
|
||||
INSERT INTO visits VALUES (default, 7, '2013-01-04', 'spayed');
|
||||
|
||||
INSERT INTO pet_attributes VALUES (default,2,'Friendly',35.5,12.0);
|
||||
INSERT INTO pet_attributes VALUES (default,9,'social',25.5,10.0);
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ DROP TABLE visits IF EXISTS;
|
|||
DROP TABLE pets IF EXISTS;
|
||||
DROP TABLE types IF EXISTS;
|
||||
DROP TABLE owners IF EXISTS;
|
||||
DROP TABLE pet_attributes IF EXISTS;
|
||||
|
||||
|
||||
CREATE TABLE vets (
|
||||
|
@ -62,3 +63,13 @@ CREATE TABLE visits (
|
|||
);
|
||||
ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id);
|
||||
CREATE INDEX visits_pet_id ON visits (pet_id);
|
||||
|
||||
CREATE TABLE pet_attributes(
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
pet_id INTEGER,
|
||||
temperament VARCHAR(255),
|
||||
length DOUBLE,
|
||||
weight DOUBLE
|
||||
);
|
||||
ALTER TABLE pet_attributes ADD CONSTRAINT fk_pets_attribute FOREIGN KEY (pet_id) REFERENCES pets (id);
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ owners=Owners
|
|||
addOwner=Add Owner
|
||||
findOwner=Find Owner
|
||||
findOwners=Find Owners
|
||||
findPet=Find Pet
|
||||
findPets=Find Pets
|
||||
updateOwner=Update Owner
|
||||
vets=Veterinarians
|
||||
name=Name
|
||||
|
@ -42,7 +44,11 @@ new=New
|
|||
addVisit=Add Visit
|
||||
editPet=Edit Pet
|
||||
ownerInformation=Owner Information
|
||||
petInformation=Pet Information
|
||||
visitDate=Visit Date
|
||||
editOwner=Edit Owner
|
||||
addNewPet=Add New Pet
|
||||
petsAndVisits=Pets and Visits
|
||||
petAttributeWeight=Weight
|
||||
petAttributeLength=Length
|
||||
petAttributeTemp=Temperament
|
||||
|
|
|
@ -48,6 +48,11 @@
|
|||
<span th:text="#{findOwners}">Find owners</span>
|
||||
</li>
|
||||
|
||||
<li th:replace="~{::menuItem ('/pets/find','pets','find pets','search',#{findPets})}">
|
||||
<span class="fa fa-search" aria-hidden="true"></span>
|
||||
<span th:text="#{findPets}">Find pets</span>
|
||||
</li>
|
||||
|
||||
<li th:replace="~{::menuItem ('/vets.html','vets','veterinarians','th-list',#{vets})}">
|
||||
<span class="fa fa-th-list" aria-hidden="true"></span>
|
||||
<span th:text="#{vets}">Veterinarians</span>
|
||||
|
|
|
@ -54,6 +54,12 @@
|
|||
<dd th:text="${#temporals.format(pet.birthDate, 'yyyy-MM-dd')}"></dd>
|
||||
<dt th:text="#{type}">Type</dt>
|
||||
<dd th:text="${pet.type}"></dd>
|
||||
<dt th:text="#{petAttributeTemp}">Temperament</dt>
|
||||
<dd th:text="${pet.petAttribute != null ?pet.petAttribute.temperament: 'N/A'}"></dd>
|
||||
<dt th:text="#{petAttributeLength}">Length</dt>
|
||||
<dd th:text="${pet.petAttribute != null ?pet.petAttribute.length: 'N/A'}"></dd>
|
||||
<dt th:text="#{petAttributeWeight}">Weight</dt>
|
||||
<dd th:text="${pet.petAttribute != null ?pet.petAttribute.weight: 'N/A'}"></dd>
|
||||
</dl>
|
||||
</td>
|
||||
<td valign="top">
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
<input th:replace="~{fragments/inputField :: input ('Name', 'name', 'text')}" />
|
||||
<input th:replace="~{fragments/inputField :: input ('Birth Date', 'birthDate', 'date')}" />
|
||||
<input th:replace="~{fragments/selectField :: select ('Type', 'type', ${types})}" />
|
||||
<input th:replace="~{fragments/inputField :: input ('Temperament', 'petAttribute.temperament', 'text')}" />
|
||||
<input th:replace="~{fragments/inputField :: input ('Length', 'petAttribute.length', 'text')}" />
|
||||
<input th:replace="~{fragments/inputField :: input ('Weigth', 'petAttribute.weight', 'text')}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
|
|
35
src/main/resources/templates/pets/findPets.html
Normal file
35
src/main/resources/templates/pets/findPets.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns:th="https://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'pets')}">
|
||||
|
||||
<body>
|
||||
|
||||
<h2 th:text="#{findPets}">Find Pets</h2>
|
||||
|
||||
<form th:object="${pet}" th:action="@{/pets}" method="get" class="form-horizontal" id="search-pet-form">
|
||||
<div class="form-group">
|
||||
<div class="control-group" id="nameGroup">
|
||||
<label class="col-sm-2 control-label" th:text="#{name}">Name </label>
|
||||
<div class="col-sm-10">
|
||||
<input class="form-control" th:field="*{name}" size="30" maxlength="80" />
|
||||
<span class="help-inline">
|
||||
<div th:if="${#fields.hasAnyErrors()}">
|
||||
<p th:each="err : ${#fields.allErrors()}" th:text="${err}">Error</p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-primary" th:text="#{findPet}">Find Pet</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
68
src/main/resources/templates/pets/petDetails.html
Normal file
68
src/main/resources/templates/pets/petDetails.html
Normal file
|
@ -0,0 +1,68 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns:th="https://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'pets')}">
|
||||
|
||||
<body>
|
||||
|
||||
<h2 th:text="#{petInformation}">Pet Information</h2>
|
||||
|
||||
<div th:if="${message}" class="alert alert-success" id="success-message">
|
||||
<span th:text="${message}"></span>
|
||||
</div>
|
||||
|
||||
<div th:if="${error}" class="alert alert-danger" id="error-message">
|
||||
<span th:text="${error}"></span>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped" th:object="${pet}">
|
||||
<tr>
|
||||
<th th:text="#{name}">Name</th>
|
||||
<td><b th:text="*{name}"></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th th:text="#{birthDate}">Birth Date</th>
|
||||
<td th:text="*{birthDate}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th th:text="#{type}">Type</th>
|
||||
<td th:text="*{type}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th th:text="#{petAttributeTemp}">Temperament</th>
|
||||
<td th:text="*{petAttribute != null ?petAttribute.temperament: 'N/A'}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th th:text="#{petAttributeLength}">Length</th>
|
||||
<td th:text="*{petAttribute != null ?petAttribute.length: 'N/A'}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th th:text="#{petAttributeWeight}">Weight</th>
|
||||
<td th:text="*{petAttribute != null ?petAttribute.weight: 'N/A'}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th th:text="#{owner}">Owner</th>
|
||||
<td th:text="*{owner.firstName !=null && owner.lastName !=null ? owner.firstName + ' ' + owner.lastName : 'N/A'}"></td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<script>
|
||||
// Function to hide the success and error messages after 3 seconds
|
||||
function hideMessages() {
|
||||
setTimeout(function () {
|
||||
document.getElementById("success-message").style.display = "none";
|
||||
document.getElementById("error-message").style.display = "none";
|
||||
}, 3000); // 3000 milliseconds (3 seconds)
|
||||
}
|
||||
|
||||
// Call the function to hide messages
|
||||
hideMessages();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
63
src/main/resources/templates/pets/petsList.html
Normal file
63
src/main/resources/templates/pets/petsList.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns:th="https://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'pets')}">
|
||||
|
||||
<body>
|
||||
|
||||
<h2 th:text="#{pets}">Pets</h2>
|
||||
|
||||
<table id="pets" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th th:text="#{name}" style="width: 150px;">Name</th>
|
||||
<th th:text="#{birthDate}" style="width: 200px;">Birth Date</th>
|
||||
<th th:text="#{type}">Type</th>
|
||||
<th th:text="#{petAttributeTemp}">Temperament</th>
|
||||
<th th:text="#{petAttributeLength}">Length</th>
|
||||
<th th:text="#{petAttributeWeight}">Weight</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="pet : ${listPets}">
|
||||
<td>
|
||||
<a th:href="@{/pets/__${pet.id}__}" th:text="${pet.name}" /></a>
|
||||
</td>
|
||||
<td th:text="${pet.birthDate}" />
|
||||
<td th:text="${pet.type}" />
|
||||
<td th:text="${pet.petAttribute != null ?pet.petAttribute.temperament: 'N/A'}" />
|
||||
<td th:text="${pet.petAttribute != null ?pet.petAttribute.length: 'N/A'}" />
|
||||
<td th:text="${pet.petAttribute != null ?pet.petAttribute.weight: 'N/A'}" />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div th:if="${totalPages > 1}">
|
||||
<span th:text="#{pages}">Pages:</span>
|
||||
<span>[</span>
|
||||
<span th:each="i: ${#numbers.sequence(1, totalPages)}">
|
||||
<a th:if="${currentPage != i}" th:href="@{'/pets?page=' + ${i}}">[[${i}]]</a>
|
||||
<span th:unless="${currentPage != i}">[[${i}]]</span>
|
||||
</span>
|
||||
<span>] </span>
|
||||
<span>
|
||||
<a th:if="${currentPage > 1}" th:href="@{'/pets?page=1'}" th:title="#{first}" class="fa fa-fast-backward"></a>
|
||||
<span th:unless="${currentPage > 1}" th:title="#{first}" class="fa fa-fast-backward"></span>
|
||||
</span>
|
||||
<span>
|
||||
<a th:if="${currentPage > 1}" th:href="@{'/pets?page=__${currentPage - 1}__'}" th:title="#{previous}"
|
||||
class="fa fa-step-backward"></a>
|
||||
<span th:unless="${currentPage > 1}" th:title="#{previous}" class="fa fa-step-backward"></span>
|
||||
</span>
|
||||
<span>
|
||||
<a th:if="${currentPage < totalPages}" th:href="@{'/pets?page=__${currentPage + 1}__'}" th:title="#{next}"
|
||||
class="fa fa-step-forward"></a>
|
||||
<span th:unless="${currentPage < totalPages}" th:title="#{next}" class="fa fa-step-forward"></span>
|
||||
</span>
|
||||
<span>
|
||||
<a th:if="${currentPage < totalPages}" th:href="@{'/pets?page=__${totalPages}__'}" th:title="#{last}"
|
||||
class="fa fa-fast-forward"></a>
|
||||
<span th:unless="${currentPage < totalPages}" th:title="#{last}" class="fa fa-fast-forward"></span>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in a new issue