Assignment Changes.

Assignment completed so committing changes.
This commit is contained in:
sd7719 2025-06-17 23:31:40 +05:30
parent f9399b7a9f
commit 6562f81750
14 changed files with 402 additions and 1 deletions

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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}";

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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>

View file

@ -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">

View file

@ -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">

View 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>

View 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>

View 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>]&nbsp;</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>