diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 000000000..ad1079926 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "src/main/webapp/bower_components" +} diff --git a/.tern-project b/.tern-project new file mode 100644 index 000000000..b5dc2b528 --- /dev/null +++ b/.tern-project @@ -0,0 +1 @@ +{"ide":{"scriptPaths":[{"type":"FOLDER","path":"src/main/webapp/bower_components"}]},"plugins":{"guess-types":{},"node":{},"angular":{}},"libs":["ecma5","browser"]} \ No newline at end of file diff --git a/bower.json b/bower.json new file mode 100644 index 000000000..8a1b71217 --- /dev/null +++ b/bower.json @@ -0,0 +1,23 @@ +{ + "name": "bookstore", + "version": "0.0.0", + "appPath": "src/main/webapp", + "testPath": "src/test/javascript/spec", + "dependencies": { + "bootstrap": "2.3.0", + "jquery": "2.1.3", + "json3": "3.3.2", + "angular": "1.3.11", + "angular-route": "1.3.11", + "angular-resource": "1.3.11" + }, + "devDependencies": { + "angular-mocks": "1.3.11", + "angular-scenario": "1.3.11" + }, + "resolutions": { + "angular": "1.3.11", + "angular-cookies": "1.3.11", + "jquery": "2.1.3" + } +} diff --git a/pom.xml b/pom.xml index fd3b8896a..fefabaeec 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,6 @@ 4.1.6.RELEASE 1.8.0.RELEASE - 1.1.0.RELEASE @@ -57,6 +56,7 @@ 1.1.1 2.7 3.2.0.GA + 2.4.5 @@ -128,19 +128,6 @@ - - org.springframework.data - spring-data-jdbc-core - ${spring-data-jdbc.version} - - - org.springframework - * - - - - - org.springframework spring-jdbc @@ -227,6 +214,12 @@ joda-time ${jodatime.version} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson.datatype.joda.version} + + joda-time joda-time-hibernate @@ -422,7 +415,6 @@ cobertura-maven-plugin ${cobertura.version} - diff --git a/readme.md b/readme.md index a314398a2..039eaff2f 100644 --- a/readme.md +++ b/readme.md @@ -48,7 +48,7 @@ File -> Import -> Maven -> Existing Maven project Java Config branch - Petclinic uses XML configuration by default. In case you'd like to use Java Config instead, there is a Java Config branch available here. Thanks to Antoine Rey for his contribution. + Petclinic uses XML configuration by default. In case you'd like to use Java Config instead, there is a Java Config branch available here. Thanks to Antoine Rey for his contribution. @@ -73,7 +73,7 @@ File -> Import -> Maven -> Existing Maven project webjars declaration inside pom.xml
Resource mapping in Spring configuration
- sample usage in JSP + sample usage in JSP diff --git a/src/main/java/org/springframework/samples/petclinic/model/Pet.java b/src/main/java/org/springframework/samples/petclinic/model/Pet.java index 4bc2b92f7..593798033 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/Pet.java +++ b/src/main/java/org/springframework/samples/petclinic/model/Pet.java @@ -36,6 +36,8 @@ import org.springframework.beans.support.MutableSortDefinition; import org.springframework.beans.support.PropertyComparator; import org.springframework.format.annotation.DateTimeFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; + /** * Simple business object representing a pet. * @@ -58,6 +60,7 @@ public class Pet extends NamedEntity { @ManyToOne @JoinColumn(name = "owner_id") + @JsonIgnore private Owner owner; @OneToMany(cascade = CascadeType.ALL, mappedBy = "pet", fetch = FetchType.EAGER) diff --git a/src/main/java/org/springframework/samples/petclinic/model/Visit.java b/src/main/java/org/springframework/samples/petclinic/model/Visit.java index ea03bde74..181830abc 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/Visit.java +++ b/src/main/java/org/springframework/samples/petclinic/model/Visit.java @@ -26,6 +26,8 @@ import org.hibernate.validator.constraints.NotEmpty; import org.joda.time.DateTime; import org.springframework.format.annotation.DateTimeFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; + /** * Simple JavaBean domain object representing a visit. * @@ -55,6 +57,7 @@ public class Visit extends BaseEntity { */ @ManyToOne @JoinColumn(name = "pet_id") + @JsonIgnore private Pet pet; diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java index 98ae49dba..579de5284 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java @@ -36,6 +36,7 @@ import org.springframework.samples.petclinic.model.PetType; import org.springframework.samples.petclinic.model.Visit; import org.springframework.samples.petclinic.repository.OwnerRepository; import org.springframework.samples.petclinic.repository.VisitRepository; +import org.springframework.samples.petclinic.util.EntityUtils; import org.springframework.stereotype.Repository; /** @@ -51,6 +52,8 @@ import org.springframework.stereotype.Repository; @Repository public class JdbcOwnerRepositoryImpl implements OwnerRepository { + private VisitRepository visitRepository; + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private SimpleJdbcInsert insertOwner; @@ -65,6 +68,7 @@ public class JdbcOwnerRepositoryImpl implements OwnerRepository { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); + this.visitRepository = visitRepository; } @@ -112,12 +116,18 @@ public class JdbcOwnerRepositoryImpl implements OwnerRepository { Map params = new HashMap(); params.put("id", owner.getId().intValue()); final List pets = this.namedParameterJdbcTemplate.query( - "SELECT pets.id, name, birth_date, type_id, owner_id, visits.id, visit_date, description, pet_id FROM pets LEFT OUTER JOIN visits ON pets.id = pet_id WHERE owner_id=:id", + "SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE owner_id=:id", params, - new JdbcPetVisitExtractor() + new JdbcPetRowMapper() ); for (JdbcPet pet : pets) { owner.addPet(pet); + // Pet types have not been loaded at this stage. They are loaded separately + pet.setType(EntityUtils.getById(getPetTypes(), PetType.class, pet.getTypeId())); + List visits = this.visitRepository.findByPetId(pet.getId()); + for (Visit visit : visits) { + pet.addVisit(visit); + } } } diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java index 28151a723..4164f746f 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java @@ -31,7 +31,7 @@ class JdbcPetRowMapper extends BeanPropertyRowMapper { @Override public JdbcPet mapRow(ResultSet rs, int rownum) throws SQLException { JdbcPet pet = new JdbcPet(); - pet.setId(rs.getInt("pets.id")); + pet.setId(rs.getInt("id")); pet.setName(rs.getString("name")); Date birthDate = rs.getDate("birth_date"); pet.setBirthDate(new DateTime(birthDate)); diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetVisitExtractor.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetVisitExtractor.java deleted file mode 100644 index c40786d93..000000000 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetVisitExtractor.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2002-2015 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.petclinic.repository.jdbc; - -import org.springframework.data.jdbc.core.OneToManyResultSetExtractor; -import org.springframework.jdbc.core.ResultSetExtractor; -import org.springframework.samples.petclinic.model.Visit; - -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * {@link ResultSetExtractor} implementation by using the - * {@link OneToManyResultSetExtractor} of Spring Data Core JDBC Extensions. - */ -public class JdbcPetVisitExtractor extends - OneToManyResultSetExtractor { - - public JdbcPetVisitExtractor() { - super(new JdbcPetRowMapper(), new JdbcVisitRowMapper()); - } - - @Override - protected Integer mapPrimaryKey(ResultSet rs) throws SQLException { - return rs.getInt("pets.id"); - } - - @Override - protected Integer mapForeignKey(ResultSet rs) throws SQLException { - if (rs.getObject("visits.pet_id") == null) { - return null; - } else { - return rs.getInt("visits.pet_id"); - } - } - - @Override - protected void addChild(JdbcPet root, Visit child) { - root.addVisit(child); - } -} \ No newline at end of file diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java index 19210574f..b6a004561 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java @@ -15,8 +15,17 @@ */ package org.springframework.samples.petclinic.repository.jdbc; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; +import java.util.List; + +import javax.sql.DataSource; + +import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; @@ -24,9 +33,6 @@ import org.springframework.samples.petclinic.model.Visit; import org.springframework.samples.petclinic.repository.VisitRepository; import org.springframework.stereotype.Repository; -import javax.sql.DataSource; -import java.util.List; - /** * A simple JDBC-based implementation of the {@link VisitRepository} interface. * @@ -84,9 +90,21 @@ public class JdbcVisitRepositoryImpl implements VisitRepository { @Override public List findByPetId(Integer petId) { - return this.jdbcTemplate.query( - "SELECT id as visit_id, visit_date, description FROM visits WHERE pet_id=?", - new JdbcVisitRowMapper(), petId); + final List visits = this.jdbcTemplate.query( + "SELECT id, visit_date, description FROM visits WHERE pet_id=?", + new BeanPropertyRowMapper() { + @Override + public Visit mapRow(ResultSet rs, int row) throws SQLException { + Visit visit = new Visit(); + visit.setId(rs.getInt("id")); + Date visitDate = rs.getDate("visit_date"); + visit.setDate(new DateTime(visitDate)); + visit.setDescription(rs.getString("description")); + return visit; + } + }, + petId); + return visits; } } diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRowMapper.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRowMapper.java deleted file mode 100644 index d6dd0cbf8..000000000 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRowMapper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2002-2015 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.petclinic.repository.jdbc; - - -import org.joda.time.DateTime; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.samples.petclinic.model.Visit; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Date; - -/** - * {@link RowMapper} implementation mapping data from a {@link ResultSet} to the corresponding properties - * of the {@link Visit} class. - */ -class JdbcVisitRowMapper implements RowMapper { - - @Override - public Visit mapRow(ResultSet rs, int row) throws SQLException { - Visit visit = new Visit(); - visit.setId(rs.getInt("visits.id")); - Date visitDate = rs.getDate("visit_date"); - visit.setDate(new DateTime(visitDate)); - visit.setDescription(rs.getString("description")); - return visit; - } -} diff --git a/src/main/java/org/springframework/samples/petclinic/web/OwnerController.java b/src/main/java/org/springframework/samples/petclinic/web/OwnerResource.java similarity index 58% rename from src/main/java/org/springframework/samples/petclinic/web/OwnerController.java rename to src/main/java/org/springframework/samples/petclinic/web/OwnerResource.java index ecbbce559..ad5a3d6dc 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/web/OwnerResource.java @@ -21,9 +21,9 @@ import java.util.Map; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.samples.petclinic.model.Owner; import org.springframework.samples.petclinic.service.ClinicService; -import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; @@ -31,7 +31,8 @@ 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.bind.annotation.SessionAttributes; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.support.SessionStatus; import org.springframework.web.servlet.ModelAndView; @@ -41,15 +42,14 @@ import org.springframework.web.servlet.ModelAndView; * @author Arjen Poutsma * @author Michael Isvy */ -@Controller -@SessionAttributes(types = Owner.class) -public class OwnerController { +@RestController +public class OwnerResource { private final ClinicService clinicService; @Autowired - public OwnerController(ClinicService clinicService) { + public OwnerResource(ClinicService clinicService) { this.clinicService = clinicService; } @@ -58,72 +58,56 @@ public class OwnerController { dataBinder.setDisallowedFields("id"); } - @RequestMapping(value = "/owners/new", method = RequestMethod.GET) + @RequestMapping(value = "/owner/new", method = RequestMethod.GET) public String initCreationForm(Map model) { Owner owner = new Owner(); model.put("owner", owner); - return "owners/createOrUpdateOwnerForm"; + return "owner/createOrUpdateOwnerForm"; } - @RequestMapping(value = "/owners/new", method = RequestMethod.POST) + @RequestMapping(value = "/owner/new", method = RequestMethod.POST) public String processCreationForm(@Valid Owner owner, BindingResult result, SessionStatus status) { if (result.hasErrors()) { - return "owners/createOrUpdateOwnerForm"; + return "owner/createOrUpdateOwnerForm"; } else { this.clinicService.saveOwner(owner); status.setComplete(); - return "redirect:/owners/" + owner.getId(); + return "redirect:/owner/" + owner.getId(); } } - @RequestMapping(value = "/owners/find", method = RequestMethod.GET) - public String initFindForm(Map model) { - model.put("owner", new Owner()); - return "owners/findOwners"; - } + @RequestMapping(value = "/owner/list", method = RequestMethod.GET, + produces = MediaType.APPLICATION_JSON_VALUE) + public Collection findOwners(@RequestParam("lastName") String ownerLastName) { - @RequestMapping(value = "/owners", method = RequestMethod.GET) - public String processFindForm(Owner owner, BindingResult result, Map 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 results = this.clinicService.findOwnerByLastName(owner.getLastName()); + if (ownerLastName == null) { + ownerLastName = ""; + } + + Collection results = this.clinicService.findOwnerByLastName(ownerLastName); 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(); + return null; } else { - // multiple owners found - model.put("selections", results); - return "owners/ownersList"; + return results; } } - @RequestMapping(value = "/owners/{ownerId}/edit", method = RequestMethod.GET) + @RequestMapping(value = "/owner/{ownerId}/edit", method = RequestMethod.GET) public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) { Owner owner = this.clinicService.findOwnerById(ownerId); model.addAttribute(owner); - return "owners/createOrUpdateOwnerForm"; + return "owner/createOrUpdateOwnerForm"; } - @RequestMapping(value = "/owners/{ownerId}/edit", method = RequestMethod.PUT) + @RequestMapping(value = "/owner/{ownerId}/edit", method = RequestMethod.PUT) public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, SessionStatus status) { if (result.hasErrors()) { return "owners/createOrUpdateOwnerForm"; } else { this.clinicService.saveOwner(owner); status.setComplete(); - return "redirect:/owners/{ownerId}"; + return "redirect:/owner/{ownerId}"; } } @@ -133,11 +117,10 @@ public class OwnerController { * @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.clinicService.findOwnerById(ownerId)); - return mav; + @RequestMapping(value = "/owner/{ownerId}", method = RequestMethod.GET, + produces = MediaType.APPLICATION_JSON_VALUE) + public Owner showOwner(@PathVariable("ownerId") int ownerId) { + return this.clinicService.findOwnerById(ownerId); } } diff --git a/src/main/java/org/springframework/samples/petclinic/web/VetController.java b/src/main/java/org/springframework/samples/petclinic/web/VetResource.java similarity index 50% rename from src/main/java/org/springframework/samples/petclinic/web/VetController.java rename to src/main/java/org/springframework/samples/petclinic/web/VetResource.java index ebbb8d6e2..968c4d259 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/web/VetResource.java @@ -15,14 +15,15 @@ */ package org.springframework.samples.petclinic.web; -import java.util.Map; +import java.util.Collection; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.samples.petclinic.model.Vets; +import org.springframework.http.MediaType; +import org.springframework.samples.petclinic.model.Vet; import org.springframework.samples.petclinic.service.ClinicService; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; /** * @author Juergen Hoeller @@ -30,34 +31,21 @@ import org.springframework.web.bind.annotation.ResponseBody; * @author Ken Krebs * @author Arjen Poutsma */ -@Controller -public class VetController { +@RestController +public class VetResource { private final ClinicService clinicService; @Autowired - public VetController(ClinicService clinicService) { + public VetResource(ClinicService clinicService) { this.clinicService = clinicService; } - - @RequestMapping(value={"/vets.xml","/vets.html"}) - public String showVetList(Map model) { - // Here we are returning an object of type 'Vets' rather than a collection of Vet objects - // so it is simpler for Object-Xml mapping - Vets vets = new Vets(); - vets.getVetList().addAll(this.clinicService.findVets()); - model.put("vets", vets); - return "vets/vetList"; - } - @RequestMapping("/vets.json") - public @ResponseBody Vets showResourcesVetList() { - // Here we are returning an object of type 'Vets' rather than a collection of Vet objects - // so it is simpler for JSon/Object mapping - Vets vets = new Vets(); - vets.getVetList().addAll(this.clinicService.findVets()); - return vets; + @RequestMapping(value="/vets", method = RequestMethod.GET, + produces = MediaType.APPLICATION_JSON_VALUE) + public Collection showResourcesVetList() { + return this.clinicService.findVets(); } diff --git a/src/main/resources/spring/mvc-core-config.xml b/src/main/resources/spring/mvc-core-config.xml index eab2062dc..ceb7d99ca 100644 --- a/src/main/resources/spring/mvc-core-config.xml +++ b/src/main/resources/spring/mvc-core-config.xml @@ -22,7 +22,15 @@ - + + + + + + + + + diff --git a/src/main/resources/spring/mvc-view-config.xml b/src/main/resources/spring/mvc-view-config.xml index 309707cf1..190005f34 100644 --- a/src/main/resources/spring/mvc-view-config.xml +++ b/src/main/resources/spring/mvc-view-config.xml @@ -7,34 +7,9 @@ xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - + - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 207a5b727..583b119f5 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -2,7 +2,7 @@ <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - +