feat: added animal resource

This commit is contained in:
Vladyslav Parashchenko 2025-11-22 22:23:29 +02:00
parent b5a630b199
commit 69f610e69e
3 changed files with 312 additions and 0 deletions

View file

@ -0,0 +1,100 @@
/*
* Copyright 2012-2025 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.model;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.PositiveOrZero;
import org.jspecify.annotations.Nullable;
/**
* Simple JavaBean domain object representing an animal.
*/
@MappedSuperclass
public class Animal extends BaseEntity {
@Column(name = "name")
@NotBlank
private @Nullable String name;
@Column(name = "species")
@NotBlank
private @Nullable String species;
@Column(name = "breed")
@NotBlank
private @Nullable String breed;
@Column(name = "age")
@PositiveOrZero
private @Nullable Integer age;
@Column(name = "color")
@NotBlank
private @Nullable String color;
@Column(name = "description")
private @Nullable String description;
public @Nullable String getName() {
return this.name;
}
public void setName(@Nullable String name) {
this.name = name;
}
public @Nullable String getSpecies() {
return this.species;
}
public void setSpecies(@Nullable String species) {
this.species = species;
}
public @Nullable String getBreed() {
return this.breed;
}
public void setBreed(@Nullable String breed) {
this.breed = breed;
}
public @Nullable Integer getAge() {
return this.age;
}
public void setAge(@Nullable Integer age) {
this.age = age;
}
public @Nullable String getColor() {
return this.color;
}
public void setColor(@Nullable String color) {
this.color = color;
}
public @Nullable String getDescription() {
return this.description;
}
public void setDescription(@Nullable String description) {
this.description = description;
}
}

View file

@ -0,0 +1,149 @@
/*
* Copyright 2012-2025 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.animal;
import java.util.Optional;
import org.springframework.samples.petclinic.model.Animal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import jakarta.validation.Valid;
import org.jspecify.annotations.Nullable;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
/**
* Controller class for managing {@link Animal} entities.
*
* Mirrors the structure of the PetController from Spring PetClinic.
*
* Author list follows PetClinic conventions.
*
* @author Juergen Hoeller
* @author Ken Krebs
* @author Arjen Poutsma
* @author Wick Dynex
*/
@Controller
@RequestMapping("/animals")
class AnimalController {
private static final String VIEWS_ANIMALS_CREATE_OR_UPDATE_FORM = "animals/createOrUpdateAnimalForm";
private final AnimalRepository animals;
public AnimalController(AnimalRepository animals) {
this.animals = animals;
}
@ModelAttribute("animal")
public @Nullable Animal findAnimal(@PathVariable(name = "animalId", required = false) @Nullable Integer id) {
if (id == null) {
return new Animal();
}
Optional<Animal> optional = this.animals.findById(id);
return optional.orElseThrow(() -> new IllegalArgumentException(
"Animal not found with id: " + id + ". Please ensure the ID is correct"));
}
@InitBinder("animal")
public void initAnimalBinder(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
@GetMapping("/new")
public String initCreationForm(ModelMap model) {
model.put("animal", new Animal());
return VIEWS_ANIMALS_CREATE_OR_UPDATE_FORM;
}
@PostMapping("/new")
public String processCreationForm(@Valid Animal animal, BindingResult result,
RedirectAttributes redirectAttributes) {
// Example validation similar to name duplicate logic in PetController
if (StringUtils.hasText(animal.getName())) {
boolean exists = this.animals.findAll().stream()
.anyMatch(a -> animal.getName().equalsIgnoreCase(a.getName()));
if (exists) {
result.rejectValue("name", "duplicate", "already exists");
}
}
if (result.hasErrors()) {
return VIEWS_ANIMALS_CREATE_OR_UPDATE_FORM;
}
this.animals.save(animal);
redirectAttributes.addFlashAttribute("message", "New Animal has been added");
return "redirect:/animals";
}
@GetMapping("/{animalId}/edit")
public String initUpdateForm() {
return VIEWS_ANIMALS_CREATE_OR_UPDATE_FORM;
}
@PostMapping("/{animalId}/edit")
public String processUpdateForm(@Valid Animal animal, BindingResult result,
RedirectAttributes redirectAttributes) {
String name = animal.getName();
if (StringUtils.hasText(name)) {
boolean exists = this.animals.findAll().stream()
.anyMatch(a -> a.getName().equalsIgnoreCase(name)
&& !a.getId().equals(animal.getId()));
if (exists) {
result.rejectValue("name", "duplicate", "already exists");
}
}
if (result.hasErrors()) {
return VIEWS_ANIMALS_CREATE_OR_UPDATE_FORM;
}
this.animals.save(animal);
redirectAttributes.addFlashAttribute("message", "Animal details have been updated");
return "redirect:/animals";
}
@GetMapping
public String listAnimals(ModelMap model) {
model.put("animals", this.animals.findAll());
return "animals/animalList";
}
@GetMapping("/{animalId}")
public String showAnimal(@PathVariable("animalId") int animalId, ModelMap model) {
Animal animal = this.animals.findById(animalId).orElseThrow(
() -> new IllegalArgumentException("Animal not found with id " + animalId));
model.put("animal", animal);
return "animals/animalDetails";
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright 2012-2025 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.animal;
import java.util.Optional;
import jakarta.annotation.Nonnull;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.samples.petclinic.model.Animal;
/**
* Repository class for {@link Animal} domain objects. All method names follow
* Spring Data JPA naming conventions. See:
* https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
*
* Provides operations for querying Animals by their attributes.
*
* Author list follows PetClinic conventions.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
* @author Michael Isvy
* @author Wick Dynex
*/
public interface AnimalRepository extends JpaRepository<Animal, Integer> {
/**
* Retrieve {@link Animal}s from the data store by species, returning all animals
* whose species <i>starts</i> with the given value.
* @param species value to search for
* @return a Page of matching {@link Animal}s (or an empty page if none found)
*/
Page<Animal> findBySpeciesStartingWith(String species, Pageable pageable);
/**
* Retrieve an {@link Animal} from the data store by id.
* <p>
* This method returns an {@link Optional} containing the {@link Animal} if found.
* If no animal is found with the provided id, it returns an empty {@link Optional}.
* </p>
* @param id the id to search for
* @return an {@link Optional} containing the {@link Animal} if found, or empty otherwise
* @throws IllegalArgumentException if the id is null
*/
Optional<Animal> findById(Integer id);
}