full refactoring

This commit is contained in:
Sasank Vishnubhatla 2017-06-07 12:51:51 -04:00
parent 39f75b0d7e
commit 03b0759065
35 changed files with 579 additions and 567 deletions

View file

@ -20,16 +20,16 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* PetClinic Spring Boot Application.
* KidClinic Spring Boot Application.
*
* @author Dave Syer
*
*/
@SpringBootApplication
public class PetClinicApplication {
public class KidClinicApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(PetClinicApplication.class, args);
SpringApplication.run(KidClinicApplication.class, args);
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.vet;
package org.springframework.samples.kidclinic.doctor;
import java.util.ArrayList;
import java.util.Collections;
@ -42,11 +42,11 @@ import org.springframework.samples.kidclinic.model.Person;
* @author Arjen Poutsma
*/
@Entity
@Table(name = "vets")
public class Vet extends Person {
@Table(name = "doctors")
public class Doctor extends Person {
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"), inverseJoinColumns = @JoinColumn(name = "specialty_id"))
@JoinTable(name = "doctor_specialties", joinColumns = @JoinColumn(name = "doctor_id"), inverseJoinColumns = @JoinColumn(name = "specialty_id"))
private Set<Specialty> specialties;
protected Set<Specialty> getSpecialtiesInternal() {

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.vet;
package org.springframework.samples.kidclinic.doctor;
import java.util.Map;
@ -29,32 +29,32 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Arjen Poutsma
*/
@Controller
class VetController {
class DoctorController {
private final VetRepository vets;
private final DoctorRepository doctors;
@Autowired
public VetController(VetRepository clinicService) {
this.vets = clinicService;
public DoctorController(DoctorRepository clinicService) {
this.doctors = clinicService;
}
@RequestMapping(value = { "/vets.html" })
public String showVetList(Map<String, Object> model) {
// Here we are returning an object of type 'Vets' rather than a collection of Vet
@RequestMapping(value = { "/doctors.html" })
public String showDoctorList(Map<String, Object> model) {
// Here we are returning an object of type 'Doctors' rather than a collection of Doctor
// objects so it is simpler for Object-Xml mapping
Vets vets = new Vets();
vets.getVetList().addAll(this.vets.findAll());
model.put("vets", vets);
return "vets/vetList";
Doctors doctors = new Doctors();
doctors.getDoctorList().addAll(this.doctors.findAll());
model.put("doctors", doctors);
return "doctors/doctorList";
}
@RequestMapping(value = { "/vets.json", "/vets.xml" })
public @ResponseBody Vets showResourcesVetList() {
// Here we are returning an object of type 'Vets' rather than a collection of Vet
@RequestMapping(value = { "/doctors.json", "/doctors.xml" })
public @ResponseBody Doctors showResourcesVetList() {
// Here we are returning an object of type 'Doctors' rather than a collection of Doctor
// objects so it is simpler for JSon/Object mapping
Vets vets = new Vets();
vets.getVetList().addAll(this.vets.findAll());
return vets;
Doctors doctors = new Doctors();
doctors.getDoctorList().addAll(this.doctors.findAll());
return doctors;
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.vet;
package org.springframework.samples.kidclinic.doctor;
import java.util.Collection;
@ -23,7 +23,7 @@ import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* Repository class for <code>Vet</code> domain objects All method names are compliant with Spring Data naming
* Repository class for <code>Doctor</code> domain objects All method names are compliant with Spring Data naming
* conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation
*
* @author Ken Krebs
@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
* @author Sam Brannen
* @author Michael Isvy
*/
public interface VetRepository extends Repository<Vet, Integer> {
public interface DoctorRepository extends Repository<Doctor, Integer> {
/**
* Retrieve all <code>Vet</code>s from the data store.
* Retrieve all <code>Doctor</code>s from the data store.
*
* @return a <code>Collection</code> of <code>Vet</code>s
* @return a <code>Collection</code> of <code>Doctor</code>s
*/
@Transactional(readOnly = true)
@Cacheable("vets")
Collection<Vet> findAll() throws DataAccessException;
@Cacheable("doctors")
Collection<Doctor> findAll() throws DataAccessException;
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.vet;
package org.springframework.samples.kidclinic.doctor;
import java.util.ArrayList;
import java.util.List;
@ -22,22 +22,22 @@ import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Simple domain object representing a list of veterinarians. Mostly here to be used for the 'vets' {@link
* Simple domain object representing a list of doctors. Mostly here to be used for the 'doctors' {@link
* org.springframework.web.servlet.view.xml.MarshallingView}.
*
* @author Arjen Poutsma
*/
@XmlRootElement
public class Vets {
public class Doctors {
private List<Vet> vets;
private List<Doctor> doctors;
@XmlElement
public List<Vet> getVetList() {
if (vets == null) {
vets = new ArrayList<>();
public List<Doctor> getDoctorList() {
if (doctors == null) {
doctors = new ArrayList<>();
}
return vets;
return doctors;
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.vet;
package org.springframework.samples.kidclinic.doctor;
import java.io.Serializable;
@ -23,7 +23,7 @@ import javax.persistence.Table;
import org.springframework.samples.kidclinic.model.NamedEntity;
/**
* Models a {@link Vet Vet's} specialty (for example, dentistry).
* Models a {@link Doctor Doctor's} specialty (for example, dentistry).
*
* @author Juergen Hoeller
*/

View file

@ -1,136 +0,0 @@
/*
* Copyright 2002-2013 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
*
* http://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.kidclinic.owner;
import java.util.Collection;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
* @author Juergen Hoeller
* @author Ken Krebs
* @author Arjen Poutsma
* @author Michael Isvy
*/
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
@Autowired
public OwnerController(OwnerRepository clinicService) {
this.owners = clinicService;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
@RequestMapping(value = "/owners/new", method = RequestMethod.GET)
public String initCreationForm(Map<String, Object> model) {
Owner owner = new Owner();
model.put("owner", owner);
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
@RequestMapping(value = "/owners/new", method = RequestMethod.POST)
public String processCreationForm(@Valid Owner owner, BindingResult result) {
if (result.hasErrors()) {
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
} else {
this.owners.save(owner);
return "redirect:/owners/" + owner.getId();
}
}
@RequestMapping(value = "/owners/find", method = RequestMethod.GET)
public String initFindForm(Map<String, Object> model) {
model.put("owner", new Owner());
return "owners/findOwners";
}
@RequestMapping(value = "/owners", method = RequestMethod.GET)
public String processFindForm(Owner owner, BindingResult result, Map<String, Object> model) {
// allow parameterless GET request for /owners to return all records
if (owner.getLastName() == null) {
owner.setLastName(""); // empty string signifies broadest possible search
}
// find owners by last name
Collection<Owner> results = this.owners.findByLastName(owner.getLastName());
if (results.isEmpty()) {
// no owners found
result.rejectValue("lastName", "notFound", "not found");
return "owners/findOwners";
} else if (results.size() == 1) {
// 1 owner found
owner = results.iterator().next();
return "redirect:/owners/" + owner.getId();
} else {
// multiple owners found
model.put("selections", results);
return "owners/ownersList";
}
}
@RequestMapping(value = "/owners/{ownerId}/edit", method = RequestMethod.GET)
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
Owner owner = this.owners.findById(ownerId);
model.addAttribute(owner);
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
@RequestMapping(value = "/owners/{ownerId}/edit", method = RequestMethod.POST)
public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId) {
if (result.hasErrors()) {
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
} else {
owner.setId(ownerId);
this.owners.save(owner);
return "redirect:/owners/{ownerId}";
}
}
/**
* Custom handler for displaying an owner.
*
* @param ownerId the ID of the owner to display
* @return a ModelMap with the model attributes for the view
*/
@RequestMapping("/owners/{ownerId}")
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
ModelAndView mav = new ModelAndView("owners/ownerDetails");
mav.addObject(this.owners.findById(ownerId));
return mav;
}
}

View file

@ -1,116 +0,0 @@
/*
* Copyright 2002-2013 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
*
* http://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.kidclinic.owner;
import java.util.Collection;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
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.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author Juergen Hoeller
* @author Ken Krebs
* @author Arjen Poutsma
*/
@Controller
@RequestMapping("/owners/{ownerId}")
class PetController {
private static final String VIEWS_PETS_CREATE_OR_UPDATE_FORM = "pets/createOrUpdatePetForm";
private final PetRepository pets;
private final OwnerRepository owners;
@Autowired
public PetController(PetRepository pets, OwnerRepository owners) {
this.pets = pets;
this.owners = owners;
}
@ModelAttribute("types")
public Collection<PetType> populatePetTypes() {
return this.pets.findPetTypes();
}
@ModelAttribute("owner")
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
return this.owners.findById(ownerId);
}
@InitBinder("owner")
public void initOwnerBinder(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
@InitBinder("pet")
public void initPetBinder(WebDataBinder dataBinder) {
dataBinder.setValidator(new PetValidator());
}
@RequestMapping(value = "/pets/new", method = RequestMethod.GET)
public String initCreationForm(Owner owner, ModelMap model) {
Pet pet = new Pet();
owner.addPet(pet);
model.put("pet", pet);
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
}
@RequestMapping(value = "/pets/new", method = RequestMethod.POST)
public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model) {
if (StringUtils.hasLength(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null){
result.rejectValue("name", "duplicate", "already exists");
}
if (result.hasErrors()) {
model.put("pet", pet);
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
} else {
owner.addPet(pet);
this.pets.save(pet);
return "redirect:/owners/{ownerId}";
}
}
@RequestMapping(value = "/pets/{petId}/edit", method = RequestMethod.GET)
public String initUpdateForm(@PathVariable("petId") int petId, ModelMap model) {
Pet pet = this.pets.findById(petId);
model.put("pet", pet);
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
}
@RequestMapping(value = "/pets/{petId}/edit", method = RequestMethod.POST)
public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) {
if (result.hasErrors()) {
pet.setOwner(owner);
model.put("pet", pet);
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
} else {
owner.addPet(pet);
this.pets.save(pet);
return "redirect:/owners/{ownerId}";
}
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.owner;
package org.springframework.samples.kidclinic.parent;
import java.util.ArrayList;
import java.util.Collections;
@ -48,8 +48,8 @@ import org.springframework.samples.kidclinic.visit.Visit;
* @author Sam Brannen
*/
@Entity
@Table(name = "pets")
public class Pet extends NamedEntity {
@Table(name = "kids")
public class Kid extends NamedEntity {
@Column(name = "birth_date")
@Temporal(TemporalType.DATE)
@ -57,14 +57,14 @@ public class Pet extends NamedEntity {
private Date birthDate;
@ManyToOne
@JoinColumn(name = "type_id")
private PetType type;
@JoinColumn(name = "gender_id")
private KidGender gender;
@ManyToOne
@JoinColumn(name = "owner_id")
private Owner owner;
@JoinColumn(name = "parent_id")
private Parent parent;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "petId", fetch = FetchType.EAGER)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "kidId", fetch = FetchType.EAGER)
private Set<Visit> visits = new LinkedHashSet<>();
public void setBirthDate(Date birthDate) {
@ -75,20 +75,20 @@ public class Pet extends NamedEntity {
return this.birthDate;
}
public PetType getType() {
return this.type;
public KidGender getGender() {
return this.gender;
}
public void setType(PetType type) {
this.type = type;
public void setGender(KidGender gender) {
this.gender = gender;
}
public Owner getOwner() {
return this.owner;
public Parent getParent() {
return this.parent;
}
protected void setOwner(Owner owner) {
this.owner = owner;
protected void setParent(Parent parent) {
this.parent = parent;
}
protected Set<Visit> getVisitsInternal() {
@ -111,7 +111,7 @@ public class Pet extends NamedEntity {
public void addVisit(Visit visit) {
getVisitsInternal().add(visit);
visit.setPetId(this.getId());
visit.setKidId(this.getId());
}
}

View file

@ -0,0 +1,116 @@
/*
* Copyright 2002-2013 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
*
* http://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.kidclinic.parent;
import java.util.Collection;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
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.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author Juergen Hoeller
* @author Ken Krebs
* @author Arjen Poutsma
*/
@Controller
@RequestMapping("/parents/{parentId}")
class KidController {
private static final String VIEWS_KIDS_CREATE_OR_UPDATE_FORM = "kids/createOrUpdateKidForm";
private final KidRepository kids;
private final ParentRepository parents;
@Autowired
public KidController(KidRepository kids, ParentRepository parents) {
this.kids = kids;
this.parents = parents;
}
@ModelAttribute("gender")
public Collection<KidGender> populateKidGenders() {
return this.kids.findKidGenders();
}
@ModelAttribute("parent")
public Parent findParent(@PathVariable("parentId") int parentId) {
return this.parents.findById(parentId);
}
@InitBinder("parent")
public void initParentBinder(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
@InitBinder("kid")
public void initKidBinder(WebDataBinder dataBinder) {
dataBinder.setValidator(new KidValidator());
}
@RequestMapping(value = "/kids/new", method = RequestMethod.GET)
public String initCreationForm(Parent parent, ModelMap model) {
Kid kid = new Kid();
parent.addKid(kid);
model.put("kid", kid);
return VIEWS_KIDS_CREATE_OR_UPDATE_FORM;
}
@RequestMapping(value = "/kids/new", method = RequestMethod.POST)
public String processCreationForm(Parent parent, @Valid Kid kid, BindingResult result, ModelMap model) {
if (StringUtils.hasLength(kid.getName()) && kid.isNew() && parent.getKid(kid.getName(), true) != null){
result.rejectValue("name", "duplicate", "already exists");
}
if (result.hasErrors()) {
model.put("kid", kid);
return VIEWS_KIDS_CREATE_OR_UPDATE_FORM;
} else {
parent.addKid(kid);
this.kids.save(kid);
return "redirect:/parents/{parentId}";
}
}
@RequestMapping(value = "/kids/{kidId}/edit", method = RequestMethod.GET)
public String initUpdateForm(@PathVariable("kidId") int kidId, ModelMap model) {
Kid kid = this.kids.findById(kidId);
model.put("kid", kid);
return VIEWS_KIDS_CREATE_OR_UPDATE_FORM;
}
@RequestMapping(value = "/kids/{kidsId}/edit", method = RequestMethod.POST)
public String processUpdateForm(@Valid Kid kid, BindingResult result, Parent parent, ModelMap model) {
if (result.hasErrors()) {
kid.setParent(parent);
model.put("kid", kid);
return VIEWS_KIDS_CREATE_OR_UPDATE_FORM;
} else {
parent.addKid(kid);
this.kids.save(kid);
return "redirect:/parents/{parentId}";
}
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.owner;
package org.springframework.samples.kidclinic.parent;
import javax.persistence.Entity;
import javax.persistence.Table;
@ -25,7 +25,7 @@ import org.springframework.samples.kidclinic.model.NamedEntity;
* Can be Cat, Dog, Hamster...
*/
@Entity
@Table(name = "types")
public class PetType extends NamedEntity {
@Table(name = "gender")
public class KidGender extends NamedEntity {
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.owner;
package org.springframework.samples.kidclinic.parent;
import java.text.ParseException;
@ -25,7 +25,7 @@ import org.springframework.format.Formatter;
import org.springframework.stereotype.Component;
/**
* Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting from Spring 3.0, Formatters have
* Instructs Spring MVC on how to parse and print elements of type 'KidGender'. 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: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html#format-Formatter-SPI
* - A nice blog entry from Gordon Dickens: http://gordondickens.com/wordpress/2010/09/30/using-spring-3-0-custom-type-converter/
@ -36,30 +36,30 @@ import org.springframework.stereotype.Component;
* @author Michael Isvy
*/
@Component
public class PetTypeFormatter implements Formatter<PetType> {
public class KidGenderFormatter implements Formatter<KidGender> {
private final PetRepository pets;
private final KidRepository kids;
@Autowired
public PetTypeFormatter(PetRepository pets) {
this.pets = pets;
public KidGenderFormatter(KidRepository kids) {
this.kids = kids;
}
@Override
public String print(PetType petType, Locale locale) {
return petType.getName();
public String print(KidGender kidGender, Locale locale) {
return kidGender.getName();
}
@Override
public PetType parse(String text, Locale locale) throws ParseException {
Collection<PetType> findPetTypes = this.pets.findPetTypes();
for (PetType type : findPetTypes) {
if (type.getName().equals(text)) {
return type;
public KidGender parse(String text, Locale locale) throws ParseException {
Collection<KidGender> findKidGenders = this.kids.findKidGenders();
for (KidGender gender : findKidGenders) {
if (gender.getName().equals(text)) {
return gender;
}
}
throw new ParseException("type not found: " + text, 0);
throw new ParseException("gender not found: " + text, 0);
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.owner;
package org.springframework.samples.kidclinic.parent;
import java.util.List;
@ -22,7 +22,7 @@ import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* Repository class for <code>Pet</code> domain objects All method names are compliant with Spring Data naming
* Repository class for <code>Kid</code> domain objects All method names are compliant with Spring Data naming
* conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation
*
* @author Ken Krebs
@ -30,29 +30,29 @@ import org.springframework.transaction.annotation.Transactional;
* @author Sam Brannen
* @author Michael Isvy
*/
public interface PetRepository extends Repository<Pet, Integer> {
public interface KidRepository extends Repository<Kid, Integer> {
/**
* Retrieve all {@link PetType}s from the data store.
* @return a Collection of {@link PetType}s.
* Retrieve all {@link KidGender}s from the data store.
* @return a Collection of {@link KidGender}s.
*/
@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
@Query("SELECT ptype FROM KidGender ptype ORDER BY ptype.name")
@Transactional(readOnly = true)
List<PetType> findPetTypes();
List<KidGender> findKidGenders();
/**
* Retrieve a {@link Pet} from the data store by id.
* Retrieve a {@link Kid} from the data store by id.
* @param id the id to search for
* @return the {@link Pet} if found
* @return the {@link Kid} if found
*/
@Transactional(readOnly = true)
Pet findById(Integer id);
Kid findById(Integer id);
/**
* Save a {@link Pet} to the data store, either inserting or updating it.
* @param pet the {@link Pet} to save
* Save a {@link Kid} to the data store, either inserting or updating it.
* @param kid the {@link Kid} to save
*/
void save(Pet pet);
void save(Kid kid);
}

View file

@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.owner;
package org.springframework.samples.kidclinic.parent;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* <code>Validator</code> for <code>Pet</code> forms.
* <code>Validator</code> for <code>Kid</code> forms.
* <p>
* We're not using Bean Validation annotations here because it is easier to define such validation rule in Java.
* </p>
@ -28,36 +28,36 @@ import org.springframework.validation.Validator;
* @author Ken Krebs
* @author Juergen Hoeller
*/
public class PetValidator implements Validator {
public class KidValidator implements Validator {
private static final String REQUIRED = "required";
@Override
public void validate(Object obj, Errors errors) {
Pet pet = (Pet) obj;
String name = pet.getName();
Kid kid = (Kid) obj;
String name = kid.getName();
// name validation
if (!StringUtils.hasLength(name)) {
errors.rejectValue("name", REQUIRED, REQUIRED);
}
// type validation
if (pet.isNew() && pet.getType() == null) {
errors.rejectValue("type", REQUIRED, REQUIRED);
// gender validation
if (kid.isNew() && kid.getGender() == null) {
errors.rejectValue("gender", REQUIRED, REQUIRED);
}
// birth date validation
if (pet.getBirthDate() == null) {
if (kid.getBirthDate() == null) {
errors.rejectValue("birthDate", REQUIRED, REQUIRED);
}
}
/**
* This Validator validates *just* Pet instances
* This Validator validates *just* Kid instances
*/
@Override
public boolean supports(Class<?> clazz) {
return Pet.class.isAssignableFrom(clazz);
return Kid.class.isAssignableFrom(clazz);
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.owner;
package org.springframework.samples.kidclinic.parent;
import java.util.ArrayList;
import java.util.Collections;
@ -35,7 +35,7 @@ import org.springframework.core.style.ToStringCreator;
import org.springframework.samples.kidclinic.model.Person;
/**
* Simple JavaBean domain object representing an owner.
* Simple JavaBean domain object representing an parent.
*
* @author Ken Krebs
* @author Juergen Hoeller
@ -43,8 +43,8 @@ import org.springframework.samples.kidclinic.model.Person;
* @author Michael Isvy
*/
@Entity
@Table(name = "owners")
public class Owner extends Person {
@Table(name = "parents")
public class Parent extends Person {
@Column(name = "address")
@NotEmpty
private String address;
@ -58,8 +58,8 @@ public class Owner extends Person {
@Digits(fraction = 0, integer = 10)
private String telephone;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
private Set<Pet> pets;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent")
private Set<Kid> kids;
public String getAddress() {
@ -86,54 +86,54 @@ public class Owner extends Person {
this.telephone = telephone;
}
protected Set<Pet> getPetsInternal() {
if (this.pets == null) {
this.pets = new HashSet<>();
protected Set<Kid> getKidsInternal() {
if (this.kids == null) {
this.kids = new HashSet<>();
}
return this.pets;
return this.kids;
}
protected void setPetsInternal(Set<Pet> pets) {
this.pets = pets;
protected void setKidsInternal(Set<Kid> kids) {
this.kids = kids;
}
public List<Pet> getPets() {
List<Pet> sortedPets = new ArrayList<>(getPetsInternal());
PropertyComparator.sort(sortedPets, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedPets);
public List<Kid> getKids() {
List<Kid> sortedKids = new ArrayList<>(getKidsInternal());
PropertyComparator.sort(sortedKids, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedKids);
}
public void addPet(Pet pet) {
if (pet.isNew()) {
getPetsInternal().add(pet);
public void addKid(Kid kid) {
if (kid.isNew()) {
getKidsInternal().add(kid);
}
pet.setOwner(this);
kid.setParent(this);
}
/**
* Return the Pet with the given name, or null if none found for this Owner.
* Return the Kid with the given name, or null if none found for this Parent.
*
* @param name to test
* @return true if pet name is already in use
* @return true if kid name is already in use
*/
public Pet getPet(String name) {
return getPet(name, false);
public Kid getKid(String name) {
return getKid(name, false);
}
/**
* Return the Pet with the given name, or null if none found for this Owner.
* Return the Kid with the given name, or null if none found for this Kid.
*
* @param name to test
* @return true if pet name is already in use
* @return true if kid name is already in use
*/
public Pet getPet(String name, boolean ignoreNew) {
public Kid getKid(String name, boolean ignoreNew) {
name = name.toLowerCase();
for (Pet pet : getPetsInternal()) {
if (!ignoreNew || !pet.isNew()) {
String compName = pet.getName();
for (Kid kid : getKidsInternal()) {
if (!ignoreNew || !kid.isNew()) {
String compName = kid.getName();
compName = compName.toLowerCase();
if (compName.equals(name)) {
return pet;
return kid;
}
}
}

View file

@ -0,0 +1,136 @@
/*
* Copyright 2002-2013 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
*
* http://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.kidclinic.parent;
import java.util.Collection;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
* @author Juergen Hoeller
* @author Ken Krebs
* @author Arjen Poutsma
* @author Michael Isvy
*/
@Controller
class ParentController {
private static final String VIEWS_PARENT_CREATE_OR_UPDATE_FORM = "parents/createOrUpdateParentForm";
private final ParentRepository parents;
@Autowired
public ParentController(ParentRepository clinicService) {
this.parents = clinicService;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
@RequestMapping(value = "/parents/new", method = RequestMethod.GET)
public String initCreationForm(Map<String, Object> model) {
Parent parent = new Parent();
model.put("parent", parent);
return VIEWS_PARENT_CREATE_OR_UPDATE_FORM;
}
@RequestMapping(value = "/parents/new", method = RequestMethod.POST)
public String processCreationForm(@Valid Parent parent, BindingResult result) {
if (result.hasErrors()) {
return VIEWS_PARENT_CREATE_OR_UPDATE_FORM;
} else {
this.parents.save(parent);
return "redirect:/parent/" + parent.getId();
}
}
@RequestMapping(value = "/parents/find", method = RequestMethod.GET)
public String initFindForm(Map<String, Object> model) {
model.put("parent", new Parent());
return "parents/findParents";
}
@RequestMapping(value = "/parents", method = RequestMethod.GET)
public String processFindForm(Parent parent, BindingResult result, Map<String, Object> model) {
// allow parameterless GET request for /parents to return all records
if (parent.getLastName() == null) {
parent.setLastName(""); // empty string signifies broadest possible search
}
// find parents by last name
Collection<Parent> results = this.parents.findByLastName(parent.getLastName());
if (results.isEmpty()) {
// no parents found
result.rejectValue("lastName", "notFound", "not found");
return "parents/findParents";
} else if (results.size() == 1) {
// 1 parent found
parent = results.iterator().next();
return "redirect:/parents/" + parent.getId();
} else {
// multiple parents found
model.put("selections", results);
return "parents/parentsList";
}
}
@RequestMapping(value = "/parents/{parentId}/edit", method = RequestMethod.GET)
public String initUpdateParentForm(@PathVariable("parentId") int parentId, Model model) {
Parent parent = this.parents.findById(parentId);
model.addAttribute(parent);
return VIEWS_PARENT_CREATE_OR_UPDATE_FORM;
}
@RequestMapping(value = "/parents/{parentId}/edit", method = RequestMethod.POST)
public String processUpdateParentForm(@Valid Parent parent, BindingResult result, @PathVariable("parentId") int parentId) {
if (result.hasErrors()) {
return VIEWS_PARENT_CREATE_OR_UPDATE_FORM;
} else {
parent.setId(parentId);
this.parents.save(parent);
return "redirect:/parents/{parentId}";
}
}
/**
* Custom handler for displaying an parent.
*
* @param parentId the ID of the parent to display
* @return a ModelMap with the model attributes for the view
*/
@RequestMapping("/parents/{parentId}")
public ModelAndView showParent(@PathVariable("parentId") int parentId) {
ModelAndView mav = new ModelAndView("parents/parentDetails");
mav.addObject(this.parents.findById(parentId));
return mav;
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.owner;
package org.springframework.samples.kidclinic.parent;
import java.util.Collection;
@ -23,7 +23,7 @@ import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* Repository class for <code>Owner</code> domain objects All method names are compliant with Spring Data naming
* Repository class for <code>Parent</code> domain objects All method names are compliant with Spring Data naming
* conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation
*
* @author Ken Krebs
@ -31,33 +31,33 @@ import org.springframework.transaction.annotation.Transactional;
* @author Sam Brannen
* @author Michael Isvy
*/
public interface OwnerRepository extends Repository<Owner, Integer> {
public interface ParentRepository extends Repository<Parent, Integer> {
/**
* Retrieve {@link Owner}s from the data store by last name, returning all owners
* Retrieve {@link Parent}s from the data store by last name, returning all parents
* whose last name <i>starts</i> with the given name.
* @param lastName Value to search for
* @return a Collection of matching {@link Owner}s (or an empty Collection if none
* @return a Collection of matching {@link Parent}s (or an empty Collection if none
* found)
*/
@Query("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName%")
@Query("SELECT DISTINCT parent FROM Parent parent left join fetch parent.kids WHERE parent.lastName LIKE :lastName%")
@Transactional(readOnly = true)
Collection<Owner> findByLastName(@Param("lastName") String lastName);
Collection<Parent> findByLastName(@Param("lastName") String lastName);
/**
* Retrieve an {@link Owner} from the data store by id.
* Retrieve an {@link Parent} from the data store by id.
* @param id the id to search for
* @return the {@link Owner} if found
* @return the {@link Parent} if found
*/
@Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id")
@Query("SELECT parent FROM Parent parent left join fetch parent.kids WHERE parent.id =:id")
@Transactional(readOnly = true)
Owner findById(@Param("id") Integer id);
Parent findById(@Param("id") Integer id);
/**
* Save an {@link Owner} to the data store, either inserting or updating it.
* @param owner the {@link Owner} to save
* Save an {@link Parent} to the data store, either inserting or updating it.
* @param owner the {@link Parent} to save
*/
void save(Owner owner);
void save(Parent parent);
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.kidclinic.owner;
package org.springframework.samples.kidclinic.parent;
import java.util.Map;
@ -42,13 +42,13 @@ import org.springframework.web.bind.annotation.RequestMethod;
class VisitController {
private final VisitRepository visits;
private final PetRepository pets;
private final KidRepository kids;
@Autowired
public VisitController(VisitRepository visits, PetRepository pets) {
public VisitController(VisitRepository visits, KidRepository kids) {
this.visits = visits;
this.pets = pets;
this.kids = kids;
}
@InitBinder
@ -60,35 +60,35 @@ class VisitController {
* Called before each and every @RequestMapping annotated method.
* 2 goals:
* - Make sure we always have fresh data
* - Since we do not use the session scope, make sure that Pet object always has an id
* - Since we do not use the session scope, make sure that Kid object always has an id
* (Even though id is not part of the form fields)
*
* @param petId
* @return Pet
* @param kidId
* @return Kid
*/
@ModelAttribute("visit")
public Visit loadPetWithVisit(@PathVariable("petId") int petId, Map<String, Object> model) {
Pet pet = this.pets.findById(petId);
model.put("pet", pet);
public Visit loadKidWithVisit(@PathVariable("kidId") int kidId, Map<String, Object> model) {
Kid kid = this.kids.findById(kidId);
model.put("kid", kid);
Visit visit = new Visit();
pet.addVisit(visit);
kid.addVisit(visit);
return visit;
}
// Spring MVC calls method loadPetWithVisit(...) before initNewVisitForm is called
@RequestMapping(value = "/owners/*/pets/{petId}/visits/new", method = RequestMethod.GET)
public String initNewVisitForm(@PathVariable("petId") int petId, Map<String, Object> model) {
return "pets/createOrUpdateVisitForm";
// Spring MVC calls method loadKidWithVisit(...) before initNewVisitForm is called
@RequestMapping(value = "/parents/*/kids/{kidId}/visits/new", method = RequestMethod.GET)
public String initNewVisitForm(@PathVariable("kidId") int kidId, Map<String, Object> model) {
return "kids/createOrUpdateVisitForm";
}
// Spring MVC calls method loadPetWithVisit(...) before processNewVisitForm is called
@RequestMapping(value = "/owners/{ownerId}/pets/{petId}/visits/new", method = RequestMethod.POST)
// Spring MVC calls method loadKidWithVisit(...) before processNewVisitForm is called
@RequestMapping(value = "/parents/{parentId}/kids/{kidId}/visits/new", method = RequestMethod.POST)
public String processNewVisitForm(@Valid Visit visit, BindingResult result) {
if (result.hasErrors()) {
return "pets/createOrUpdateVisitForm";
return "kids/createOrUpdateVisitForm";
} else {
this.visits.save(visit);
return "redirect:/owners/{ownerId}";
return "redirect:/parents/{parentId}";
}
}

View file

@ -20,7 +20,7 @@ class CacheConfig {
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
Configuration<Object, Object> cacheConfiguration = createCacheConfiguration();
cm.createCache("vets", cacheConfiguration);
cm.createCache("doctors", cacheConfiguration);
};
}

View file

@ -29,7 +29,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
@Controller
class CrashController {
@RequestMapping(value = "/oups", method = RequestMethod.GET)
@RequestMapping(value = "/oops", method = RequestMethod.GET)
public String triggerException() {
throw new RuntimeException(
"Expected: controller used to showcase what " + "happens when an exception is thrown");

View file

@ -55,8 +55,8 @@ public class Visit extends BaseEntity {
/**
* Holds value of property pet.
*/
@Column(name = "pet_id")
private Integer petId;
@Column(name = "kid_id")
private Integer kidId;
/**
@ -104,21 +104,21 @@ public class Visit extends BaseEntity {
}
/**
* Getter for property pet id.
* Getter for property kid id.
*
* @return Value of property pet id.
* @return Value of property kid id.
*/
public Integer getPetId() {
return this.petId;
public Integer getKidId() {
return this.kidId;
}
/**
* Setter for property pet id.
* Setter for property kid id.
*
* @param petId New value of property pet id.
* @param petId New value of property kid id.
*/
public void setPetId(Integer petId) {
this.petId = petId;
public void setKidId(Integer kidId) {
this.kidId = kidId;
}
}

View file

@ -40,6 +40,6 @@ public interface VisitRepository extends Repository<Visit, Integer> {
*/
void save(Visit visit) throws DataAccessException;
List<Visit> findByPetId(Integer petId);
List<Visit> findByKidId(Integer kidId);
}

View file

@ -1,50 +1,50 @@
INSERT INTO vets VALUES (1, 'James', 'Carter');
INSERT INTO vets VALUES (2, 'Helen', 'Leary');
INSERT INTO vets VALUES (3, 'Linda', 'Douglas');
INSERT INTO vets VALUES (4, 'Rafael', 'Ortega');
INSERT INTO vets VALUES (5, 'Henry', 'Stevens');
INSERT INTO vets VALUES (6, 'Sharon', 'Jenkins');
INSERT INTO doctors VALUES (1, 'James', 'Carter');
INSERT INTO doctors VALUES (2, 'Helen', 'Leary');
INSERT INTO doctors VALUES (3, 'Linda', 'Douglas');
INSERT INTO doctors VALUES (4, 'Rafael', 'Ortega');
INSERT INTO doctors VALUES (5, 'Henry', 'Stevens');
INSERT INTO doctors VALUES (6, 'Sharon', 'Jenkins');
INSERT INTO specialties VALUES (1, 'radiology');
INSERT INTO specialties VALUES (2, 'surgery');
INSERT INTO specialties VALUES (3, 'dentistry');
INSERT INTO vet_specialties VALUES (2, 1);
INSERT INTO vet_specialties VALUES (3, 2);
INSERT INTO vet_specialties VALUES (3, 3);
INSERT INTO vet_specialties VALUES (4, 2);
INSERT INTO vet_specialties VALUES (5, 1);
INSERT INTO doctor_specialties VALUES (2, 1);
INSERT INTO doctor_specialties VALUES (3, 2);
INSERT INTO doctor_specialties VALUES (3, 3);
INSERT INTO doctor_specialties VALUES (4, 2);
INSERT INTO doctor_specialties VALUES (5, 1);
INSERT INTO types VALUES (1, 'male');
INSERT INTO types VALUES (2, 'female');
INSERT INTO gender VALUES (1, 'male');
INSERT INTO gender VALUES (2, 'female');
INSERT INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
INSERT INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');
INSERT INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');
INSERT INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');
INSERT INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');
INSERT INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');
INSERT INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');
INSERT INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');
INSERT INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');
INSERT INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');
INSERT INTO parents VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
INSERT INTO parents VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');
INSERT INTO parents VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');
INSERT INTO parents VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');
INSERT INTO parents VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');
INSERT INTO parents VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');
INSERT INTO parents VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');
INSERT INTO parents VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');
INSERT INTO parents VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');
INSERT INTO parents VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');
INSERT INTO pets VALUES (1, 'Alyssa', '2000-09-07', 2, 1);
INSERT INTO pets VALUES (2, 'Joe', '2002-08-06', 1, 2);
INSERT INTO pets VALUES (3, 'Lauren', '2001-04-17', 2, 3);
INSERT INTO pets VALUES (4, 'Nicole', '2000-03-07', 2, 3);
INSERT INTO pets VALUES (5, 'Thomas', '2000-11-30', 1, 4);
INSERT INTO pets VALUES (6, 'Samantha', '2000-01-20', 2, 5);
INSERT INTO pets VALUES (7, 'George', '1995-09-04', 1, 6);
INSERT INTO pets VALUES (8, 'Max', '1995-09-04', 1, 6);
INSERT INTO pets VALUES (9, 'Brendan', '1999-08-06', 1, 7);
INSERT INTO pets VALUES (10, 'Elizabeth', '1997-02-24', 2, 8);
INSERT INTO pets VALUES (11, 'Lucy', '2000-03-09', 2, 9);
INSERT INTO pets VALUES (12, 'Sunny', '2000-06-24', 2, 10);
INSERT INTO pets VALUES (13, 'Conner', '2002-06-08', 1, 10);
INSERT INTO kids VALUES (1, 'Alyssa', '2000-09-07', 2, 1);
INSERT INTO kids VALUES (2, 'Joe', '2002-08-06', 1, 2);
INSERT INTO kids VALUES (3, 'Lauren', '2001-04-17', 2, 3);
INSERT INTO kids VALUES (4, 'Nicole', '2000-03-07', 2, 3);
INSERT INTO kids VALUES (5, 'Thomas', '2000-11-30', 1, 4);
INSERT INTO kids VALUES (6, 'Samantha', '2000-01-20', 2, 5);
INSERT INTO kids VALUES (7, 'George', '1995-09-04', 1, 6);
INSERT INTO kids VALUES (8, 'Max', '1995-09-04', 1, 6);
INSERT INTO kids VALUES (9, 'Brendan', '1999-08-06', 1, 7);
INSERT INTO kids VALUES (10, 'Elizabeth', '1997-02-24', 2, 8);
INSERT INTO kids VALUES (11, 'Lucy', '2000-03-09', 2, 9);
INSERT INTO kids VALUES (12, 'Sunny', '2000-06-24', 2, 10);
INSERT INTO kids VALUES (13, 'Conner', '2002-06-08', 1, 10);
INSERT INTO visits VALUES (1, 7, '2013-01-01', 'rabies shot');
INSERT INTO visits VALUES (2, 8, '2013-01-02', 'rabies shot');
INSERT INTO visits VALUES (3, 8, '2013-01-03', 'neutered');
INSERT INTO visits VALUES (4, 7, '2013-01-04', 'spayed');
INSERT INTO visits VALUES (3, 8, '2013-01-03', 'cold');
INSERT INTO visits VALUES (4, 7, '2013-01-04', 'flu');

View file

@ -1,18 +1,18 @@
DROP TABLE vet_specialties IF EXISTS;
DROP TABLE vets IF EXISTS;
DROP TABLE doctor_specialties IF EXISTS;
DROP TABLE doctors IF EXISTS;
DROP TABLE specialties IF EXISTS;
DROP TABLE visits IF EXISTS;
DROP TABLE pets IF EXISTS;
DROP TABLE types IF EXISTS;
DROP TABLE owners IF EXISTS;
DROP TABLE kids IF EXISTS;
DROP TABLE gender IF EXISTS;
DROP TABLE parents IF EXISTS;
CREATE TABLE vets (
CREATE TABLE doctors (
id INTEGER IDENTITY PRIMARY KEY,
first_name VARCHAR(30),
last_name VARCHAR(30)
);
CREATE INDEX vets_last_name ON vets (last_name);
CREATE INDEX doctors_last_name ON doctors (last_name);
CREATE TABLE specialties (
id INTEGER IDENTITY PRIMARY KEY,
@ -20,20 +20,20 @@ CREATE TABLE specialties (
);
CREATE INDEX specialties_name ON specialties (name);
CREATE TABLE vet_specialties (
vet_id INTEGER NOT NULL,
CREATE TABLE doctor_specialties (
doctor_id INTEGER NOT NULL,
specialty_id INTEGER NOT NULL
);
ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_vets FOREIGN KEY (vet_id) REFERENCES vets (id);
ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_specialties FOREIGN KEY (specialty_id) REFERENCES specialties (id);
ALTER TABLE doctor_specialties ADD CONSTRAINT fk_doctor_specialties_doctors FOREIGN KEY (doctor_id) REFERENCES doctors (id);
ALTER TABLE doctor_specialties ADD CONSTRAINT fk_doctor_specialties_specialties FOREIGN KEY (specialty_id) REFERENCES specialties (id);
CREATE TABLE types (
CREATE TABLE gender (
id INTEGER IDENTITY PRIMARY KEY,
name VARCHAR(80)
);
CREATE INDEX types_name ON types (name);
CREATE INDEX gender_name ON gender (name);
CREATE TABLE owners (
CREATE TABLE parents (
id INTEGER IDENTITY PRIMARY KEY,
first_name VARCHAR(30),
last_name VARCHAR_IGNORECASE(30),
@ -41,24 +41,24 @@ CREATE TABLE owners (
city VARCHAR(80),
telephone VARCHAR(20)
);
CREATE INDEX owners_last_name ON owners (last_name);
CREATE INDEX parents_last_name ON parents (last_name);
CREATE TABLE pets (
CREATE TABLE kids (
id INTEGER IDENTITY PRIMARY KEY,
name VARCHAR(30),
birth_date DATE,
type_id INTEGER NOT NULL,
owner_id INTEGER NOT NULL
parent_id INTEGER NOT NULL
);
ALTER TABLE pets ADD CONSTRAINT fk_pets_owners FOREIGN KEY (owner_id) REFERENCES owners (id);
ALTER TABLE pets ADD CONSTRAINT fk_pets_types FOREIGN KEY (type_id) REFERENCES types (id);
CREATE INDEX pets_name ON pets (name);
ALTER TABLE kids ADD CONSTRAINT fk_kids_parents FOREIGN KEY (parent_id) REFERENCES parents (id);
ALTER TABLE kids ADD CONSTRAINT fk_kids_gender FOREIGN KEY (type_id) REFERENCES gender (id);
CREATE INDEX kids_name ON kids (name);
CREATE TABLE visits (
id INTEGER IDENTITY PRIMARY KEY,
pet_id INTEGER NOT NULL,
kid_id INTEGER NOT NULL,
visit_date DATE,
description VARCHAR(255)
);
ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id);
CREATE INDEX visits_pet_id ON visits (pet_id);
ALTER TABLE visits ADD CONSTRAINT fk_visits_kids FOREIGN KEY (kid_id) REFERENCES kids (id);
CREATE INDEX visits_kid_id ON visits (kid_id);

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'doctors')}">
<body>
<h2>Pediatricians</h2>
<table id="doctors" class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Specialties</th>
</tr>
</thead>
<tbody>
<tr th:each="doctor : ${doctors.doctorList}">
<td th:text="${doctor.firstName + ' ' + doctor.lastName}"></td>
<td><span th:each="specialty : ${doctor.specialties}"
th:text="${specialty.name + ' '}" /> <span
th:if="${doctor.nrOfSpecialties == 0}">none</span></td>
</tr>
</tbody>
</table>
</body>
</html>

View file

@ -3,8 +3,8 @@
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'error')}">
<body>
<h2>Reviews</h2>
<p>We are currently curating our review.</p>
<h2>Errors</h2>
<p th:text="${message}">Exception message</p>
</body>
</html>

View file

@ -10,7 +10,7 @@
<link rel="shortcut icon" type="image/x-icon" th:href="@{/resources/images/favicon.png}">
<title>PetClinic :: a Spring Framework demonstration</title>
<title>KidClinic :: a Spring Framework demonstration</title>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
@ -40,11 +40,13 @@
<a class="navbar-brand" href="#" size="5">KidClinic</a>
<li th:class="${module == 'home' ? 'active' : ''}"><a href="#" th:href="@{/}" ><span class="glyphicon glyphicon-home"> Home</a></li>
<li th:classappend="${module == 'owners' ? 'active' : ''}"><a href="/owners/find" th:href="@{/owners/find}" ><span class="glyphicon glyphicon-search"> Owners</a></li>
<li th:classappend="${module == 'parents' ? 'active' : ''}"><a href="/parents/find" th:href="@{/parents/find}" ><span class="glyphicon glyphicon-search"> Parents</a></li>
<li th:classappend="${module == 'vets' ? 'active' : ''}"><a href="/vets.html" th:href="@{/vets.html}"><span class="glyphicon glyphicon-th-list"> Doctors</a></li>
<li th:classappend="${module == 'doctors' ? 'active' : ''}"><a href="/doctors.html" th:href="@{/doctors.html}"><span class="glyphicon glyphicon-th-list"> Doctors</a></li>
<li th:classappend="${module == 'reviews' ? 'active' : ''}"><a href="/reviews.html" th:href="@{/reviews.html}"><span class="glyphicon glyphicon-star"> Reviews</a></li>
<li th:classappend="${module == 'error' ? 'active' : ''}"><a href="/oups" th:href="@{/oups}" ><span class="glyphicon glyphicon-star"> Reviews</a></li>
<li th:classappend="${module == 'error' ? 'active' : ''}"><a href="/oops" th:href="@{/oops}" ><span class="glyphicon glyphicon-warning-sign"> Errors</a></li>

View file

@ -1,19 +1,19 @@
<html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
th:replace="~{fragments/layout :: layout (~{::body},'parents')}">
<body>
<h2>
<th:block th:if="${pet['new']}">New </th:block>
Pet
<th:block th:if="${kid['new']}">New </th:block>
Kid
</h2>
<form th:object="${pet}" class="form-horizontal" method="post">
<form th:object="${kid}" class="form-horizontal" method="post">
<input type="hidden" name="id" th:value="*{id}" />
<div class="form-group has-feedback">
<div class="form-group">
<label class="col-sm-2 control-label">Owner</label>
<label class="col-sm-2 control-label">Parent</label>
<div class="col-sm-10">
<span th:text="${pet.owner?.firstName + ' ' + pet.owner?.lastName}" />
<span th:text="${kid.parent?.firstName + ' ' + kid.parent?.lastName}" />
</div>
</div>
<input
@ -26,9 +26,9 @@
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button
th:with="text=${owner['new']} ? 'Add Pet' : 'Update Pet'"
th:with="text=${parent['new']} ? 'Add Kid' : 'Update Kid'"
class="btn btn-default" type="submit" th:text="${text}">Add
Pet</button>
Kid</button>
</div>
</div>
</form>

View file

@ -1,5 +1,5 @@
<html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
th:replace="~{fragments/layout :: layout (~{::body},'parents')}">
<body>
@ -8,23 +8,23 @@
Visit
</h2>
<b>Pet</b>
<b>Kid</b>
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Birth Date</th>
<th>Type</th>
<th>Owner</th>
<th>Parent</th>
</tr>
</thead>
<tr>
<td th:text="${pet.name}" /></td>
<td th:text="${kid.name}" /></td>
<td
th:text="${#calendars.format(pet.birthDate, 'yyyy-MM-dd')}" /></td>
<td th:text="${pet.type}" /></td>
th:text="${#calendars.format(kid.birthDate, 'yyyy-MM-dd')}" /></td>
<td th:text="${kid.gender}" /></td>
<td
th:text="${pet.owner?.firstName + ' ' + pet.owner?.lastName}" /></td>
th:text="${kid.parent?.firstName + ' ' + kid.parent?.lastName}" /></td>
</tr>
</table>
@ -38,7 +38,7 @@
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type="hidden" name="petId" th:value="${pet.id}" />
<input type="hidden" name="kidId" th:value="${kid.id}" />
<button class="btn btn-default" type="submit">Add Visit</button>
</div>
</div>
@ -51,7 +51,7 @@
<th>Date</th>
<th>Description</th>
</tr>
<tr th:if="${!visit['new']}" th:each="visit : ${pet.visits}">
<tr th:if="${!visit['new']}" th:each="visit : ${kid.visits}">
<td th:text="${#calendars.format(visit.date, 'yyyy-MM-dd')}" /></td>
<td th:text=" ${visit.description}" /></td>
</tr>

View file

@ -1,10 +1,10 @@
<html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
th:replace="~{fragments/layout :: layout (~{::body},'parents')}">
<body>
<h2>Parent</h2>
<form th:object="${owner}" class="form-horizontal" id="add-owner-form" method="post">
<form th:object="${parent}" class="form-horizontal" id="add-parent-form" method="post">
<div class="form-group has-feedback">
<input
th:replace="~{fragments/inputField :: input ('First Name', 'firstName')}" />
@ -20,7 +20,7 @@
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button
th:with="text=${owner['new']} ? 'Add Owner' : 'Update Owner'"
th:with="text=${parent['new']} ? 'Add Parent' : 'Update Parent'"
class="btn btn-default" type="submit" th:text="${text}">Add
Parent</button>
</div>

View file

@ -1,12 +1,12 @@
<html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
th:replace="~{fragments/layout :: layout (~{::body},'parents')}">
<body>
<h2>Find Parents</h2>
<form th:object="${owner}" th:action="@{/owners}" method="get"
class="form-horizontal" id="search-owner-form">
<form th:object="${parent}" th:action="@{/parents}" method="get"
class="form-horizontal" id="search-parent-form">
<div class="form-group">
<div class="control-group" id="lastName">
<label class="col-sm-2 control-label">Last name </label>
@ -29,7 +29,7 @@
</form>
<br />
<a class="btn btn-default" th:href="@{/owners/new}">Add Parent</a>
<a class="btn btn-default" th:href="@{/parents/new}">Add Parent</a>
</body>
</html>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
th:replace="~{fragments/layout :: layout (~{::body},'parents')}">
<body>
@ -9,7 +9,7 @@
<h2>Parent Information</h2>
<table class="table table-striped" th:object="${owner}">
<table class="table table-striped" th:object="${parents}">
<tr>
<th>Name</th>
<td><b th:text="*{firstName + ' ' + lastName}"></b></td>
@ -28,9 +28,9 @@
</tr>
</table>
<a th:href="@{{id}/edit(id=${owner.id})}" class="btn btn-default">Edit
<a th:href="@{{id}/edit(id=${parent.id})}" class="btn btn-default">Edit
Parent</a>
<a th:href="@{{id}/pets/new(id=${owner.id})}" class="btn btn-default">Add
<a th:href="@{{id}/kids/new(id=${parent.id})}" class="btn btn-default">Add
New Child</a>
<br />
@ -40,16 +40,16 @@
<table class="table table-striped">
<tr th:each="pet : ${owner.pets}">
<tr th:each="kid : ${parent.kids}">
<td valign="top">
<dl class="dl-horizontal">
<dt>Name</dt>
<dd th:text="${pet.name}" /></dd>
<dd th:text="${kid.name}" /></dd>
<dt>Birth Date</dt>
<dd
th:text="${#calendars.format(pet.birthDate, 'yyyy-MM-dd')}" /></dd>
th:text="${#calendars.format(kid.birthDate, 'yyyy-MM-dd')}" /></dd>
<dt>Type</dt>
<dd th:text="${pet.type}" /></dd>
<dd th:text="${kid.gender}" /></dd>
</dl>
</td>
<td valign="top">
@ -60,16 +60,16 @@
<th>Description</th>
</tr>
</thead>
<tr th:each="visit : ${pet.visits}">
<tr th:each="visit : ${kid.visits}">
<td th:text="${#calendars.format(visit.date, 'yyyy-MM-dd')}"></td>
<td th:text="${visit?.description}"></td>
</tr>
<tr>
<td><a
th:href="@{{ownerId}/pets/{petId}/edit(ownerId=${owner.id},petId=${pet.id})}">Edit
th:href="@{{parentId}/kids/{kidId}/edit(parentId=${parent.id},kidId=${kid.id})}">Edit
Child</a></td>
<td><a
th:href="@{{ownerId}/pets/{petId}/visits/new(ownerId=${owner.id},petId=${pet.id})}">Add
th:href="@{{parentId}/kids/{kidId}/visits/new(parentId=${parent.id},kidId=${kid.id})}">Add
Visit</a></td>
</tr>
</table>

View file

@ -1,12 +1,12 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'parents')}">
<body>
<h2>Parents</h2>
<table id="vets" class="table table-striped">
<table id="doctors" class="table table-striped">
<thead>
<tr>
<th style="width: 150px;">Name</th>
@ -17,14 +17,14 @@
</tr>
</thead>
<tbody>
<tr th:each="owner : ${selections}">
<tr th:each="parent : ${selections}">
<td>
<a th:href="@{owners/__${owner.id}__}" th:text="${owner.firstName + ' ' + owner.lastName}"/></a>
<a th:href="@{parents/__${parent.id}__}" th:text="${parent.firstName + ' ' + parent.lastName}"/></a>
</td>
<td th:text="${owner.address}"/>
<td th:text="${owner.city}"/>
<td th:text="${owner.telephone}"/>
<td><span th:each="pet : ${owner.pets}" th:text="${pet.name} + ' '"/></td>
<td th:text="${parent.address}"/>
<td th:text="${parent.city}"/>
<td th:text="${parent.telephone}"/>
<td><span th:each="kid : ${parent.kids}" th:text="${kid.name} + ' '"/></td>
</tr>
</tbody>
</table>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'reviews')}">
<body>
<h2>Reviews</h2>
<p>We are currently curating our review.</p>
</body>
</html>

View file

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'vets')}">
<body>
<h2>Pediatricians</h2>
<table id="vets" class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Specialties</th>
</tr>
</thead>
<tbody>
<tr th:each="vet : ${vets.vetList}">
<td th:text="${vet.firstName + ' ' + vet.lastName}"></td>
<td><span th:each="specialty : ${vet.specialties}"
th:text="${specialty.name + ' '}" /> <span
th:if="${vet.nrOfSpecialties == 0}">none</span></td>
</tr>
</tbody>
</table>
</body>
</html>