mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-19 22:15:50 +00:00
Merge 4e5db56cd6
into 30aab0ae76
This commit is contained in:
commit
ce4ed8d20b
17 changed files with 286 additions and 15 deletions
13
pom.xml
13
pom.xml
|
@ -145,6 +145,19 @@
|
||||||
<groupId>jakarta.xml.bind</groupId>
|
<groupId>jakarta.xml.bind</groupId>
|
||||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- <dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-database-postgresql</artifactId>
|
||||||
|
</dependency>-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.springframework.samples.petclinic.api.controller;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.samples.petclinic.api.service.OwnerService;
|
||||||
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController("owner-controller")
|
||||||
|
@RestControllerAdvice
|
||||||
|
@Validated
|
||||||
|
@RequestMapping("/api/owner")
|
||||||
|
public class OwnerHistoryController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OwnerService ownerService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public void addOwner(@Valid @RequestBody Owner owner) {
|
||||||
|
ownerService.addOwner(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public String getOwner() {
|
||||||
|
return "API working";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.springframework.samples.petclinic.api.service;
|
||||||
|
|
||||||
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class OwnerService {
|
||||||
|
|
||||||
|
private OwnerRepository ownerRepository;
|
||||||
|
|
||||||
|
public void addOwner(Owner owner) {
|
||||||
|
ownerRepository.save(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.springframework.samples.petclinic.exception;
|
||||||
|
|
||||||
|
public class NotFoundException extends RuntimeException{
|
||||||
|
|
||||||
|
public NotFoundException(String message){
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ package org.springframework.samples.petclinic.owner;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
import org.springframework.core.style.ToStringCreator;
|
import org.springframework.core.style.ToStringCreator;
|
||||||
import org.springframework.samples.petclinic.model.Person;
|
import org.springframework.samples.petclinic.model.Person;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -60,6 +61,8 @@ public class Owner extends Person {
|
||||||
@Pattern(regexp = "\\d{10}", message = "{telephone.invalid}")
|
@Pattern(regexp = "\\d{10}", message = "{telephone.invalid}")
|
||||||
private String telephone;
|
private String telephone;
|
||||||
|
|
||||||
|
private Boolean active;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "owner_id")
|
@JoinColumn(name = "owner_id")
|
||||||
@OrderBy("name")
|
@OrderBy("name")
|
||||||
|
@ -93,6 +96,14 @@ public class Owner extends Person {
|
||||||
return this.pets;
|
return this.pets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(Boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
}
|
||||||
|
|
||||||
public void addPet(Pet pet) {
|
public void addPet(Pet pet) {
|
||||||
if (pet.isNew()) {
|
if (pet.isNew()) {
|
||||||
getPets().add(pet);
|
getPets().add(pet);
|
||||||
|
|
|
@ -18,24 +18,23 @@ package org.springframework.samples.petclinic.owner;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.samples.petclinic.exception.NotFoundException;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
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.RequestParam;
|
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
|
@ -43,7 +42,9 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
* @author Wick Dynex
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Controller
|
@RestController
|
||||||
|
@Slf4j
|
||||||
|
@RequestMapping
|
||||||
class OwnerController {
|
class OwnerController {
|
||||||
|
|
||||||
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
|
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
|
||||||
|
@ -170,4 +171,17 @@ class OwnerController {
|
||||||
return mav;
|
return mav;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("owners/{ownerId}")
|
||||||
|
public void deleteOwner(@PathVariable("ownerId") int id,
|
||||||
|
@RequestParam(name = "deletePets", required = false, defaultValue = "false") boolean deletePets){
|
||||||
|
var owner = this.owners.findById(id)
|
||||||
|
.orElseThrow(() -> new NotFoundException(format("owner not found with id %s", id)));
|
||||||
|
owner.setActive(Boolean.FALSE);
|
||||||
|
if(deletePets){
|
||||||
|
owner.getPets()
|
||||||
|
.forEach(pet -> pet.setActive(Boolean.FALSE));
|
||||||
|
}
|
||||||
|
owners.save(owner);
|
||||||
|
log.info("Owner {} marked inactive{}", id, deletePets ? " along with pets" : "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,4 +62,6 @@ public interface OwnerRepository extends JpaRepository<Owner, Integer> {
|
||||||
*/
|
*/
|
||||||
Optional<Owner> findById(@Nonnull Integer id);
|
Optional<Owner> findById(@Nonnull Integer id);
|
||||||
|
|
||||||
|
Optional<Owner> findByIdAndActive(@Nonnull Integer id, Boolean active);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.OrderBy;
|
import jakarta.persistence.OrderBy;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
import org.springframework.samples.petclinic.vaccine.Vaccination;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple business object representing a pet.
|
* Simple business object representing a pet.
|
||||||
|
@ -53,11 +55,16 @@ public class Pet extends NamedEntity {
|
||||||
@JoinColumn(name = "type_id")
|
@JoinColumn(name = "type_id")
|
||||||
private PetType type;
|
private PetType type;
|
||||||
|
|
||||||
|
private Boolean active;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "pet_id")
|
@JoinColumn(name = "pet_id")
|
||||||
@OrderBy("date ASC")
|
@OrderBy("date ASC")
|
||||||
private final Set<Visit> visits = new LinkedHashSet<>();
|
private final Set<Visit> visits = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "pet", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
|
private final Set<Vaccination> vaccinations = new LinkedHashSet<>();
|
||||||
|
|
||||||
public void setBirthDate(LocalDate birthDate) {
|
public void setBirthDate(LocalDate birthDate) {
|
||||||
this.birthDate = birthDate;
|
this.birthDate = birthDate;
|
||||||
}
|
}
|
||||||
|
@ -82,4 +89,16 @@ public class Pet extends NamedEntity {
|
||||||
getVisits().add(visit);
|
getVisits().add(visit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(Boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addVaccination(Vaccination vaccination) {
|
||||||
|
vaccination.setPet(this);
|
||||||
|
this.vaccinations.add(vaccination);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.springframework.samples.petclinic.vaccine;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.samples.petclinic.owner.Pet;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
public class Vaccination {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
private String vaccineName;
|
||||||
|
private LocalDate vaccinationDate;
|
||||||
|
private String description;
|
||||||
|
private Boolean injected;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "pet_id", nullable = false)
|
||||||
|
private Pet pet;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.springframework.samples.petclinic.vaccine;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||||
|
import org.springframework.samples.petclinic.owner.PetTypeRepository;
|
||||||
|
import org.springframework.samples.petclinic.vaccine.request.VaccinationRequest;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/vaccine")
|
||||||
|
public class VaccinationController {
|
||||||
|
|
||||||
|
private VaccinationService vaccinationService;
|
||||||
|
|
||||||
|
@PostMapping("/owner/{owner_id}/pet/{petId}")
|
||||||
|
public void addVaccine(@PathVariable Integer owner_id, @PathVariable Long petId,
|
||||||
|
@RequestBody VaccinationRequest request){
|
||||||
|
|
||||||
|
vaccinationService.addVaccineToPet(owner_id, petId, request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.springframework.samples.petclinic.vaccine;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface VaccinationRepository extends JpaRepository<Vaccination, Long> {
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.springframework.samples.petclinic.vaccine;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.samples.petclinic.exception.NotFoundException;
|
||||||
|
import org.springframework.samples.petclinic.owner.OwnerRepository;
|
||||||
|
import org.springframework.samples.petclinic.owner.Pet;
|
||||||
|
import org.springframework.samples.petclinic.vaccine.predicate.PetPredicates;
|
||||||
|
import org.springframework.samples.petclinic.vaccine.request.VaccinationRequest;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class VaccinationService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private VaccinationRepository vaccinationRepository;
|
||||||
|
private OwnerRepository ownerRepository;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void addVaccineToPet(Integer ownerId, Long petId, VaccinationRequest request) {
|
||||||
|
var owner = ownerRepository.findByIdAndActive(ownerId, Boolean.TRUE)
|
||||||
|
.orElseThrow(()-> new NotFoundException(format("owner not found with id %s", ownerId)));
|
||||||
|
|
||||||
|
var pet = owner.getPets().stream()
|
||||||
|
.filter(PetPredicates.isActive().and(PetPredicates.hasId(petId)))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new NotFoundException(format("Active pet not found with id %s", petId)));;
|
||||||
|
|
||||||
|
Vaccination vaccination = new Vaccination();
|
||||||
|
vaccination.setPet(pet);
|
||||||
|
vaccination.setVaccineName("Vacine1");
|
||||||
|
vaccination.setDescription("Need to inject");
|
||||||
|
vaccination.setVaccinationDate(request.getVaccinationDate());
|
||||||
|
|
||||||
|
pet.addVaccination(vaccination);
|
||||||
|
|
||||||
|
vaccinationRepository.save(vaccination);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.springframework.samples.petclinic.vaccine.predicate;
|
||||||
|
|
||||||
|
import org.springframework.samples.petclinic.owner.Pet;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class PetPredicates {
|
||||||
|
|
||||||
|
public static Predicate<Pet> isActive() {
|
||||||
|
return pet -> Boolean.TRUE.equals(pet.getActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<Pet> hasId(Long petId) {
|
||||||
|
return pet -> petId.equals(pet.getId());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package org.springframework.samples.petclinic.vaccine.request;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Valid
|
||||||
|
public class VaccinationRequest {
|
||||||
|
@NotBlank
|
||||||
|
private String vaccineName;
|
||||||
|
private LocalDate vaccinationDate;
|
||||||
|
@NotBlank
|
||||||
|
private String description;
|
||||||
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
# database init, supports postgres too
|
# database init, supports postgres too
|
||||||
database=postgres
|
database=petclinic
|
||||||
spring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhost/petclinic}
|
#spring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhost/petclinic}
|
||||||
spring.datasource.username=${POSTGRES_USER:petclinic}
|
spring.datasource.url=jdbc:postgresql://localhost:5432/petclinic
|
||||||
spring.datasource.password=${POSTGRES_PASS:petclinic}
|
spring.datasource.username=postgres
|
||||||
|
spring.datasource.password=root
|
||||||
|
|
||||||
|
#spring.datasource.username=${POSTGRES_USER:petclinic}
|
||||||
|
#spring.datasource.password=${POSTGRES_PASS:petclinic}
|
||||||
# SQL is written to be idempotent so this is safe
|
# SQL is written to be idempotent so this is safe
|
||||||
spring.sql.init.mode=always
|
spring.sql.init.mode=always
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
# database init, supports mysql too
|
# database init, supports mysql too
|
||||||
database=h2
|
database=postgres
|
||||||
spring.sql.init.schema-locations=classpath*:db/${database}/schema.sql
|
spring.sql.init.schema-locations=classpath*:db/postgres/schema.sql,classpath*:db/postgres/alter.sql
|
||||||
spring.sql.init.data-locations=classpath*:db/${database}/data.sql
|
spring.sql.init.data-locations=classpath*:db/postgres/data.sql
|
||||||
|
|
||||||
|
spring.jpa.show-sql=true
|
||||||
|
spring.jpa.properties.hibernate.format_sql=true
|
||||||
|
logging.level.org.springframework.jdbc.datasource.init=DEBUG
|
||||||
|
|
||||||
|
|
||||||
# Web
|
# Web
|
||||||
spring.thymeleaf.mode=HTML
|
spring.thymeleaf.mode=HTML
|
||||||
|
|
||||||
# JPA
|
# JPA
|
||||||
spring.jpa.hibernate.ddl-auto=none
|
spring.jpa.hibernate.ddl-auto=none
|
||||||
spring.jpa.open-in-view=false
|
spring.jpa.open-in-view=true
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
spring.messages.basename=messages/messages
|
spring.messages.basename=messages/messages
|
||||||
|
@ -23,3 +28,5 @@ logging.level.org.springframework=INFO
|
||||||
|
|
||||||
# Maximum time static resources should be cached
|
# Maximum time static resources should be cached
|
||||||
spring.web.resources.cache.cachecontrol.max-age=12h
|
spring.web.resources.cache.cachecontrol.max-age=12h
|
||||||
|
|
||||||
|
|
||||||
|
|
16
src/main/resources/db/postgres/alter.sql
Normal file
16
src/main/resources/db/postgres/alter.sql
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
ALTER TABLE owners ADD COLUMN IF NOT EXISTS active BOOLEAN DEFAULT TRUE;
|
||||||
|
|
||||||
|
ALTER TABLE pets ADD COLUMN IF NOT EXISTS active BOOLEAN DEFAULT TRUE;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS vaccinations (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
vaccine_name TEXT NOT NULL,
|
||||||
|
vaccination_date DATE NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
injected BOOLEAN DEFAULT FALSE,
|
||||||
|
pet_id BIGINT NOT NULL,
|
||||||
|
CONSTRAINT fk_pet
|
||||||
|
FOREIGN KEY (pet_id)
|
||||||
|
REFERENCES pets(id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
Loading…
Reference in a new issue