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; import org.springframework.boot.autoconfigure.SpringBootApplication;
/** /**
* PetClinic Spring Boot Application. * KidClinic Spring Boot Application.
* *
* @author Dave Syer * @author Dave Syer
* *
*/ */
@SpringBootApplication @SpringBootApplication
public class PetClinicApplication { public class KidClinicApplication {
public static void main(String[] args) throws Exception { 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.vet; package org.springframework.samples.kidclinic.doctor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -42,11 +42,11 @@ import org.springframework.samples.kidclinic.model.Person;
* @author Arjen Poutsma * @author Arjen Poutsma
*/ */
@Entity @Entity
@Table(name = "vets") @Table(name = "doctors")
public class Vet extends Person { public class Doctor extends Person {
@ManyToMany(fetch = FetchType.EAGER) @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; private Set<Specialty> specialties;
protected Set<Specialty> getSpecialtiesInternal() { protected Set<Specialty> getSpecialtiesInternal() {

View file

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

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.vet; package org.springframework.samples.kidclinic.doctor;
import java.util.Collection; import java.util.Collection;
@ -23,7 +23,7 @@ import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional; 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 * 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 * @author Ken Krebs
@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
* @author Sam Brannen * @author Sam Brannen
* @author Michael Isvy * @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) @Transactional(readOnly = true)
@Cacheable("vets") @Cacheable("doctors")
Collection<Vet> findAll() throws DataAccessException; Collection<Doctor> findAll() throws DataAccessException;
} }

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.vet; package org.springframework.samples.kidclinic.doctor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -22,22 +22,22 @@ import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement; 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}. * org.springframework.web.servlet.view.xml.MarshallingView}.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
*/ */
@XmlRootElement @XmlRootElement
public class Vets { public class Doctors {
private List<Vet> vets; private List<Doctor> doctors;
@XmlElement @XmlElement
public List<Vet> getVetList() { public List<Doctor> getDoctorList() {
if (vets == null) { if (doctors == null) {
vets = new ArrayList<>(); doctors = new ArrayList<>();
} }
return vets; return doctors;
} }
} }

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.vet; package org.springframework.samples.kidclinic.doctor;
import java.io.Serializable; import java.io.Serializable;
@ -23,7 +23,7 @@ import javax.persistence.Table;
import org.springframework.samples.kidclinic.model.NamedEntity; 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 * @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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.owner; package org.springframework.samples.kidclinic.parent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -48,8 +48,8 @@ import org.springframework.samples.kidclinic.visit.Visit;
* @author Sam Brannen * @author Sam Brannen
*/ */
@Entity @Entity
@Table(name = "pets") @Table(name = "kids")
public class Pet extends NamedEntity { public class Kid extends NamedEntity {
@Column(name = "birth_date") @Column(name = "birth_date")
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@ -57,14 +57,14 @@ public class Pet extends NamedEntity {
private Date birthDate; private Date birthDate;
@ManyToOne @ManyToOne
@JoinColumn(name = "type_id") @JoinColumn(name = "gender_id")
private PetType type; private KidGender gender;
@ManyToOne @ManyToOne
@JoinColumn(name = "owner_id") @JoinColumn(name = "parent_id")
private Owner owner; 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<>(); private Set<Visit> visits = new LinkedHashSet<>();
public void setBirthDate(Date birthDate) { public void setBirthDate(Date birthDate) {
@ -75,20 +75,20 @@ public class Pet extends NamedEntity {
return this.birthDate; return this.birthDate;
} }
public PetType getType() { public KidGender getGender() {
return this.type; return this.gender;
} }
public void setType(PetType type) { public void setGender(KidGender gender) {
this.type = type; this.gender = gender;
} }
public Owner getOwner() { public Parent getParent() {
return this.owner; return this.parent;
} }
protected void setOwner(Owner owner) { protected void setParent(Parent parent) {
this.owner = owner; this.parent = parent;
} }
protected Set<Visit> getVisitsInternal() { protected Set<Visit> getVisitsInternal() {
@ -111,7 +111,7 @@ public class Pet extends NamedEntity {
public void addVisit(Visit visit) { public void addVisit(Visit visit) {
getVisitsInternal().add(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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.owner; package org.springframework.samples.kidclinic.parent;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
@ -25,7 +25,7 @@ import org.springframework.samples.kidclinic.model.NamedEntity;
* Can be Cat, Dog, Hamster... * Can be Cat, Dog, Hamster...
*/ */
@Entity @Entity
@Table(name = "types") @Table(name = "gender")
public class PetType extends NamedEntity { public class KidGender extends NamedEntity {
} }

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.owner; package org.springframework.samples.kidclinic.parent;
import java.text.ParseException; import java.text.ParseException;
@ -25,7 +25,7 @@ import org.springframework.format.Formatter;
import org.springframework.stereotype.Component; 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 * 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 * 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/ * - 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 * @author Michael Isvy
*/ */
@Component @Component
public class PetTypeFormatter implements Formatter<PetType> { public class KidGenderFormatter implements Formatter<KidGender> {
private final PetRepository pets; private final KidRepository kids;
@Autowired @Autowired
public PetTypeFormatter(PetRepository pets) { public KidGenderFormatter(KidRepository kids) {
this.pets = pets; this.kids = kids;
} }
@Override @Override
public String print(PetType petType, Locale locale) { public String print(KidGender kidGender, Locale locale) {
return petType.getName(); return kidGender.getName();
} }
@Override @Override
public PetType parse(String text, Locale locale) throws ParseException { public KidGender parse(String text, Locale locale) throws ParseException {
Collection<PetType> findPetTypes = this.pets.findPetTypes(); Collection<KidGender> findKidGenders = this.kids.findKidGenders();
for (PetType type : findPetTypes) { for (KidGender gender : findKidGenders) {
if (type.getName().equals(text)) { if (gender.getName().equals(text)) {
return type; 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.owner; package org.springframework.samples.kidclinic.parent;
import java.util.List; import java.util.List;
@ -22,7 +22,7 @@ import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional; 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 * 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 * @author Ken Krebs
@ -30,29 +30,29 @@ import org.springframework.transaction.annotation.Transactional;
* @author Sam Brannen * @author Sam Brannen
* @author Michael Isvy * @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. * Retrieve all {@link KidGender}s from the data store.
* @return a Collection of {@link PetType}s. * @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) @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 * @param id the id to search for
* @return the {@link Pet} if found * @return the {@link Kid} if found
*/ */
@Transactional(readOnly = true) @Transactional(readOnly = true)
Pet findById(Integer id); Kid findById(Integer id);
/** /**
* Save a {@link Pet} to the data store, either inserting or updating it. * Save a {@link Kid} to the data store, either inserting or updating it.
* @param pet the {@link Pet} to save * @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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.owner; package org.springframework.samples.kidclinic.parent;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.validation.Validator; import org.springframework.validation.Validator;
/** /**
* <code>Validator</code> for <code>Pet</code> forms. * <code>Validator</code> for <code>Kid</code> forms.
* <p> * <p>
* We're not using Bean Validation annotations here because it is easier to define such validation rule in Java. * We're not using Bean Validation annotations here because it is easier to define such validation rule in Java.
* </p> * </p>
@ -28,36 +28,36 @@ import org.springframework.validation.Validator;
* @author Ken Krebs * @author Ken Krebs
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
public class PetValidator implements Validator { public class KidValidator implements Validator {
private static final String REQUIRED = "required"; private static final String REQUIRED = "required";
@Override @Override
public void validate(Object obj, Errors errors) { public void validate(Object obj, Errors errors) {
Pet pet = (Pet) obj; Kid kid = (Kid) obj;
String name = pet.getName(); String name = kid.getName();
// name validation // name validation
if (!StringUtils.hasLength(name)) { if (!StringUtils.hasLength(name)) {
errors.rejectValue("name", REQUIRED, REQUIRED); errors.rejectValue("name", REQUIRED, REQUIRED);
} }
// type validation // gender validation
if (pet.isNew() && pet.getType() == null) { if (kid.isNew() && kid.getGender() == null) {
errors.rejectValue("type", REQUIRED, REQUIRED); errors.rejectValue("gender", REQUIRED, REQUIRED);
} }
// birth date validation // birth date validation
if (pet.getBirthDate() == null) { if (kid.getBirthDate() == null) {
errors.rejectValue("birthDate", REQUIRED, REQUIRED); errors.rejectValue("birthDate", REQUIRED, REQUIRED);
} }
} }
/** /**
* This Validator validates *just* Pet instances * This Validator validates *just* Kid instances
*/ */
@Override @Override
public boolean supports(Class<?> clazz) { 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.owner; package org.springframework.samples.kidclinic.parent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -35,7 +35,7 @@ import org.springframework.core.style.ToStringCreator;
import org.springframework.samples.kidclinic.model.Person; 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 Ken Krebs
* @author Juergen Hoeller * @author Juergen Hoeller
@ -43,8 +43,8 @@ import org.springframework.samples.kidclinic.model.Person;
* @author Michael Isvy * @author Michael Isvy
*/ */
@Entity @Entity
@Table(name = "owners") @Table(name = "parents")
public class Owner extends Person { public class Parent extends Person {
@Column(name = "address") @Column(name = "address")
@NotEmpty @NotEmpty
private String address; private String address;
@ -58,8 +58,8 @@ public class Owner extends Person {
@Digits(fraction = 0, integer = 10) @Digits(fraction = 0, integer = 10)
private String telephone; private String telephone;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "owner") @OneToMany(cascade = CascadeType.ALL, mappedBy = "parent")
private Set<Pet> pets; private Set<Kid> kids;
public String getAddress() { public String getAddress() {
@ -86,54 +86,54 @@ public class Owner extends Person {
this.telephone = telephone; this.telephone = telephone;
} }
protected Set<Pet> getPetsInternal() { protected Set<Kid> getKidsInternal() {
if (this.pets == null) { if (this.kids == null) {
this.pets = new HashSet<>(); this.kids = new HashSet<>();
} }
return this.pets; return this.kids;
} }
protected void setPetsInternal(Set<Pet> pets) { protected void setKidsInternal(Set<Kid> kids) {
this.pets = pets; this.kids = kids;
} }
public List<Pet> getPets() { public List<Kid> getKids() {
List<Pet> sortedPets = new ArrayList<>(getPetsInternal()); List<Kid> sortedKids = new ArrayList<>(getKidsInternal());
PropertyComparator.sort(sortedPets, new MutableSortDefinition("name", true, true)); PropertyComparator.sort(sortedKids, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedPets); return Collections.unmodifiableList(sortedKids);
} }
public void addPet(Pet pet) { public void addKid(Kid kid) {
if (pet.isNew()) { if (kid.isNew()) {
getPetsInternal().add(pet); 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 * @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) { public Kid getKid(String name) {
return getPet(name, false); 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 * @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(); name = name.toLowerCase();
for (Pet pet : getPetsInternal()) { for (Kid kid : getKidsInternal()) {
if (!ignoreNew || !pet.isNew()) { if (!ignoreNew || !kid.isNew()) {
String compName = pet.getName(); String compName = kid.getName();
compName = compName.toLowerCase(); compName = compName.toLowerCase();
if (compName.equals(name)) { 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.owner; package org.springframework.samples.kidclinic.parent;
import java.util.Collection; import java.util.Collection;
@ -23,7 +23,7 @@ import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; 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 * 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 * @author Ken Krebs
@ -31,33 +31,33 @@ import org.springframework.transaction.annotation.Transactional;
* @author Sam Brannen * @author Sam Brannen
* @author Michael Isvy * @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. * whose last name <i>starts</i> with the given name.
* @param lastName Value to search for * @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) * 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) @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 * @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) @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. * Save an {@link Parent} to the data store, either inserting or updating it.
* @param owner the {@link Owner} to save * @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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.samples.kidclinic.owner; package org.springframework.samples.kidclinic.parent;
import java.util.Map; import java.util.Map;
@ -42,13 +42,13 @@ import org.springframework.web.bind.annotation.RequestMethod;
class VisitController { class VisitController {
private final VisitRepository visits; private final VisitRepository visits;
private final PetRepository pets; private final KidRepository kids;
@Autowired @Autowired
public VisitController(VisitRepository visits, PetRepository pets) { public VisitController(VisitRepository visits, KidRepository kids) {
this.visits = visits; this.visits = visits;
this.pets = pets; this.kids = kids;
} }
@InitBinder @InitBinder
@ -60,35 +60,35 @@ class VisitController {
* Called before each and every @RequestMapping annotated method. * Called before each and every @RequestMapping annotated method.
* 2 goals: * 2 goals:
* - Make sure we always have fresh data * - 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) * (Even though id is not part of the form fields)
* *
* @param petId * @param kidId
* @return Pet * @return Kid
*/ */
@ModelAttribute("visit") @ModelAttribute("visit")
public Visit loadPetWithVisit(@PathVariable("petId") int petId, Map<String, Object> model) { public Visit loadKidWithVisit(@PathVariable("kidId") int kidId, Map<String, Object> model) {
Pet pet = this.pets.findById(petId); Kid kid = this.kids.findById(kidId);
model.put("pet", pet); model.put("kid", kid);
Visit visit = new Visit(); Visit visit = new Visit();
pet.addVisit(visit); kid.addVisit(visit);
return visit; return visit;
} }
// Spring MVC calls method loadPetWithVisit(...) before initNewVisitForm is called // Spring MVC calls method loadKidWithVisit(...) before initNewVisitForm is called
@RequestMapping(value = "/owners/*/pets/{petId}/visits/new", method = RequestMethod.GET) @RequestMapping(value = "/parents/*/kids/{kidId}/visits/new", method = RequestMethod.GET)
public String initNewVisitForm(@PathVariable("petId") int petId, Map<String, Object> model) { public String initNewVisitForm(@PathVariable("kidId") int kidId, Map<String, Object> model) {
return "pets/createOrUpdateVisitForm"; return "kids/createOrUpdateVisitForm";
} }
// Spring MVC calls method loadPetWithVisit(...) before processNewVisitForm is called // Spring MVC calls method loadKidWithVisit(...) before processNewVisitForm is called
@RequestMapping(value = "/owners/{ownerId}/pets/{petId}/visits/new", method = RequestMethod.POST) @RequestMapping(value = "/parents/{parentId}/kids/{kidId}/visits/new", method = RequestMethod.POST)
public String processNewVisitForm(@Valid Visit visit, BindingResult result) { public String processNewVisitForm(@Valid Visit visit, BindingResult result) {
if (result.hasErrors()) { if (result.hasErrors()) {
return "pets/createOrUpdateVisitForm"; return "kids/createOrUpdateVisitForm";
} else { } else {
this.visits.save(visit); this.visits.save(visit);
return "redirect:/owners/{ownerId}"; return "redirect:/parents/{parentId}";
} }
} }

View file

@ -20,7 +20,7 @@ class CacheConfig {
public JCacheManagerCustomizer cacheManagerCustomizer() { public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> { return cm -> {
Configuration<Object, Object> cacheConfiguration = createCacheConfiguration(); 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 @Controller
class CrashController { class CrashController {
@RequestMapping(value = "/oups", method = RequestMethod.GET) @RequestMapping(value = "/oops", method = RequestMethod.GET)
public String triggerException() { public String triggerException() {
throw new RuntimeException( throw new RuntimeException(
"Expected: controller used to showcase what " + "happens when an exception is thrown"); "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. * Holds value of property pet.
*/ */
@Column(name = "pet_id") @Column(name = "kid_id")
private Integer petId; 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() { public Integer getKidId() {
return this.petId; 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) { public void setKidId(Integer kidId) {
this.petId = petId; this.kidId = kidId;
} }
} }

View file

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

View file

@ -1,18 +1,18 @@
DROP TABLE vet_specialties IF EXISTS; DROP TABLE doctor_specialties IF EXISTS;
DROP TABLE vets IF EXISTS; DROP TABLE doctors IF EXISTS;
DROP TABLE specialties IF EXISTS; DROP TABLE specialties IF EXISTS;
DROP TABLE visits IF EXISTS; DROP TABLE visits IF EXISTS;
DROP TABLE pets IF EXISTS; DROP TABLE kids IF EXISTS;
DROP TABLE types IF EXISTS; DROP TABLE gender IF EXISTS;
DROP TABLE owners IF EXISTS; DROP TABLE parents IF EXISTS;
CREATE TABLE vets ( CREATE TABLE doctors (
id INTEGER IDENTITY PRIMARY KEY, id INTEGER IDENTITY PRIMARY KEY,
first_name VARCHAR(30), first_name VARCHAR(30),
last_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 ( CREATE TABLE specialties (
id INTEGER IDENTITY PRIMARY KEY, id INTEGER IDENTITY PRIMARY KEY,
@ -20,20 +20,20 @@ CREATE TABLE specialties (
); );
CREATE INDEX specialties_name ON specialties (name); CREATE INDEX specialties_name ON specialties (name);
CREATE TABLE vet_specialties ( CREATE TABLE doctor_specialties (
vet_id INTEGER NOT NULL, doctor_id INTEGER NOT NULL,
specialty_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 doctor_specialties ADD CONSTRAINT fk_doctor_specialties_doctors FOREIGN KEY (doctor_id) REFERENCES doctors (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_specialties FOREIGN KEY (specialty_id) REFERENCES specialties (id);
CREATE TABLE types ( CREATE TABLE gender (
id INTEGER IDENTITY PRIMARY KEY, id INTEGER IDENTITY PRIMARY KEY,
name VARCHAR(80) 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, id INTEGER IDENTITY PRIMARY KEY,
first_name VARCHAR(30), first_name VARCHAR(30),
last_name VARCHAR_IGNORECASE(30), last_name VARCHAR_IGNORECASE(30),
@ -41,24 +41,24 @@ CREATE TABLE owners (
city VARCHAR(80), city VARCHAR(80),
telephone VARCHAR(20) 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, id INTEGER IDENTITY PRIMARY KEY,
name VARCHAR(30), name VARCHAR(30),
birth_date DATE, birth_date DATE,
type_id INTEGER NOT NULL, 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 kids ADD CONSTRAINT fk_kids_parents FOREIGN KEY (parent_id) REFERENCES parents (id);
ALTER TABLE pets ADD CONSTRAINT fk_pets_types FOREIGN KEY (type_id) REFERENCES types (id); ALTER TABLE kids ADD CONSTRAINT fk_kids_gender FOREIGN KEY (type_id) REFERENCES gender (id);
CREATE INDEX pets_name ON pets (name); CREATE INDEX kids_name ON kids (name);
CREATE TABLE visits ( CREATE TABLE visits (
id INTEGER IDENTITY PRIMARY KEY, id INTEGER IDENTITY PRIMARY KEY,
pet_id INTEGER NOT NULL, kid_id INTEGER NOT NULL,
visit_date DATE, visit_date DATE,
description VARCHAR(255) description VARCHAR(255)
); );
ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id); ALTER TABLE visits ADD CONSTRAINT fk_visits_kids FOREIGN KEY (kid_id) REFERENCES kids (id);
CREATE INDEX visits_pet_id ON visits (pet_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')}"> <html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'error')}">
<body> <body>
<h2>Reviews</h2> <h2>Errors</h2>
<p>We are currently curating our review.</p> <p th:text="${message}">Exception message</p>
</body> </body>
</html> </html>

View file

@ -10,7 +10,7 @@
<link rel="shortcut icon" type="image/x-icon" th:href="@{/resources/images/favicon.png}"> <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]> <!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <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> <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: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" <html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'owners')}"> th:replace="~{fragments/layout :: layout (~{::body},'parents')}">
<body> <body>
<h2> <h2>
<th:block th:if="${pet['new']}">New </th:block> <th:block th:if="${kid['new']}">New </th:block>
Pet Kid
</h2> </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}" /> <input type="hidden" name="id" th:value="*{id}" />
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<div class="form-group"> <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"> <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>
</div> </div>
<input <input
@ -26,9 +26,9 @@
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<button <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 class="btn btn-default" type="submit" th:text="${text}">Add
Pet</button> Kid</button>
</div> </div>
</div> </div>
</form> </form>

View file

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

View file

@ -1,10 +1,10 @@
<html xmlns:th="http://www.thymeleaf.org" <html xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'owners')}"> th:replace="~{fragments/layout :: layout (~{::body},'parents')}">
<body> <body>
<h2>Parent</h2> <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"> <div class="form-group has-feedback">
<input <input
th:replace="~{fragments/inputField :: input ('First Name', 'firstName')}" /> th:replace="~{fragments/inputField :: input ('First Name', 'firstName')}" />
@ -20,7 +20,7 @@
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<button <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 class="btn btn-default" type="submit" th:text="${text}">Add
Parent</button> Parent</button>
</div> </div>

View file

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

View file

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

View file

@ -1,12 +1,12 @@
<!DOCTYPE html> <!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> <body>
<h2>Parents</h2> <h2>Parents</h2>
<table id="vets" class="table table-striped"> <table id="doctors" class="table table-striped">
<thead> <thead>
<tr> <tr>
<th style="width: 150px;">Name</th> <th style="width: 150px;">Name</th>
@ -17,14 +17,14 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr th:each="owner : ${selections}"> <tr th:each="parent : ${selections}">
<td> <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>
<td th:text="${owner.address}"/> <td th:text="${parent.address}"/>
<td th:text="${owner.city}"/> <td th:text="${parent.city}"/>
<td th:text="${owner.telephone}"/> <td th:text="${parent.telephone}"/>
<td><span th:each="pet : ${owner.pets}" th:text="${pet.name} + ' '"/></td> <td><span th:each="kid : ${parent.kids}" th:text="${kid.name} + ' '"/></td>
</tr> </tr>
</tbody> </tbody>
</table> </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>