diff --git a/db/build.xml b/db/build.xml
new file mode 100644
index 000000000..b6d09936b
--- /dev/null
+++ b/db/build.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/db/dropTables.txt b/db/dropTables.txt
new file mode 100644
index 000000000..90ae6329f
--- /dev/null
+++ b/db/dropTables.txt
@@ -0,0 +1,7 @@
+DROP TABLE visits;
+DROP TABLE pets;
+DROP TABLE owners;
+DROP TABLE types;
+DROP TABLE vet_specialties;
+DROP TABLE specialties;
+DROP TABLE vets;
diff --git a/db/emptyDB.txt b/db/emptyDB.txt
new file mode 100644
index 000000000..d5dd8728c
--- /dev/null
+++ b/db/emptyDB.txt
@@ -0,0 +1,7 @@
+DELETE FROM vets;
+DELETE FROM specialties;
+DELETE FROM vet_specialties;
+DELETE FROM types;
+DELETE FROM owners;
+DELETE FROM pets;
+DELETE FROM visits;
diff --git a/db/mysql/createDB.txt b/db/mysql/createDB.txt
new file mode 100644
index 000000000..5b4b3859e
--- /dev/null
+++ b/db/mysql/createDB.txt
@@ -0,0 +1,3 @@
+CREATE DATABASE petclinic;
+
+GRANT ALL PRIVILEGES ON petclinic.* TO pc@localhost IDENTIFIED BY 'pc';
\ No newline at end of file
diff --git a/db/mysql/dropDB.txt b/db/mysql/dropDB.txt
new file mode 100644
index 000000000..e1209da0e
--- /dev/null
+++ b/db/mysql/dropDB.txt
@@ -0,0 +1 @@
+DROP DATABASE petclinic;
diff --git a/db/mysql/initDB.txt b/db/mysql/initDB.txt
new file mode 100644
index 000000000..0007ee3a3
--- /dev/null
+++ b/db/mysql/initDB.txt
@@ -0,0 +1,58 @@
+USE petclinic;
+
+CREATE TABLE vets (
+ id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ first_name VARCHAR(30),
+ last_name VARCHAR(30),
+ INDEX(last_name)
+) engine=InnoDB;
+
+CREATE TABLE specialties (
+ id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(80),
+ INDEX(name)
+) engine=InnoDB;
+
+CREATE TABLE vet_specialties (
+ vet_id INT(4) UNSIGNED NOT NULL,
+ specialty_id INT(4) UNSIGNED NOT NULL
+) engine=InnoDB;
+ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_vets FOREIGN KEY (vet_id) REFERENCES vets(id);
+ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_specialties FOREIGN KEY (specialty_id) REFERENCES specialties(id);
+
+CREATE TABLE types (
+ id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(80),
+ INDEX(name)
+) engine=InnoDB;
+
+CREATE TABLE owners (
+ id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ first_name VARCHAR(30),
+ last_name VARCHAR(30),
+ address VARCHAR(255),
+ city VARCHAR(80),
+ telephone VARCHAR(20),
+ INDEX(last_name)
+) engine=InnoDB;
+
+CREATE TABLE pets (
+ id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(30),
+ birth_date DATE,
+ type_id INT(4) UNSIGNED NOT NULL,
+ owner_id INT(4) UNSIGNED NOT NULL,
+ INDEX(name)
+) engine=InnoDB;
+ALTER TABLE pets ADD CONSTRAINT fk_pets_owners FOREIGN KEY (owner_id) REFERENCES owners(id);
+ALTER TABLE pets ADD CONSTRAINT fk_pets_types FOREIGN KEY (type_id) REFERENCES types(id);
+CREATE INDEX pets_name ON pets(name);
+
+CREATE TABLE visits (
+ id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ pet_id INT(4) UNSIGNED NOT NULL,
+ visit_date DATE,
+ description VARCHAR(255),
+ INDEX(pet_id)
+) engine=InnoDB;
+ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets(id);
diff --git a/db/mysql/petclinic_db_setup_mysql.txt b/db/mysql/petclinic_db_setup_mysql.txt
new file mode 100644
index 000000000..66727e190
--- /dev/null
+++ b/db/mysql/petclinic_db_setup_mysql.txt
@@ -0,0 +1,22 @@
+================================================================================
+=== Spring PetClinic sample application - MySQL Configuration ===
+================================================================================
+
+@author Sam Brannen
+
+--------------------------------------------------------------------------------
+
+1) Download and install the MySQL database (e.g., MySQL Community Server 5.1.x),
+ which can be found here: http://dev.mysql.com/downloads/
+
+2) Download Connector/J, the MySQL JDBC driver (e.g., Connector/J 5.1.x), which
+ can be found here: http://dev.mysql.com/downloads/connector/j/
+ Copy the Connector/J JAR file (e.g., mysql-connector-java-5.1.5-bin.jar) into
+ the db/mysql directory.
+
+3) Create the PetClinic database and user by executing the "db/mysql/createDB.txt"
+ script.
+
+4) Open "src/main/resources/jdbc.properties"; comment out all properties in the
+ "HSQL Settings" section; uncomment all properties in the "MySQL Settings"
+ section.
\ No newline at end of file
diff --git a/db/mysql/petclinic_tomcat_mysql.xml b/db/mysql/petclinic_tomcat_mysql.xml
new file mode 100644
index 000000000..d1c5a3b04
--- /dev/null
+++ b/db/mysql/petclinic_tomcat_mysql.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+ factory
+ org.apache.commons.dbcp.BasicDataSourceFactory
+
+
+
+ driverClassName
+ org.gjt.mm.mysql.Driver
+
+
+
+ url
+ jdbc:mysql://localhost:3306/petclinic?autoReconnect=true
+
+
+ username
+ pc
+
+
+ password
+ pc
+
+
+
+ maxActive
+ 50
+
+
+ maxIdle
+ 10
+
+
+ maxWait
+ 10000
+
+
+ removeAbandoned
+ true
+
+
+ removeAbandonedTimeout
+ 60
+
+
+ logAbandoned
+ true
+
+
+
+
+
diff --git a/db/petclinic_tomcat_all.xml b/db/petclinic_tomcat_all.xml
new file mode 100644
index 000000000..ed45c5cd3
--- /dev/null
+++ b/db/petclinic_tomcat_all.xml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+ factory
+ org.apache.commons.dbcp.BasicDataSourceFactory
+
+
+
+ driverClassName
+ org.hsqldb.jdbcDriver
+
+
+ url
+ jdbc:hsqldb:hsql://localhost:9001
+
+
+ username
+ sa
+
+
+
+ maxActive
+ 50
+
+
+ maxIdle
+ 10
+
+
+ maxWait
+ 10000
+
+
+ removeAbandoned
+ true
+
+
+ removeAbandonedTimeout
+ 60
+
+
+ logAbandoned
+ true
+
+
+
+
+
+
+
+ factory
+ org.apache.commons.dbcp.BasicDataSourceFactory
+
+
+
+ driverClassName
+ org.gjt.mm.mysql.Driver
+
+
+
+ url
+ jdbc:mysql://localhost:3306/petclinic?autoReconnect=true
+
+
+ username
+ pc
+
+
+ password
+ pc
+
+
+
+ maxActive
+ 50
+
+
+ maxIdle
+ 10
+
+
+ maxWait
+ 10000
+
+
+ removeAbandoned
+ true
+
+
+ removeAbandonedTimeout
+ 60
+
+
+ logAbandoned
+ true
+
+
+
+
+
diff --git a/db/populateDB.txt b/db/populateDB.txt
new file mode 100644
index 000000000..1bf0c4a6e
--- /dev/null
+++ b/db/populateDB.txt
@@ -0,0 +1,53 @@
+INSERT INTO vets VALUES (1, 'James', 'Carter');
+INSERT INTO vets VALUES (2, 'Helen', 'Leary');
+INSERT INTO vets VALUES (3, 'Linda', 'Douglas');
+INSERT INTO vets VALUES (4, 'Rafael', 'Ortega');
+INSERT INTO vets VALUES (5, 'Henry', 'Stevens');
+INSERT INTO vets VALUES (6, 'Sharon', 'Jenkins');
+
+INSERT INTO specialties VALUES (1, 'radiology');
+INSERT INTO specialties VALUES (2, 'surgery');
+INSERT INTO specialties VALUES (3, 'dentistry');
+
+INSERT INTO vet_specialties VALUES (2, 1);
+INSERT INTO vet_specialties VALUES (3, 2);
+INSERT INTO vet_specialties VALUES (3, 3);
+INSERT INTO vet_specialties VALUES (4, 2);
+INSERT INTO vet_specialties VALUES (5, 1);
+
+INSERT INTO types VALUES (1, 'cat');
+INSERT INTO types VALUES (2, 'dog');
+INSERT INTO types VALUES (3, 'lizard');
+INSERT INTO types VALUES (4, 'snake');
+INSERT INTO types VALUES (5, 'bird');
+INSERT INTO types VALUES (6, 'hamster');
+
+INSERT INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
+INSERT INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');
+INSERT INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');
+INSERT INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');
+INSERT INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');
+INSERT INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');
+INSERT INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');
+INSERT INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');
+INSERT INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');
+INSERT INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');
+
+INSERT INTO pets VALUES (1, 'Leo', '2000-09-07', 1, 1);
+INSERT INTO pets VALUES (2, 'Basil', '2002-08-06', 6, 2);
+INSERT INTO pets VALUES (3, 'Rosy', '2001-04-17', 2, 3);
+INSERT INTO pets VALUES (4, 'Jewel', '2000-03-07', 2, 3);
+INSERT INTO pets VALUES (5, 'Iggy', '2000-11-30', 3, 4);
+INSERT INTO pets VALUES (6, 'George', '2000-01-20', 4, 5);
+INSERT INTO pets VALUES (7, 'Samantha', '1995-09-04', 1, 6);
+INSERT INTO pets VALUES (8, 'Max', '1995-09-04', 1, 6);
+INSERT INTO pets VALUES (9, 'Lucky', '1999-08-06', 5, 7);
+INSERT INTO pets VALUES (10, 'Mulligan', '1997-02-24', 2, 8);
+INSERT INTO pets VALUES (11, 'Freddy', '2000-03-09', 5, 9);
+INSERT INTO pets VALUES (12, 'Lucky', '2000-06-24', 2, 10);
+INSERT INTO pets VALUES (13, 'Sly', '2002-06-08', 1, 10);
+
+INSERT INTO visits VALUES (1, 7, '1996-03-04', 'rabies shot');
+INSERT INTO visits VALUES (2, 8, '1996-03-04', 'rabies shot');
+INSERT INTO visits VALUES (3, 8, '1996-06-04', 'neutered');
+INSERT INTO visits VALUES (4, 7, '1996-09-04', 'spayed');
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000..d9b79897e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,233 @@
+
+
+ 4.0.0
+ org.springframework.samples
+ org.springframework.samples.petclinic
+ petclinic-classic
+ war
+ 1.0.0-SNAPSHOT
+
+ 3.0.0.RC2
+ 1.5.6
+
+
+
+ com.oracle.toplink.essentials
+ com.springsource.oracle.toplink.essentials
+ 2.0.0.b41-beta2
+
+
+ com.sun.syndication
+ com.springsource.com.sun.syndication
+ 1.0.0
+
+
+ javax.persistence
+ com.springsource.javax.persistence
+ 1.0.0
+
+
+ javax.servlet
+ com.springsource.javax.servlet
+ 2.5.0
+ provided
+
+
+ javax.servlet
+ com.springsource.javax.servlet.jsp
+ 2.1.0
+ provided
+
+
+ javax.servlet
+ com.springsource.javax.servlet.jsp.jstl
+ 1.2.0
+
+
+ javax.xml.bind
+ com.springsource.javax.xml.bind
+ 2.1.7
+
+
+ org.apache.commons
+ com.springsource.org.apache.commons.dbcp
+ 1.2.2.osgi
+
+
+ org.apache.log4j
+ com.springsource.org.apache.log4j
+ 1.2.15
+
+
+ org.apache.openjpa
+ com.springsource.org.apache.openjpa
+ 1.1.0
+
+
+ org.apache.taglibs
+ com.springsource.org.apache.taglibs.standard
+ 1.1.2
+
+
+ org.aspectj
+ com.springsource.org.aspectj.weaver
+ 1.6.3.RELEASE
+
+
+ org.hibernate
+ com.springsource.org.hibernate
+ 3.3.1.GA
+
+
+ org.hibernate
+ com.springsource.org.hibernate.ejb
+ 3.4.0.GA
+
+
+ org.hsqldb
+ com.springsource.org.hsqldb
+ 1.8.0.9
+
+
+ org.jdom
+ com.springsource.org.jdom
+ 1.0.0
+
+
+ org.springframework
+ org.springframework.asm
+ ${spring.version}
+
+
+ org.springframework
+ org.springframework.orm
+ ${spring.version}
+
+
+ org.springframework
+ org.springframework.oxm
+ ${spring.version}
+
+
+ org.springframework
+ org.springframework.web.servlet
+ ${spring.version}
+
+
+
+ org.junit
+ com.springsource.org.junit
+ 4.5.0
+ test
+
+
+ org.springframework
+ org.springframework.test
+ ${spring.version}
+ test
+
+
+ javax.transaction
+ com.springsource.javax.transaction
+ 1.1.0
+ test
+
+
+ org.hibernate
+ com.springsource.org.hibernate.annotations
+ 3.4.0.GA
+ test
+
+
+ org.slf4j
+ com.springsource.slf4j.org.apache.commons.logging
+ ${slf4j.version}
+ provided
+
+
+ org.slf4j
+ com.springsource.slf4j.api
+ ${slf4j.version}
+ provided
+
+
+ org.slf4j
+ com.springsource.slf4j.log4j
+ ${slf4j.version}
+ provided
+
+
+ log4j
+ log4j
+
+
+ org.apache.log4j
+ com.springsource.org.apache.log4j
+
+
+
+
+
+
+ com.springsource.repository.bundles.release
+ SpringSource Enterprise Bundle Repository - SpringSource Releases
+ http://repository.springsource.com/maven/bundles/release
+
+
+ com.springsource.repository.bundles.milestone
+ SpringSource Enterprise Bundle Repository - SpringSource Milestones
+ http://repository.springsource.com/maven/bundles/milestone
+
+
+ com.springsource.repository.bundles.external
+ SpringSource Enterprise Bundle Repository - External Releases
+ http://repository.springsource.com/maven/bundles/external
+
+
+ com.springsource.repository.bundles.snapshot
+ SpringSource Enterprise Bundle Repository - Snapshot Releases
+ http://repository.springsource.com/maven/bundles/snapshot
+
+
+ spring-release
+ Spring Portfolio Release Repository
+ http://maven.springframework.org/release
+
+
+ spring-external
+ Spring Portfolio External Repository
+ http://maven.springframework.org/external
+
+
+ spring-milestone
+ Spring Portfolio Milestone Repository
+ http://maven.springframework.org/milestone
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.5
+ 1.5
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ install
+ install
+
+ sources
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/readme.txt b/readme.txt
index aee19b7ff..ae1fb2511 100644
--- a/readme.txt
+++ b/readme.txt
@@ -44,22 +44,20 @@ pooling.
=== Build and Deployment
==========================================================================
-The Spring PetClinic sample application is built using Spring Build, which
-is a custom build solution based on Ant and Ivy for dependency management.
-For deployment, the web application needs to be built with Apache Ant 1.6
-or higher. When the project is first built, Spring Build will use Ivy to
-automatically download all required dependencies. Thus the initial build
+The Spring PetClinic sample application is built using Maven.
+When the project is first built, Maven will automatically download all required
+dependencies (if these haven't been downloaded before). Thus the initial build
may take a few minutes depending on the speed of your Internet connection,
but subsequent builds will be much faster.
Available build commands:
-- ant clean --> cleans the project
-- ant clean test --> cleans the project and runs all tests
-- ant clean jar --> cleans the project and builds the WAR
+- mvn clean --> cleans the project
+- mvn clean test --> cleans the project and runs all tests
+- mvn clean package --> cleans the project and builds the WAR
-After building the project with "ant clean jar", you will find the
-resulting WAR file in the "target/artifacts" directory. By default, an
+After building the project with "mvn clean package", you will find the
+resulting WAR file in the "target/" directory. By default, an
embedded HSQLDB instance in configured. No other steps are necessary to
get the data source up and running: you can simply deploy the built WAR
file directly to your Servlet container.
diff --git a/src/main/java/org/springframework/samples/petclinic/BaseEntity.java b/src/main/java/org/springframework/samples/petclinic/BaseEntity.java
new file mode 100644
index 000000000..bb68af4fc
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/BaseEntity.java
@@ -0,0 +1,27 @@
+package org.springframework.samples.petclinic;
+
+/**
+ * Simple JavaBean domain object with an id property.
+ * Used as a base class for objects needing this property.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ */
+public class BaseEntity {
+
+ private Integer id;
+
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public boolean isNew() {
+ return (this.id == null);
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/Clinic.java b/src/main/java/org/springframework/samples/petclinic/Clinic.java
new file mode 100644
index 000000000..e48e8507b
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/Clinic.java
@@ -0,0 +1,82 @@
+package org.springframework.samples.petclinic;
+
+import java.util.Collection;
+
+import org.springframework.dao.DataAccessException;
+
+/**
+ * The high-level PetClinic business interface.
+ *
+ *
This is basically a data access object.
+ * PetClinic doesn't have a dedicated business facade.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ */
+public interface Clinic {
+
+ /**
+ * Retrieve all Vets from the data store.
+ * @return a Collection of Vets
+ */
+ Collection getVets() throws DataAccessException;
+
+ /**
+ * Retrieve all PetTypes from the data store.
+ * @return a Collection of PetTypes
+ */
+ Collection getPetTypes() throws DataAccessException;
+
+ /**
+ * Retrieve Owners from the data store by last name,
+ * returning all owners whose last name starts with the given name.
+ * @param lastName Value to search for
+ * @return a Collection of matching Owners
+ * (or an empty Collection if none found)
+ */
+ Collection findOwners(String lastName) throws DataAccessException;
+
+ /**
+ * Retrieve an Owner from the data store by id.
+ * @param id the id to search for
+ * @return the Owner if found
+ * @throws org.springframework.dao.DataRetrievalFailureException if not found
+ */
+ Owner loadOwner(int id) throws DataAccessException;
+
+ /**
+ * Retrieve a Pet from the data store by id.
+ * @param id the id to search for
+ * @return the Pet if found
+ * @throws org.springframework.dao.DataRetrievalFailureException if not found
+ */
+ Pet loadPet(int id) throws DataAccessException;
+
+ /**
+ * Save an Owner to the data store, either inserting or updating it.
+ * @param owner the Owner to save
+ * @see BaseEntity#isNew
+ */
+ void storeOwner(Owner owner) throws DataAccessException;
+
+ /**
+ * Save a Pet to the data store, either inserting or updating it.
+ * @param pet the Pet to save
+ * @see BaseEntity#isNew
+ */
+ void storePet(Pet pet) throws DataAccessException;
+
+ /**
+ * Save a Visit to the data store, either inserting or updating it.
+ * @param visit the Visit to save
+ * @see BaseEntity#isNew
+ */
+ void storeVisit(Visit visit) throws DataAccessException;
+
+ /**
+ * Deletes a Pet from the data store.
+ */
+ void deletePet(int id) throws DataAccessException;
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/NamedEntity.java b/src/main/java/org/springframework/samples/petclinic/NamedEntity.java
new file mode 100644
index 000000000..40c5931d8
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/NamedEntity.java
@@ -0,0 +1,28 @@
+package org.springframework.samples.petclinic;
+
+/**
+ * Simple JavaBean domain object adds a name property to BaseEntity.
+ * Used as a base class for objects needing these properties.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ */
+public class NamedEntity extends BaseEntity {
+
+ private String name;
+
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String toString() {
+ return this.getName();
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/Owner.java b/src/main/java/org/springframework/samples/petclinic/Owner.java
new file mode 100644
index 000000000..75ea3bc06
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/Owner.java
@@ -0,0 +1,127 @@
+package org.springframework.samples.petclinic;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.beans.support.MutableSortDefinition;
+import org.springframework.beans.support.PropertyComparator;
+import org.springframework.core.style.ToStringCreator;
+
+/**
+ * Simple JavaBean domain object representing an owner.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ */
+public class Owner extends Person {
+
+ private String address;
+
+ private String city;
+
+ private String telephone;
+
+ private Set pets;
+
+
+ public String getAddress() {
+ return this.address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public String getCity() {
+ return this.city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getTelephone() {
+ return this.telephone;
+ }
+
+ public void setTelephone(String telephone) {
+ this.telephone = telephone;
+ }
+
+ protected void setPetsInternal(Set pets) {
+ this.pets = pets;
+ }
+
+ protected Set getPetsInternal() {
+ if (this.pets == null) {
+ this.pets = new HashSet();
+ }
+ return this.pets;
+ }
+
+ public List getPets() {
+ List sortedPets = new ArrayList(getPetsInternal());
+ PropertyComparator.sort(sortedPets, new MutableSortDefinition("name", true, true));
+ return Collections.unmodifiableList(sortedPets);
+ }
+
+ public void addPet(Pet pet) {
+ getPetsInternal().add(pet);
+ pet.setOwner(this);
+ }
+
+ /**
+ * Return the Pet with the given name, or null if none found for this Owner.
+ *
+ * @param name to test
+ * @return true if pet name is already in use
+ */
+ public Pet getPet(String name) {
+ return getPet(name, false);
+ }
+
+ /**
+ * Return the Pet with the given name, or null if none found for this Owner.
+ *
+ * @param name to test
+ * @return true if pet name is already in use
+ */
+ public Pet getPet(String name, boolean ignoreNew) {
+ name = name.toLowerCase();
+ for (Pet pet : getPetsInternal()) {
+ if (!ignoreNew || !pet.isNew()) {
+ String compName = pet.getName();
+ compName = compName.toLowerCase();
+ if (compName.equals(name)) {
+ return pet;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringCreator(this)
+
+ .append("id", this.getId())
+
+ .append("new", this.isNew())
+
+ .append("lastName", this.getLastName())
+
+ .append("firstName", this.getFirstName())
+
+ .append("address", this.address)
+
+ .append("city", this.city)
+
+ .append("telephone", this.telephone)
+
+ .toString();
+ }
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/Person.java b/src/main/java/org/springframework/samples/petclinic/Person.java
new file mode 100644
index 000000000..da7974a7d
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/Person.java
@@ -0,0 +1,32 @@
+package org.springframework.samples.petclinic;
+
+/**
+ * Simple JavaBean domain object representing an person.
+ *
+ * @author Ken Krebs
+ */
+public class Person extends BaseEntity {
+
+ private String firstName;
+
+ private String lastName;
+
+ public String getFirstName() {
+ return this.firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return this.lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/Pet.java b/src/main/java/org/springframework/samples/petclinic/Pet.java
new file mode 100644
index 000000000..f5294b5ca
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/Pet.java
@@ -0,0 +1,77 @@
+package org.springframework.samples.petclinic;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.beans.support.MutableSortDefinition;
+import org.springframework.beans.support.PropertyComparator;
+
+/**
+ * Simple JavaBean business object representing a pet.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ */
+public class Pet extends NamedEntity {
+
+ private Date birthDate;
+
+ private PetType type;
+
+ private Owner owner;
+
+ private Set visits;
+
+
+ public void setBirthDate(Date birthDate) {
+ this.birthDate = birthDate;
+ }
+
+ public Date getBirthDate() {
+ return this.birthDate;
+ }
+
+ public void setType(PetType type) {
+ this.type = type;
+ }
+
+ public PetType getType() {
+ return this.type;
+ }
+
+ protected void setOwner(Owner owner) {
+ this.owner = owner;
+ }
+
+ public Owner getOwner() {
+ return this.owner;
+ }
+
+ protected void setVisitsInternal(Set visits) {
+ this.visits = visits;
+ }
+
+ protected Set getVisitsInternal() {
+ if (this.visits == null) {
+ this.visits = new HashSet();
+ }
+ return this.visits;
+ }
+
+ public List getVisits() {
+ List sortedVisits = new ArrayList(getVisitsInternal());
+ PropertyComparator.sort(sortedVisits, new MutableSortDefinition("date", false, false));
+ return Collections.unmodifiableList(sortedVisits);
+ }
+
+ public void addVisit(Visit visit) {
+ getVisitsInternal().add(visit);
+ visit.setPet(this);
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/PetType.java b/src/main/java/org/springframework/samples/petclinic/PetType.java
new file mode 100644
index 000000000..aaadc5c44
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/PetType.java
@@ -0,0 +1,8 @@
+package org.springframework.samples.petclinic;
+
+/**
+ * @author Juergen Hoeller
+ */
+public class PetType extends NamedEntity {
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/Specialty.java b/src/main/java/org/springframework/samples/petclinic/Specialty.java
new file mode 100644
index 000000000..d19ccaba9
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/Specialty.java
@@ -0,0 +1,10 @@
+package org.springframework.samples.petclinic;
+
+/**
+ * Models a {@link Vet Vet's} specialty (for example, dentistry).
+ *
+ * @author Juergen Hoeller
+ */
+public class Specialty extends NamedEntity {
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/Vet.java b/src/main/java/org/springframework/samples/petclinic/Vet.java
new file mode 100644
index 000000000..9c7c8da0b
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/Vet.java
@@ -0,0 +1,52 @@
+package org.springframework.samples.petclinic;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.xml.bind.annotation.XmlElement;
+
+import org.springframework.beans.support.MutableSortDefinition;
+import org.springframework.beans.support.PropertyComparator;
+
+/**
+ * Simple JavaBean domain object representing a veterinarian.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ * @author Arjen Poutsma
+ */
+public class Vet extends Person {
+
+ private Set specialties;
+
+
+ protected void setSpecialtiesInternal(Set specialties) {
+ this.specialties = specialties;
+ }
+
+ protected Set getSpecialtiesInternal() {
+ if (this.specialties == null) {
+ this.specialties = new HashSet();
+ }
+ return this.specialties;
+ }
+
+ @XmlElement
+ public List getSpecialties() {
+ List sortedSpecs = new ArrayList(getSpecialtiesInternal());
+ PropertyComparator.sort(sortedSpecs, new MutableSortDefinition("name", true, true));
+ return Collections.unmodifiableList(sortedSpecs);
+ }
+
+ public int getNrOfSpecialties() {
+ return getSpecialtiesInternal().size();
+ }
+
+ public void addSpecialty(Specialty specialty) {
+ getSpecialtiesInternal().add(specialty);
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/Vets.java b/src/main/java/org/springframework/samples/petclinic/Vets.java
new file mode 100644
index 000000000..2e3b25e27
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/Vets.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2009 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;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple JavaBean domain object representing a list of veterinarians. Mostly here to be used for the 'vets'
+ * {@link org.springframework.web.servlet.view.xml.MarshallingView}.
+ *
+ * @author Arjen Poutsma
+ */
+@XmlRootElement
+public class Vets {
+
+ private List vets;
+
+ @XmlElement
+ public List getVetList() {
+ if (vets == null) {
+ vets = new ArrayList();
+ }
+ return vets;
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/Visit.java b/src/main/java/org/springframework/samples/petclinic/Visit.java
new file mode 100644
index 000000000..c42bdcee5
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/Visit.java
@@ -0,0 +1,70 @@
+package org.springframework.samples.petclinic;
+
+import java.util.Date;
+
+/**
+ * Simple JavaBean domain object representing a visit.
+ *
+ * @author Ken Krebs
+ */
+public class Visit extends BaseEntity {
+
+ /** Holds value of property date. */
+ private Date date;
+
+ /** Holds value of property description. */
+ private String description;
+
+ /** Holds value of property pet. */
+ private Pet pet;
+
+
+ /** Creates a new instance of Visit for the current date */
+ public Visit() {
+ this.date = new Date();
+ }
+
+
+ /** Getter for property date.
+ * @return Value of property date.
+ */
+ public Date getDate() {
+ return this.date;
+ }
+
+ /** Setter for property date.
+ * @param date New value of property date.
+ */
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ /** Getter for property description.
+ * @return Value of property description.
+ */
+ public String getDescription() {
+ return this.description;
+ }
+
+ /** Setter for property description.
+ * @param description New value of property description.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /** Getter for property pet.
+ * @return Value of property pet.
+ */
+ public Pet getPet() {
+ return this.pet;
+ }
+
+ /** Setter for property pet.
+ * @param pet New value of property pet.
+ */
+ public void setPet(Pet pet) {
+ this.pet = pet;
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/aspects/AbstractTraceAspect.java b/src/main/java/org/springframework/samples/petclinic/aspects/AbstractTraceAspect.java
new file mode 100644
index 000000000..26d32359f
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/aspects/AbstractTraceAspect.java
@@ -0,0 +1,31 @@
+package org.springframework.samples.petclinic.aspects;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+
+/**
+ * Aspect to illustrate Spring-driven load-time weaving.
+ *
+ * @author Ramnivas Laddad
+ * @since 2.5
+ */
+@Aspect
+public abstract class AbstractTraceAspect {
+
+ private static final Log logger = LogFactory.getLog(AbstractTraceAspect.class);
+
+ @Pointcut
+ public abstract void traced();
+
+ @Before("traced()")
+ public void trace(JoinPoint.StaticPart jpsp) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Entering " + jpsp.getSignature().toLongString());
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/aspects/CallMonitoringAspect.java b/src/main/java/org/springframework/samples/petclinic/aspects/CallMonitoringAspect.java
new file mode 100644
index 000000000..2de4cb41d
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/aspects/CallMonitoringAspect.java
@@ -0,0 +1,81 @@
+package org.springframework.samples.petclinic.aspects;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+import org.springframework.util.StopWatch;
+
+/**
+ * Simple AspectJ aspect that monitors call count and call invocation time.
+ * Implements the CallMonitor management interface.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.5
+ */
+@ManagedResource("petclinic:type=CallMonitor")
+@Aspect
+public class CallMonitoringAspect {
+
+ private boolean isEnabled = true;
+
+ private int callCount = 0;
+
+ private long accumulatedCallTime = 0;
+
+
+ @ManagedAttribute
+ public void setEnabled(boolean enabled) {
+ isEnabled = enabled;
+ }
+
+ @ManagedAttribute
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ @ManagedOperation
+ public void reset() {
+ this.callCount = 0;
+ this.accumulatedCallTime = 0;
+ }
+
+ @ManagedAttribute
+ public int getCallCount() {
+ return callCount;
+ }
+
+ @ManagedAttribute
+ public long getCallTime() {
+ return (this.callCount > 0 ? this.accumulatedCallTime / this.callCount : 0);
+ }
+
+
+ @Around("within(@org.springframework.stereotype.Service *)")
+ public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
+ if (this.isEnabled) {
+ StopWatch sw = new StopWatch(joinPoint.toShortString());
+
+ sw.start("invoke");
+ try {
+ return joinPoint.proceed();
+ }
+ finally {
+ sw.stop();
+ synchronized (this) {
+ this.callCount++;
+ this.accumulatedCallTime += sw.getTotalTimeMillis();
+ }
+ }
+ }
+
+ else {
+ return joinPoint.proceed();
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/aspects/UsageLogAspect.java b/src/main/java/org/springframework/samples/petclinic/aspects/UsageLogAspect.java
new file mode 100644
index 000000000..e326abfb7
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/aspects/UsageLogAspect.java
@@ -0,0 +1,48 @@
+package org.springframework.samples.petclinic.aspects;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+
+/**
+ * Sample AspectJ annotation-style aspect that saves
+ * every owner name requested to the clinic.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+@Aspect
+public class UsageLogAspect {
+
+ private int historySize = 100;
+
+ // Of course saving all names is not suitable for
+ // production use, but this is a simple example.
+ private List namesRequested = new ArrayList(this.historySize);
+
+
+ public synchronized void setHistorySize(int historySize) {
+ this.historySize = historySize;
+ this.namesRequested = new ArrayList(historySize);
+ }
+
+ @Before("execution(* *.findOwners(String)) && args(name)")
+ public synchronized void logNameRequest(String name) {
+ // Not the most efficient implementation,
+ // but we're aiming to illustrate the power of
+ // @AspectJ AOP, not write perfect code here :-)
+ if (this.namesRequested.size() > this.historySize) {
+ this.namesRequested.remove(0);
+ }
+ this.namesRequested.add(name);
+ }
+
+ public synchronized List getNamesRequested() {
+ return Collections.unmodifiableList(this.namesRequested);
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/config/DbcpDataSourceFactory.java b/src/main/java/org/springframework/samples/petclinic/config/DbcpDataSourceFactory.java
new file mode 100644
index 000000000..0454161e6
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/config/DbcpDataSourceFactory.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2002-2008 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.config;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import javax.sql.DataSource;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.core.io.Resource;
+
+/**
+ * A factory that creates a data source fit for use in a system environment. Creates a DBCP simple data source
+ * from the provided connection properties.
+ *
+ * This factory returns a fully-initialized DataSource implementation. When the DataSource is returned, callers are
+ * guaranteed that the database schema and data will have been loaded by that time.
+ *
+ * Is a FactoryBean, for exposing the fully-initialized DataSource as a Spring bean. See {@link #getObject()}.
+ *
+ * @author Chris Beams
+ * @author Scott Andrews
+ */
+public class DbcpDataSourceFactory implements FactoryBean, DisposableBean {
+
+ // configurable properties
+
+ private String driverClassName;
+
+ private String url;
+
+ private String username;
+
+ private String password;
+
+ private boolean populate;
+
+ private Resource schemaLocation;
+
+ private Resource dataLocation;
+
+ private Resource dropLocation;
+
+ /**
+ * The object created by this factory.
+ */
+ private BasicDataSource dataSource;
+
+ public void setDriverClassName(String driverClassName) {
+ this.driverClassName = driverClassName;
+ }
+
+ /**
+ * The data source connection URL
+ */
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ /**
+ * The data source username
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ /**
+ *The data source password
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * Indicates that the data base should be populated from the schema and data locations
+ */
+ public void setPopulate(boolean populate) {
+ this.populate = populate;
+ }
+
+ /**
+ * Sets the location of the file containing the schema DDL to export to the database.
+ * @param schemaLocation the location of the database schema DDL
+ */
+ public void setSchemaLocation(Resource schemaLocation) {
+ this.schemaLocation = schemaLocation;
+ }
+
+ /**
+ * Sets the location of the file containing the data to load into the database.
+ * @param testDataLocation the location of the data file
+ */
+ public void setDataLocation(Resource testDataLocation) {
+ this.dataLocation = testDataLocation;
+ }
+
+ /**
+ * Sets the location of the file containing the drop scripts for the database.
+ * @param testDataLocation the location of the data file
+ */
+ public void setDropLocation(Resource testDropLocation) {
+ this.dropLocation = testDropLocation;
+ }
+
+ // implementing FactoryBean
+
+ // this method is called by Spring to expose the DataSource as a bean
+ public DataSource getObject() throws Exception {
+ if (dataSource == null) {
+ initDataSource();
+ }
+ return dataSource;
+ }
+
+ public Class getObjectType() {
+ return DataSource.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+ // implementing DisposableBean
+
+ public void destroy() throws Exception {
+ dataSource.close();
+ }
+
+ // internal helper methods
+
+ // encapsulates the steps involved in initializing the data source: creating it, and populating it
+ private void initDataSource() {
+ // create the database source first
+ this.dataSource = createDataSource();
+
+ if (this.populate) {
+ // now populate the database by loading the schema and data
+ populateDataSource();
+ }
+ }
+
+ private BasicDataSource createDataSource() {
+ BasicDataSource dataSource = new BasicDataSource();
+ dataSource.setDriverClassName(this.driverClassName);
+ dataSource.setUrl(this.url);
+ dataSource.setUsername(this.username);
+ dataSource.setPassword(this.password);
+ return dataSource;
+ }
+
+ private void populateDataSource() {
+ DatabasePopulator populator = new DatabasePopulator(dataSource);
+ if (dropLocation != null) {
+ try {
+ populator.populate(this.dropLocation);
+ }
+ catch (Exception e) {
+ // ignore
+ }
+ }
+ populator.populate(this.schemaLocation);
+ populator.populate(this.dataLocation);
+ }
+
+ /**
+ * Populates a in memory data source with data.
+ */
+ private class DatabasePopulator {
+
+ private DataSource dataSource;
+
+ /**
+ * Creates a new database populator.
+ * @param dataSource the data source that will be populated.
+ */
+ public DatabasePopulator(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ /**
+ * Populate the database executing the statements in the provided resource against the database
+ * @param sqlFile spring resource containing SQL to run against the db
+ */
+ public void populate(Resource sqlFile) {
+ Connection connection = null;
+ try {
+ connection = dataSource.getConnection();
+ try {
+ String sql = parseSqlIn(sqlFile);
+ executeSql(sql, connection);
+ } catch (IOException e) {
+ throw new RuntimeException("I/O exception occurred accessing the database schema file", e);
+ } catch (SQLException e) {
+ throw new RuntimeException("SQL exception occurred exporting database schema", e);
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException("SQL exception occurred acquiring connection", e);
+ } finally {
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (SQLException e) {
+ }
+ }
+ }
+ }
+
+ // utility method to read a .sql txt input stream
+ private String parseSqlIn(Resource resource) throws IOException {
+ InputStream is = null;
+ try {
+ is = resource.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+
+ StringWriter sw = new StringWriter();
+ BufferedWriter writer = new BufferedWriter(sw);
+
+ for (int c=reader.read(); c != -1; c=reader.read()) {
+ writer.write(c);
+ }
+ writer.flush();
+ return sw.toString();
+
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ // utility method to run the parsed sql
+ private void executeSql(String sql, Connection connection) throws SQLException {
+ Statement statement = connection.createStatement();
+ statement.execute(sql);
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/hibernate/HibernateClinic.java b/src/main/java/org/springframework/samples/petclinic/hibernate/HibernateClinic.java
new file mode 100644
index 000000000..411638562
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/hibernate/HibernateClinic.java
@@ -0,0 +1,98 @@
+package org.springframework.samples.petclinic.hibernate;
+
+import java.util.Collection;
+
+import org.hibernate.SessionFactory;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Owner;
+import org.springframework.samples.petclinic.Pet;
+import org.springframework.samples.petclinic.PetType;
+import org.springframework.samples.petclinic.Vet;
+import org.springframework.samples.petclinic.Visit;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Hibernate implementation of the Clinic interface.
+ *
+ *
The mappings are defined in "petclinic.hbm.xml", located in the root of the
+ * class path.
+ *
+ *
Note that transactions are declared with annotations and that some methods
+ * contain "readOnly = true" which is an optimization that is particularly
+ * valuable when using Hibernate (to suppress unnecessary flush attempts for
+ * read-only operations).
+ *
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ * @author Mark Fisher
+ * @since 19.10.2003
+ */
+@Repository
+@Transactional
+public class HibernateClinic implements Clinic {
+
+ private SessionFactory sessionFactory;
+
+ @Autowired
+ public HibernateClinic(SessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ @Transactional(readOnly = true)
+ @SuppressWarnings("unchecked")
+ public Collection getVets() {
+ return sessionFactory.getCurrentSession().createQuery("from Vet vet order by vet.lastName, vet.firstName").list();
+ }
+
+ @Transactional(readOnly = true)
+ @SuppressWarnings("unchecked")
+ public Collection getPetTypes() {
+ return sessionFactory.getCurrentSession().createQuery("from PetType type order by type.name").list();
+ }
+
+ @Transactional(readOnly = true)
+ @SuppressWarnings("unchecked")
+ public Collection findOwners(String lastName) {
+ return sessionFactory.getCurrentSession().createQuery("from Owner owner where owner.lastName like :lastName")
+ .setString("lastName", lastName + "%").list();
+ }
+
+ @Transactional(readOnly = true)
+ public Owner loadOwner(int id) {
+ return (Owner) sessionFactory.getCurrentSession().load(Owner.class, id);
+ }
+
+ @Transactional(readOnly = true)
+ public Pet loadPet(int id) {
+ return (Pet) sessionFactory.getCurrentSession().load(Pet.class, id);
+ }
+
+ public void storeOwner(Owner owner) {
+ // Note: Hibernate3's merge operation does not reassociate the object
+ // with the current Hibernate Session. Instead, it will always copy the
+ // state over to a registered representation of the entity. In case of a
+ // new entity, it will register a copy as well, but will not update the
+ // id of the passed-in object. To still update the ids of the original
+ // objects too, we need to register Spring's
+ // IdTransferringMergeEventListener on our SessionFactory.
+ sessionFactory.getCurrentSession().merge(owner);
+ }
+
+ public void storePet(Pet pet) {
+ sessionFactory.getCurrentSession().merge(pet);
+ }
+
+ public void storeVisit(Visit visit) {
+ sessionFactory.getCurrentSession().merge(visit);
+ }
+
+ public void deletePet(int id) throws DataAccessException {
+ Pet pet = loadPet(id);
+ sessionFactory.getCurrentSession().delete(pet);
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/hibernate/package-info.java b/src/main/java/org/springframework/samples/petclinic/hibernate/package-info.java
new file mode 100644
index 000000000..e39ebac47
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/hibernate/package-info.java
@@ -0,0 +1,9 @@
+
+/**
+ *
+ * The classes in this package represent the Hibernate implementation
+ * of PetClinic's persistence layer.
+ *
+ */
+package org.springframework.samples.petclinic.hibernate;
+
diff --git a/src/main/java/org/springframework/samples/petclinic/jdbc/JdbcPet.java b/src/main/java/org/springframework/samples/petclinic/jdbc/JdbcPet.java
new file mode 100644
index 000000000..963ffdfe0
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/jdbc/JdbcPet.java
@@ -0,0 +1,35 @@
+package org.springframework.samples.petclinic.jdbc;
+
+import org.springframework.samples.petclinic.Pet;
+
+/**
+ * Subclass of Pet that carries temporary id properties which
+ * are only relevant for a JDBC implmentation of the Clinic.
+ *
+ * @author Juergen Hoeller
+ * @see SimpleJdbcClinic
+ */
+class JdbcPet extends Pet {
+
+ private int typeId;
+
+ private int ownerId;
+
+
+ public void setTypeId(int typeId) {
+ this.typeId = typeId;
+ }
+
+ public int getTypeId() {
+ return this.typeId;
+ }
+
+ public void setOwnerId(int ownerId) {
+ this.ownerId = ownerId;
+ }
+
+ public int getOwnerId() {
+ return this.ownerId;
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/jdbc/SimpleJdbcClinic.java b/src/main/java/org/springframework/samples/petclinic/jdbc/SimpleJdbcClinic.java
new file mode 100644
index 000000000..587acecb9
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/jdbc/SimpleJdbcClinic.java
@@ -0,0 +1,342 @@
+package org.springframework.samples.petclinic.jdbc;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.sql.DataSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
+import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
+import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
+import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+import org.springframework.orm.ObjectRetrievalFailureException;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Owner;
+import org.springframework.samples.petclinic.Pet;
+import org.springframework.samples.petclinic.PetType;
+import org.springframework.samples.petclinic.Specialty;
+import org.springframework.samples.petclinic.Vet;
+import org.springframework.samples.petclinic.Visit;
+import org.springframework.samples.petclinic.util.EntityUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * A simple JDBC-based implementation of the {@link Clinic} interface.
+ *
+ *
This class uses Java 5 language features and the {@link SimpleJdbcTemplate}
+ * plus {@link SimpleJdbcInsert}. It also takes advantage of classes like
+ * {@link BeanPropertySqlParameterSource} and
+ * {@link ParameterizedBeanPropertyRowMapper} which provide automatic mapping
+ * between JavaBean properties and JDBC parameters or query results.
+ *
+ *
SimpleJdbcClinic is a rewrite of the AbstractJdbcClinic which was the base
+ * class for JDBC implementations of the Clinic interface for Spring 2.0.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ * @author Rob Harrop
+ * @author Sam Brannen
+ * @author Thomas Risberg
+ * @author Mark Fisher
+ */
+@Service
+@ManagedResource("petclinic:type=Clinic")
+public class SimpleJdbcClinic implements Clinic, SimpleJdbcClinicMBean {
+
+ private final Log logger = LogFactory.getLog(getClass());
+
+ private SimpleJdbcTemplate simpleJdbcTemplate;
+
+ private SimpleJdbcInsert insertOwner;
+ private SimpleJdbcInsert insertPet;
+ private SimpleJdbcInsert insertVisit;
+
+ private final List vets = new ArrayList();
+
+
+ @Autowired
+ public void init(DataSource dataSource) {
+ this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
+
+ this.insertOwner = new SimpleJdbcInsert(dataSource)
+ .withTableName("owners")
+ .usingGeneratedKeyColumns("id");
+ this.insertPet = new SimpleJdbcInsert(dataSource)
+ .withTableName("pets")
+ .usingGeneratedKeyColumns("id");
+ this.insertVisit = new SimpleJdbcInsert(dataSource)
+ .withTableName("visits")
+ .usingGeneratedKeyColumns("id");
+ }
+
+
+ /**
+ * Refresh the cache of Vets that the Clinic is holding.
+ * @see org.springframework.samples.petclinic.Clinic#getVets()
+ */
+ @ManagedOperation
+ @Transactional(readOnly = true)
+ public void refreshVetsCache() throws DataAccessException {
+ synchronized (this.vets) {
+ this.logger.info("Refreshing vets cache");
+
+ // Retrieve the list of all vets.
+ this.vets.clear();
+ this.vets.addAll(this.simpleJdbcTemplate.query(
+ "SELECT id, first_name, last_name FROM vets ORDER BY last_name,first_name",
+ ParameterizedBeanPropertyRowMapper.newInstance(Vet.class)));
+
+ // Retrieve the list of all possible specialties.
+ final List specialties = this.simpleJdbcTemplate.query(
+ "SELECT id, name FROM specialties",
+ ParameterizedBeanPropertyRowMapper.newInstance(Specialty.class));
+
+ // Build each vet's list of specialties.
+ for (Vet vet : this.vets) {
+ final List vetSpecialtiesIds = this.simpleJdbcTemplate.query(
+ "SELECT specialty_id FROM vet_specialties WHERE vet_id=?",
+ new ParameterizedRowMapper() {
+ public Integer mapRow(ResultSet rs, int row) throws SQLException {
+ return Integer.valueOf(rs.getInt(1));
+ }},
+ vet.getId().intValue());
+ for (int specialtyId : vetSpecialtiesIds) {
+ Specialty specialty = EntityUtils.getById(specialties, Specialty.class, specialtyId);
+ vet.addSpecialty(specialty);
+ }
+ }
+ }
+ }
+
+
+ // START of Clinic implementation section *******************************
+
+ @Transactional(readOnly = true)
+ public Collection getVets() throws DataAccessException {
+ synchronized (this.vets) {
+ if (this.vets.isEmpty()) {
+ refreshVetsCache();
+ }
+ return this.vets;
+ }
+ }
+
+ @Transactional(readOnly = true)
+ public Collection getPetTypes() throws DataAccessException {
+ return this.simpleJdbcTemplate.query(
+ "SELECT id, name FROM types ORDER BY name",
+ ParameterizedBeanPropertyRowMapper.newInstance(PetType.class));
+ }
+
+ /**
+ * Loads {@link Owner Owners} from the data store by last name, returning
+ * all owners whose last name starts with the given name; also loads
+ * the {@link Pet Pets} and {@link Visit Visits} for the corresponding
+ * owners, if not already loaded.
+ */
+ @Transactional(readOnly = true)
+ public Collection findOwners(String lastName) throws DataAccessException {
+ List owners = this.simpleJdbcTemplate.query(
+ "SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE last_name like ?",
+ ParameterizedBeanPropertyRowMapper.newInstance(Owner.class),
+ lastName + "%");
+ loadOwnersPetsAndVisits(owners);
+ return owners;
+ }
+
+ /**
+ * Loads the {@link Owner} with the supplied id; also loads
+ * the {@link Pet Pets} and {@link Visit Visits} for the corresponding
+ * owner, if not already loaded.
+ */
+ @Transactional(readOnly = true)
+ public Owner loadOwner(int id) throws DataAccessException {
+ Owner owner;
+ try {
+ owner = this.simpleJdbcTemplate.queryForObject(
+ "SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE id=?",
+ ParameterizedBeanPropertyRowMapper.newInstance(Owner.class),
+ id);
+ }
+ catch (EmptyResultDataAccessException ex) {
+ throw new ObjectRetrievalFailureException(Owner.class, new Integer(id));
+ }
+ loadPetsAndVisits(owner);
+ return owner;
+ }
+
+ @Transactional(readOnly = true)
+ public Pet loadPet(int id) throws DataAccessException {
+ JdbcPet pet;
+ try {
+ pet = this.simpleJdbcTemplate.queryForObject(
+ "SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE id=?",
+ new JdbcPetRowMapper(),
+ id);
+ }
+ catch (EmptyResultDataAccessException ex) {
+ throw new ObjectRetrievalFailureException(Pet.class, new Integer(id));
+ }
+ Owner owner = loadOwner(pet.getOwnerId());
+ owner.addPet(pet);
+ pet.setType(EntityUtils.getById(getPetTypes(), PetType.class, pet.getTypeId()));
+ loadVisits(pet);
+ return pet;
+ }
+
+ @Transactional
+ public void storeOwner(Owner owner) throws DataAccessException {
+ if (owner.isNew()) {
+ Number newKey = this.insertOwner.executeAndReturnKey(
+ new BeanPropertySqlParameterSource(owner));
+ owner.setId(newKey.intValue());
+ }
+ else {
+ this.simpleJdbcTemplate.update(
+ "UPDATE owners SET first_name=:firstName, last_name=:lastName, address=:address, " +
+ "city=:city, telephone=:telephone WHERE id=:id",
+ new BeanPropertySqlParameterSource(owner));
+ }
+ }
+
+ @Transactional
+ public void storePet(Pet pet) throws DataAccessException {
+ if (pet.isNew()) {
+ Number newKey = this.insertPet.executeAndReturnKey(
+ createPetParameterSource(pet));
+ pet.setId(newKey.intValue());
+ }
+ else {
+ this.simpleJdbcTemplate.update(
+ "UPDATE pets SET name=:name, birth_date=:birth_date, type_id=:type_id, " +
+ "owner_id=:owner_id WHERE id=:id",
+ createPetParameterSource(pet));
+ }
+ }
+
+ @Transactional
+ public void storeVisit(Visit visit) throws DataAccessException {
+ if (visit.isNew()) {
+ Number newKey = this.insertVisit.executeAndReturnKey(
+ createVisitParameterSource(visit));
+ visit.setId(newKey.intValue());
+ }
+ else {
+ throw new UnsupportedOperationException("Visit update not supported");
+ }
+ }
+
+ public void deletePet(int id) throws DataAccessException {
+ this.simpleJdbcTemplate.update("DELETE FROM pets WHERE id=?", id);
+ }
+
+ // END of Clinic implementation section ************************************
+
+
+ /**
+ * Creates a {@link MapSqlParameterSource} based on data values from the
+ * supplied {@link Pet} instance.
+ */
+ private MapSqlParameterSource createPetParameterSource(Pet pet) {
+ return new MapSqlParameterSource()
+ .addValue("id", pet.getId())
+ .addValue("name", pet.getName())
+ .addValue("birth_date", pet.getBirthDate())
+ .addValue("type_id", pet.getType().getId())
+ .addValue("owner_id", pet.getOwner().getId());
+ }
+
+ /**
+ * Creates a {@link MapSqlParameterSource} based on data values from the
+ * supplied {@link Visit} instance.
+ */
+ private MapSqlParameterSource createVisitParameterSource(Visit visit) {
+ return new MapSqlParameterSource()
+ .addValue("id", visit.getId())
+ .addValue("visit_date", visit.getDate())
+ .addValue("description", visit.getDescription())
+ .addValue("pet_id", visit.getPet().getId());
+ }
+
+ /**
+ * Loads the {@link Visit} data for the supplied {@link Pet}.
+ */
+ private void loadVisits(JdbcPet pet) {
+ final List visits = this.simpleJdbcTemplate.query(
+ "SELECT id, visit_date, description FROM visits WHERE pet_id=?",
+ new ParameterizedRowMapper() {
+ public Visit mapRow(ResultSet rs, int row) throws SQLException {
+ Visit visit = new Visit();
+ visit.setId(rs.getInt("id"));
+ visit.setDate(rs.getTimestamp("visit_date"));
+ visit.setDescription(rs.getString("description"));
+ return visit;
+ }
+ },
+ pet.getId().intValue());
+ for (Visit visit : visits) {
+ pet.addVisit(visit);
+ }
+ }
+
+ /**
+ * Loads the {@link Pet} and {@link Visit} data for the supplied
+ * {@link Owner}.
+ */
+ private void loadPetsAndVisits(final Owner owner) {
+ final List pets = this.simpleJdbcTemplate.query(
+ "SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE owner_id=?",
+ new JdbcPetRowMapper(),
+ owner.getId().intValue());
+ for (JdbcPet pet : pets) {
+ owner.addPet(pet);
+ pet.setType(EntityUtils.getById(getPetTypes(), PetType.class, pet.getTypeId()));
+ loadVisits(pet);
+ }
+ }
+
+ /**
+ * Loads the {@link Pet} and {@link Visit} data for the supplied
+ * {@link List} of {@link Owner Owners}.
+ *
+ * @param owners the list of owners for whom the pet and visit data should be loaded
+ * @see #loadPetsAndVisits(Owner)
+ */
+ private void loadOwnersPetsAndVisits(List owners) {
+ for (Owner owner : owners) {
+ loadPetsAndVisits(owner);
+ }
+ }
+
+ /**
+ * {@link ParameterizedRowMapper} implementation mapping data from a
+ * {@link ResultSet} to the corresponding properties of the {@link JdbcPet} class.
+ */
+ private class JdbcPetRowMapper implements ParameterizedRowMapper {
+
+ public JdbcPet mapRow(ResultSet rs, int rownum) throws SQLException {
+ JdbcPet pet = new JdbcPet();
+ pet.setId(rs.getInt("id"));
+ pet.setName(rs.getString("name"));
+ pet.setBirthDate(rs.getDate("birth_date"));
+ pet.setTypeId(rs.getInt("type_id"));
+ pet.setOwnerId(rs.getInt("owner_id"));
+ return pet;
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/jdbc/SimpleJdbcClinicMBean.java b/src/main/java/org/springframework/samples/petclinic/jdbc/SimpleJdbcClinicMBean.java
new file mode 100644
index 000000000..c9a7a7847
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/jdbc/SimpleJdbcClinicMBean.java
@@ -0,0 +1,20 @@
+package org.springframework.samples.petclinic.jdbc;
+
+/**
+ * Interface that defines a cache refresh operation.
+ * To be exposed for management via JMX.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @see SimpleJdbcClinic
+ */
+public interface SimpleJdbcClinicMBean {
+
+ /**
+ * Refresh the cache of Vets that the Clinic is holding.
+ * @see org.springframework.samples.petclinic.Clinic#getVets()
+ * @see SimpleJdbcClinic#refreshVetsCache()
+ */
+ void refreshVetsCache();
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/jdbc/package-info.java b/src/main/java/org/springframework/samples/petclinic/jdbc/package-info.java
new file mode 100644
index 000000000..6ec278b44
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/jdbc/package-info.java
@@ -0,0 +1,9 @@
+
+/**
+ *
+ * The classes in this package represent the JDBC implementation
+ * of PetClinic's persistence layer.
+ *
+ */
+package org.springframework.samples.petclinic.jdbc;
+
diff --git a/src/main/java/org/springframework/samples/petclinic/jpa/EntityManagerClinic.java b/src/main/java/org/springframework/samples/petclinic/jpa/EntityManagerClinic.java
new file mode 100644
index 000000000..92fe1e247
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/jpa/EntityManagerClinic.java
@@ -0,0 +1,96 @@
+package org.springframework.samples.petclinic.jpa;
+
+import java.util.Collection;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Owner;
+import org.springframework.samples.petclinic.Pet;
+import org.springframework.samples.petclinic.PetType;
+import org.springframework.samples.petclinic.Vet;
+import org.springframework.samples.petclinic.Visit;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.dao.DataAccessException;
+
+/**
+ * JPA implementation of the Clinic interface using EntityManager.
+ *
+ *
The mappings are defined in "orm.xml" located in the META-INF directory.
+ *
+ * @author Mike Keith
+ * @author Rod Johnson
+ * @author Sam Brannen
+ * @since 22.4.2006
+ */
+@Repository
+@Transactional
+public class EntityManagerClinic implements Clinic {
+
+ @PersistenceContext
+ private EntityManager em;
+
+
+ @Transactional(readOnly = true)
+ @SuppressWarnings("unchecked")
+ public Collection getVets() {
+ return this.em.createQuery("SELECT vet FROM Vet vet ORDER BY vet.lastName, vet.firstName").getResultList();
+ }
+
+ @Transactional(readOnly = true)
+ @SuppressWarnings("unchecked")
+ public Collection getPetTypes() {
+ return this.em.createQuery("SELECT ptype FROM PetType ptype ORDER BY ptype.name").getResultList();
+ }
+
+ @Transactional(readOnly = true)
+ @SuppressWarnings("unchecked")
+ public Collection findOwners(String lastName) {
+ Query query = this.em.createQuery("SELECT owner FROM Owner owner WHERE owner.lastName LIKE :lastName");
+ query.setParameter("lastName", lastName + "%");
+ return query.getResultList();
+ }
+
+ @Transactional(readOnly = true)
+ public Owner loadOwner(int id) {
+ return this.em.find(Owner.class, id);
+ }
+
+ @Transactional(readOnly = true)
+ public Pet loadPet(int id) {
+ return this.em.find(Pet.class, id);
+ }
+
+ public void storeOwner(Owner owner) {
+ // Consider returning the persistent object here, for exposing
+ // a newly assigned id using any persistence provider...
+ Owner merged = this.em.merge(owner);
+ this.em.flush();
+ owner.setId(merged.getId());
+ }
+
+ public void storePet(Pet pet) {
+ // Consider returning the persistent object here, for exposing
+ // a newly assigned id using any persistence provider...
+ Pet merged = this.em.merge(pet);
+ this.em.flush();
+ pet.setId(merged.getId());
+ }
+
+ public void storeVisit(Visit visit) {
+ // Consider returning the persistent object here, for exposing
+ // a newly assigned id using any persistence provider...
+ Visit merged = this.em.merge(visit);
+ this.em.flush();
+ visit.setId(merged.getId());
+ }
+
+ public void deletePet(int id) throws DataAccessException {
+ Pet pet = loadPet(id);
+ this.em.remove(pet);
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/jpa/package-info.java b/src/main/java/org/springframework/samples/petclinic/jpa/package-info.java
new file mode 100644
index 000000000..8093784ec
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/jpa/package-info.java
@@ -0,0 +1,9 @@
+
+/**
+ *
+ * The classes in this package represent the JPA implementation
+ * of PetClinic's persistence layer.
+ *
+ */
+package org.springframework.samples.petclinic.jpa;
+
diff --git a/src/main/java/org/springframework/samples/petclinic/package-info.java b/src/main/java/org/springframework/samples/petclinic/package-info.java
new file mode 100644
index 000000000..f2cc36915
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/package-info.java
@@ -0,0 +1,8 @@
+
+/**
+ *
+ * The classes in this package represent PetClinic's business layer.
+ *
+ */
+package org.springframework.samples.petclinic;
+
diff --git a/src/main/java/org/springframework/samples/petclinic/toplink/EssentialsHSQLPlatformWithNativeSequence.java b/src/main/java/org/springframework/samples/petclinic/toplink/EssentialsHSQLPlatformWithNativeSequence.java
new file mode 100644
index 000000000..1086591de
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/toplink/EssentialsHSQLPlatformWithNativeSequence.java
@@ -0,0 +1,56 @@
+package org.springframework.samples.petclinic.toplink;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import oracle.toplink.essentials.exceptions.ValidationException;
+import oracle.toplink.essentials.platform.database.HSQLPlatform;
+import oracle.toplink.essentials.queryframework.ValueReadQuery;
+
+/**
+ * Subclass of the TopLink Essentials default HSQLPlatform class, using native
+ * HSQLDB identity columns for id generation.
+ *
+ *
Necessary for PetClinic's default data model, which relies on identity
+ * columns: this is uniformly used across all persistence layer implementations
+ * (JDBC, Hibernate, and JPA).
+ *
+ * @author Juergen Hoeller
+ * @author James Clark
+ * @since 1.2
+ */
+public class EssentialsHSQLPlatformWithNativeSequence extends HSQLPlatform {
+
+ private static final long serialVersionUID = -55658009691346735L;
+
+
+ public EssentialsHSQLPlatformWithNativeSequence() {
+ // setUsesNativeSequencing(true);
+ }
+
+ @Override
+ public boolean supportsNativeSequenceNumbers() {
+ return true;
+ }
+
+ @Override
+ public boolean shouldNativeSequenceAcquireValueAfterInsert() {
+ return true;
+ }
+
+ @Override
+ public ValueReadQuery buildSelectQueryForNativeSequence() {
+ return new ValueReadQuery("CALL IDENTITY()");
+ }
+
+ @Override
+ public void printFieldIdentityClause(Writer writer) throws ValidationException {
+ try {
+ writer.write(" IDENTITY");
+ }
+ catch (IOException ex) {
+ throw ValidationException.fileError(ex);
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/toplink/package-info.java b/src/main/java/org/springframework/samples/petclinic/toplink/package-info.java
new file mode 100644
index 000000000..3bcc9add7
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/toplink/package-info.java
@@ -0,0 +1,10 @@
+
+/**
+ *
+ * The classes in this package provide support for using the TopLink
+ * implementation with PetClinic's EntityManagerClinic.
+ *
+ *
+ */
+package org.springframework.samples.petclinic.toplink;
+
diff --git a/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java b/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java
new file mode 100644
index 000000000..16df5fa9a
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java
@@ -0,0 +1,41 @@
+
+package org.springframework.samples.petclinic.util;
+
+import java.util.Collection;
+
+import org.springframework.orm.ObjectRetrievalFailureException;
+import org.springframework.samples.petclinic.BaseEntity;
+
+/**
+ * Utility methods for handling entities. Separate from the BaseEntity class
+ * mainly because of dependency on the ORM-associated
+ * ObjectRetrievalFailureException.
+ *
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ * @since 29.10.2003
+ * @see org.springframework.samples.petclinic.BaseEntity
+ */
+public abstract class EntityUtils {
+
+ /**
+ * Look up the entity of the given class with the given id in the given
+ * collection.
+ *
+ * @param entities the collection to search
+ * @param entityClass the entity class to look up
+ * @param entityId the entity id to look up
+ * @return the found entity
+ * @throws ObjectRetrievalFailureException if the entity was not found
+ */
+ public static T getById(Collection entities, Class entityClass, int entityId)
+ throws ObjectRetrievalFailureException {
+ for (T entity : entities) {
+ if (entity.getId().intValue() == entityId && entityClass.isInstance(entity)) {
+ return entity;
+ }
+ }
+ throw new ObjectRetrievalFailureException(entityClass, new Integer(entityId));
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/validation/OwnerValidator.java b/src/main/java/org/springframework/samples/petclinic/validation/OwnerValidator.java
new file mode 100644
index 000000000..04b6b7d58
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/validation/OwnerValidator.java
@@ -0,0 +1,43 @@
+package org.springframework.samples.petclinic.validation;
+
+import org.springframework.samples.petclinic.Owner;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.Errors;
+
+/**
+ * Validator for Owner forms.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ */
+public class OwnerValidator {
+
+ public void validate(Owner owner, Errors errors) {
+ if (!StringUtils.hasLength(owner.getFirstName())) {
+ errors.rejectValue("firstName", "required", "required");
+ }
+ if (!StringUtils.hasLength(owner.getLastName())) {
+ errors.rejectValue("lastName", "required", "required");
+ }
+ if (!StringUtils.hasLength(owner.getAddress())) {
+ errors.rejectValue("address", "required", "required");
+ }
+ if (!StringUtils.hasLength(owner.getCity())) {
+ errors.rejectValue("city", "required", "required");
+ }
+
+ String telephone = owner.getTelephone();
+ if (!StringUtils.hasLength(telephone)) {
+ errors.rejectValue("telephone", "required", "required");
+ }
+ else {
+ for (int i = 0; i < telephone.length(); ++i) {
+ if ((Character.isDigit(telephone.charAt(i))) == false) {
+ errors.rejectValue("telephone", "nonNumeric", "non-numeric");
+ break;
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/validation/PetValidator.java b/src/main/java/org/springframework/samples/petclinic/validation/PetValidator.java
new file mode 100644
index 000000000..8ad6eb0ac
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/validation/PetValidator.java
@@ -0,0 +1,25 @@
+package org.springframework.samples.petclinic.validation;
+
+import org.springframework.samples.petclinic.Pet;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.Errors;
+
+/**
+ * Validator for Pet forms.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ */
+public class PetValidator {
+
+ public void validate(Pet pet, Errors errors) {
+ String name = pet.getName();
+ if (!StringUtils.hasLength(name)) {
+ errors.rejectValue("name", "required", "required");
+ }
+ else if (pet.isNew() && pet.getOwner().getPet(name, true) != null) {
+ errors.rejectValue("name", "duplicate", "already exists");
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/validation/VisitValidator.java b/src/main/java/org/springframework/samples/petclinic/validation/VisitValidator.java
new file mode 100644
index 000000000..35c80bafa
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/validation/VisitValidator.java
@@ -0,0 +1,21 @@
+package org.springframework.samples.petclinic.validation;
+
+import org.springframework.samples.petclinic.Visit;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.Errors;
+
+/**
+ * Validator for Visit forms.
+ *
+ * @author Ken Krebs
+ * @author Juergen Hoeller
+ */
+public class VisitValidator {
+
+ public void validate(Visit visit, Errors errors) {
+ if (!StringUtils.hasLength(visit.getDescription())) {
+ errors.rejectValue("description", "required", "required");
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/validation/package-info.java b/src/main/java/org/springframework/samples/petclinic/validation/package-info.java
new file mode 100644
index 000000000..7db2ee521
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/validation/package-info.java
@@ -0,0 +1,9 @@
+
+/**
+ *
+ * The classes in this package represent the set of Validator objects
+ * the Business Layer makes available to the Presentation Layer.
+ *
+ */
+package org.springframework.samples.petclinic.validation;
+
diff --git a/src/main/java/org/springframework/samples/petclinic/web/AddOwnerForm.java b/src/main/java/org/springframework/samples/petclinic/web/AddOwnerForm.java
new file mode 100644
index 000000000..cd830aff6
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/AddOwnerForm.java
@@ -0,0 +1,65 @@
+
+package org.springframework.samples.petclinic.web;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Owner;
+import org.springframework.samples.petclinic.validation.OwnerValidator;
+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.ModelAttribute;
+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.support.SessionStatus;
+
+/**
+ * JavaBean form controller that is used to add a new Owner to the
+ * system.
+ *
+ * @author Juergen Hoeller
+ * @author Ken Krebs
+ * @author Arjen Poutsma
+ */
+@Controller
+@RequestMapping("/owners/new")
+@SessionAttributes(types = Owner.class)
+public class AddOwnerForm {
+
+ private final Clinic clinic;
+
+
+ @Autowired
+ public AddOwnerForm(Clinic clinic) {
+ this.clinic = clinic;
+ }
+
+ @InitBinder
+ public void setAllowedFields(WebDataBinder dataBinder) {
+ dataBinder.setDisallowedFields("id");
+ }
+
+ @RequestMapping(method = RequestMethod.GET)
+ public String setupForm(Model model) {
+ Owner owner = new Owner();
+ model.addAttribute(owner);
+ return "owners/form";
+ }
+
+ @RequestMapping(method = RequestMethod.POST)
+ public String processSubmit(@ModelAttribute Owner owner, BindingResult result, SessionStatus status) {
+ new OwnerValidator().validate(owner, result);
+ if (result.hasErrors()) {
+ return "owners/form";
+ }
+ else {
+ this.clinic.storeOwner(owner);
+ status.setComplete();
+ return "redirect:/owners/" + owner.getId();
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/AddPetForm.java b/src/main/java/org/springframework/samples/petclinic/web/AddPetForm.java
new file mode 100644
index 000000000..586cf3d67
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/AddPetForm.java
@@ -0,0 +1,77 @@
+
+package org.springframework.samples.petclinic.web;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Owner;
+import org.springframework.samples.petclinic.Pet;
+import org.springframework.samples.petclinic.PetType;
+import org.springframework.samples.petclinic.validation.PetValidator;
+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.ModelAttribute;
+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.support.SessionStatus;
+
+/**
+ * JavaBean form controller that is used to add a new Pet to the
+ * system.
+ *
+ * @author Juergen Hoeller
+ * @author Ken Krebs
+ * @author Arjen Poutsma
+ */
+@Controller
+@RequestMapping("/owners/{ownerId}/pets/new")
+@SessionAttributes("pet")
+public class AddPetForm {
+
+ private final Clinic clinic;
+
+
+ @Autowired
+ public AddPetForm(Clinic clinic) {
+ this.clinic = clinic;
+ }
+
+ @ModelAttribute("types")
+ public Collection populatePetTypes() {
+ return this.clinic.getPetTypes();
+ }
+
+ @InitBinder
+ public void setAllowedFields(WebDataBinder dataBinder) {
+ dataBinder.setDisallowedFields("id");
+ }
+
+ @RequestMapping(method = RequestMethod.GET)
+ public String setupForm(@PathVariable("ownerId") int ownerId, Model model) {
+ Owner owner = this.clinic.loadOwner(ownerId);
+ Pet pet = new Pet();
+ owner.addPet(pet);
+ model.addAttribute("pet", pet);
+ return "pets/form";
+ }
+
+ @RequestMapping(method = RequestMethod.POST)
+ public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) {
+ new PetValidator().validate(pet, result);
+ if (result.hasErrors()) {
+ return "pets/form";
+ }
+ else {
+ this.clinic.storePet(pet);
+ status.setComplete();
+ return "redirect:/owners/" + pet.getOwner().getId();
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/AddVisitForm.java b/src/main/java/org/springframework/samples/petclinic/web/AddVisitForm.java
new file mode 100644
index 000000000..683686440
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/AddVisitForm.java
@@ -0,0 +1,69 @@
+
+package org.springframework.samples.petclinic.web;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Pet;
+import org.springframework.samples.petclinic.Visit;
+import org.springframework.samples.petclinic.validation.VisitValidator;
+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.ModelAttribute;
+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.support.SessionStatus;
+
+/**
+ * JavaBean form controller that is used to add a new Visit to the
+ * system.
+ *
+ * @author Juergen Hoeller
+ * @author Ken Krebs
+ * @author Arjen Poutsma
+ */
+@Controller
+@RequestMapping("/owners/*/pets/{petId}/visits/new")
+@SessionAttributes("visit")
+public class AddVisitForm {
+
+ private final Clinic clinic;
+
+
+ @Autowired
+ public AddVisitForm(Clinic clinic) {
+ this.clinic = clinic;
+ }
+
+ @InitBinder
+ public void setAllowedFields(WebDataBinder dataBinder) {
+ dataBinder.setDisallowedFields("id");
+ }
+
+ @RequestMapping(method = RequestMethod.GET)
+ public String setupForm(@PathVariable("petId") int petId, Model model) {
+ Pet pet = this.clinic.loadPet(petId);
+ Visit visit = new Visit();
+ pet.addVisit(visit);
+ model.addAttribute("visit", visit);
+ return "pets/visitForm";
+ }
+
+ @RequestMapping(method = RequestMethod.POST)
+ public String processSubmit(@ModelAttribute("visit") Visit visit, BindingResult result, SessionStatus status) {
+ new VisitValidator().validate(visit, result);
+ if (result.hasErrors()) {
+ return "pets/visitForm";
+ }
+ else {
+ this.clinic.storeVisit(visit);
+ status.setComplete();
+ return "redirect:/owners/" + visit.getPet().getOwner().getId();
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/ClinicBindingInitializer.java b/src/main/java/org/springframework/samples/petclinic/web/ClinicBindingInitializer.java
new file mode 100644
index 000000000..2d2a8bdc1
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/ClinicBindingInitializer.java
@@ -0,0 +1,37 @@
+package org.springframework.samples.petclinic.web;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.propertyeditors.CustomDateEditor;
+import org.springframework.beans.propertyeditors.StringTrimmerEditor;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.PetType;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.support.WebBindingInitializer;
+import org.springframework.web.context.request.WebRequest;
+
+/**
+ * Shared WebBindingInitializer for PetClinic's custom editors.
+ *
+ *
Alternatively, such init-binder code may be put into
+ * {@link org.springframework.web.bind.annotation.InitBinder}
+ * annotated methods on the controller classes themselves.
+ *
+ * @author Juergen Hoeller
+ */
+public class ClinicBindingInitializer implements WebBindingInitializer {
+
+ @Autowired
+ private Clinic clinic;
+
+ public void initBinder(WebDataBinder binder, WebRequest request) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ dateFormat.setLenient(false);
+ binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
+ binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
+ binder.registerCustomEditor(PetType.class, new PetTypeEditor(this.clinic));
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/ClinicController.java b/src/main/java/org/springframework/samples/petclinic/web/ClinicController.java
new file mode 100644
index 000000000..e93ae8f66
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/ClinicController.java
@@ -0,0 +1,89 @@
+
+package org.springframework.samples.petclinic.web;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Vets;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+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;
+
+/**
+ * Annotation-driven MultiActionController that handles all non-form
+ * URL's.
+ *
+ * @author Juergen Hoeller
+ * @author Mark Fisher
+ * @author Ken Krebs
+ * @author Arjen Poutsma
+ */
+@Controller
+public class ClinicController {
+
+ private final Clinic clinic;
+
+
+ @Autowired
+ public ClinicController(Clinic clinic) {
+ this.clinic = clinic;
+ }
+
+ /**
+ * Custom handler for the welcome view.
+ *
+ * Note that this handler relies on the RequestToViewNameTranslator to
+ * determine the logical view name based on the request URL: "/welcome.do"
+ * -> "welcome".
+ */
+ @RequestMapping("/")
+ public String welcomeHandler() {
+ return "welcome";
+ }
+
+ /**
+ * Custom handler for displaying vets.
+ *
+ *
Note that this handler returns a plain {@link ModelMap} object instead of
+ * a ModelAndView, thus leveraging convention-based model attribute names.
+ * It relies on the RequestToViewNameTranslator to determine the logical
+ * view name based on the request URL: "/vets.do" -> "vets".
+ *
+ * @return a ModelMap with the model attributes for the view
+ */
+ @RequestMapping("/vets")
+ public ModelMap vetsHandler() {
+ Vets vets = new Vets();
+ vets.getVetList().addAll(this.clinic.getVets());
+ return new ModelMap(vets);
+ }
+
+ /**
+ * 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 ownerHandler(@PathVariable("ownerId") int ownerId) {
+ ModelAndView mav = new ModelAndView("owners/show");
+ mav.addObject(this.clinic.loadOwner(ownerId));
+ return mav;
+ }
+
+ /**
+ * Custom handler for displaying an list of visits.
+ *
+ * @param petId the ID of the pet whose visits to display
+ * @return a ModelMap with the model attributes for the view
+ */
+ @RequestMapping(value="/owners/*/pets/{petId}/visits", method=RequestMethod.GET)
+ public ModelAndView visitsHandler(@PathVariable int petId) {
+ ModelAndView mav = new ModelAndView("visits");
+ mav.addObject("visits", this.clinic.loadPet(petId).getVisits());
+ return mav;
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/EditOwnerForm.java b/src/main/java/org/springframework/samples/petclinic/web/EditOwnerForm.java
new file mode 100644
index 000000000..0b65de51b
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/EditOwnerForm.java
@@ -0,0 +1,65 @@
+
+package org.springframework.samples.petclinic.web;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Owner;
+import org.springframework.samples.petclinic.validation.OwnerValidator;
+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.ModelAttribute;
+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.support.SessionStatus;
+
+/**
+ * JavaBean Form controller that is used to edit an existing Owner.
+ *
+ * @author Juergen Hoeller
+ * @author Ken Krebs
+ * @author Arjen Poutsma
+ */
+@Controller
+@RequestMapping("/owners/{ownerId}/edit")
+@SessionAttributes(types = Owner.class)
+public class EditOwnerForm {
+
+ private final Clinic clinic;
+
+
+ @Autowired
+ public EditOwnerForm(Clinic clinic) {
+ this.clinic = clinic;
+ }
+
+ @InitBinder
+ public void setAllowedFields(WebDataBinder dataBinder) {
+ dataBinder.setDisallowedFields("id");
+ }
+
+ @RequestMapping(method = RequestMethod.GET)
+ public String setupForm(@PathVariable("ownerId") int ownerId, Model model) {
+ Owner owner = this.clinic.loadOwner(ownerId);
+ model.addAttribute(owner);
+ return "owners/form";
+ }
+
+ @RequestMapping(method = RequestMethod.PUT)
+ public String processSubmit(@ModelAttribute Owner owner, BindingResult result, SessionStatus status) {
+ new OwnerValidator().validate(owner, result);
+ if (result.hasErrors()) {
+ return "owners/form";
+ }
+ else {
+ this.clinic.storeOwner(owner);
+ status.setComplete();
+ return "redirect:/owners/" + owner.getId();
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/EditPetForm.java b/src/main/java/org/springframework/samples/petclinic/web/EditPetForm.java
new file mode 100644
index 000000000..1a7fd6ed3
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/EditPetForm.java
@@ -0,0 +1,80 @@
+
+package org.springframework.samples.petclinic.web;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Pet;
+import org.springframework.samples.petclinic.PetType;
+import org.springframework.samples.petclinic.validation.PetValidator;
+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.ModelAttribute;
+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.support.SessionStatus;
+
+/**
+ * JavaBean Form controller that is used to edit an existing Pet.
+ *
+ * @author Juergen Hoeller
+ * @author Ken Krebs
+ * @author Arjen Poutsma
+ */
+@Controller
+@RequestMapping("/owners/*/pets/{petId}/edit")
+@SessionAttributes("pet")
+public class EditPetForm {
+
+ private final Clinic clinic;
+
+
+ @Autowired
+ public EditPetForm(Clinic clinic) {
+ this.clinic = clinic;
+ }
+
+ @ModelAttribute("types")
+ public Collection populatePetTypes() {
+ return this.clinic.getPetTypes();
+ }
+
+ @InitBinder
+ public void setAllowedFields(WebDataBinder dataBinder) {
+ dataBinder.setDisallowedFields("id");
+ }
+
+ @RequestMapping(method = RequestMethod.GET)
+ public String setupForm(@PathVariable("petId") int petId, Model model) {
+ Pet pet = this.clinic.loadPet(petId);
+ model.addAttribute("pet", pet);
+ return "pets/form";
+ }
+
+ @RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
+ public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) {
+ new PetValidator().validate(pet, result);
+ if (result.hasErrors()) {
+ return "pets/form";
+ }
+ else {
+ this.clinic.storePet(pet);
+ status.setComplete();
+ return "redirect:/owners/" + pet.getOwner().getId();
+ }
+ }
+
+ @RequestMapping(method = RequestMethod.DELETE)
+ public String deletePet(@PathVariable int petId) {
+ Pet pet = this.clinic.loadPet(petId);
+ this.clinic.deletePet(petId);
+ return "redirect:/owners/" + pet.getOwner().getId();
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/FindOwnersForm.java b/src/main/java/org/springframework/samples/petclinic/web/FindOwnersForm.java
new file mode 100644
index 000000000..eb93fabad
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/FindOwnersForm.java
@@ -0,0 +1,74 @@
+
+package org.springframework.samples.petclinic.web;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.Owner;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+/**
+ * JavaBean Form controller that is used to search for Owners by
+ * last name.
+ *
+ * @author Juergen Hoeller
+ * @author Ken Krebs
+ * @author Arjen Poutsma
+ */
+@Controller
+public class FindOwnersForm {
+
+ private final Clinic clinic;
+
+
+ @Autowired
+ public FindOwnersForm(Clinic clinic) {
+ this.clinic = clinic;
+ }
+
+ @InitBinder
+ public void setAllowedFields(WebDataBinder dataBinder) {
+ dataBinder.setDisallowedFields("id");
+ }
+
+ @RequestMapping(value = "/owners/search", method = RequestMethod.GET)
+ public String setupForm(Model model) {
+ model.addAttribute("owner", new Owner());
+ return "owners/search";
+ }
+
+ @RequestMapping(value = "/owners", method = RequestMethod.GET)
+ public String processSubmit(Owner owner, BindingResult result, Model 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.clinic.findOwners(owner.getLastName());
+ if (results.size() < 1) {
+ // no owners found
+ result.rejectValue("lastName", "notFound", "not found");
+ return "owners/search";
+ }
+ if (results.size() > 1) {
+ // multiple owners found
+ model.addAttribute("selections", results);
+ return "owners/list";
+ }
+ else {
+ // 1 owner found
+ owner = results.iterator().next();
+ return "redirect:/owners/" + owner.getId();
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/PetTypeEditor.java b/src/main/java/org/springframework/samples/petclinic/web/PetTypeEditor.java
new file mode 100644
index 000000000..812b648d7
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/PetTypeEditor.java
@@ -0,0 +1,30 @@
+package org.springframework.samples.petclinic.web;
+
+import java.beans.PropertyEditorSupport;
+
+import org.springframework.samples.petclinic.Clinic;
+import org.springframework.samples.petclinic.PetType;
+
+/**
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ */
+public class PetTypeEditor extends PropertyEditorSupport {
+
+ private final Clinic clinic;
+
+
+ public PetTypeEditor(Clinic clinic) {
+ this.clinic = clinic;
+ }
+
+ @Override
+ public void setAsText(String text) throws IllegalArgumentException {
+ for (PetType type : this.clinic.getPetTypes()) {
+ if (type.getName().equals(text)) {
+ setValue(type);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/VisitsAtomView.java b/src/main/java/org/springframework/samples/petclinic/web/VisitsAtomView.java
new file mode 100644
index 000000000..e9da832e4
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/VisitsAtomView.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2002-2009 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.web;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.sun.syndication.feed.atom.Content;
+import com.sun.syndication.feed.atom.Entry;
+import com.sun.syndication.feed.atom.Feed;
+
+import org.springframework.samples.petclinic.Visit;
+import org.springframework.web.servlet.view.feed.AbstractAtomFeedView;
+
+/**
+ * A view creating a Atom representation from a list of Visit objects.
+ *
+ * @author Alef Arendsen
+ * @author Arjen Poutsma
+ */
+public class VisitsAtomView extends AbstractAtomFeedView {
+
+ @Override
+ protected void buildFeedMetadata(Map model, Feed feed, HttpServletRequest request) {
+ feed.setId("tag:springsource.com");
+ feed.setTitle("Pet Clinic Visits");
+ @SuppressWarnings("unchecked")
+ List visits = (List) model.get("visits");
+ for (Visit visit : visits) {
+ Date date = visit.getDate();
+ if (feed.getUpdated() == null || date.compareTo(feed.getUpdated()) > 0) {
+ feed.setUpdated(date);
+ }
+ }
+ }
+
+ @Override
+ protected List buildFeedEntries(Map model,
+ HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ @SuppressWarnings("unchecked")
+ List visits = (List) model.get("visits");
+ List entries = new ArrayList(visits.size());
+
+ for (Visit visit : visits) {
+ Entry entry = new Entry();
+ String date = String.format("%1$tY-%1$tm-%1$td", visit.getDate());
+ // see http://diveintomark.org/archives/2004/05/28/howto-atom-id#other
+ entry.setId(String.format("tag:springsource.com,%s:%d", date, visit.getId()));
+ entry.setTitle(String.format("%s visit on %s", visit.getPet().getName(), date));
+ entry.setUpdated(visit.getDate());
+
+ Content summary = new Content();
+ summary.setValue(visit.getDescription());
+ entry.setSummary(summary);
+
+ entries.add(entry);
+ }
+
+ return entries;
+
+ }
+
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/web/package-info.java b/src/main/java/org/springframework/samples/petclinic/web/package-info.java
new file mode 100644
index 000000000..c909ccf7f
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/web/package-info.java
@@ -0,0 +1,8 @@
+
+/**
+ *
+ * The classes in this package represent PetClinic's web presentation layer.
+ *
+ */
+package org.springframework.samples.petclinic.web;
+
diff --git a/src/main/java/overview.html b/src/main/java/overview.html
new file mode 100644
index 000000000..1eb7a2e8c
--- /dev/null
+++ b/src/main/java/overview.html
@@ -0,0 +1,7 @@
+
+
+
+The Spring Data Binding framework, an internal library used by Spring Web Flow.
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/jdbc.properties b/src/main/resources/jdbc.properties
new file mode 100644
index 000000000..e039cdf1b
--- /dev/null
+++ b/src/main/resources/jdbc.properties
@@ -0,0 +1,63 @@
+# Properties file with JDBC and JPA settings.
+#
+# Applied by from
+# various application context XML files (e.g., "applicationContext-*.xml").
+# Targeted at system administrators, to avoid touching the context XML files.
+
+
+#-------------------------------------------------------------------------------
+# Common Settings
+
+hibernate.generate_statistics=true
+hibernate.show_sql=true
+jpa.showSql=true
+
+
+#-------------------------------------------------------------------------------
+# HSQL Settings
+
+jdbc.driverClassName=org.hsqldb.jdbcDriver
+jdbc.url=jdbc:hsqldb:mem:petclinic
+jdbc.username=sa
+jdbc.password=
+
+# Properties that control the population of schema and data for a new data source
+jdbc.populate=true
+jdbc.schemaLocation=classpath:/META-INF/hsqldb/initDB.txt
+jdbc.dataLocation=classpath:/META-INF/hsqldb/populateDB.txt
+jdbc.dropLocation=classpath:/META-INF/hsqldb/dropTables.txt
+
+# Property that determines which Hibernate dialect to use
+# (only applied with "applicationContext-hibernate.xml")
+hibernate.dialect=org.hibernate.dialect.HSQLDialect
+
+# Property that determines which JPA DatabasePlatform to use with TopLink Essentials
+jpa.databasePlatform=org.springframework.samples.petclinic.toplink.EssentialsHSQLPlatformWithNativeSequence
+
+# Property that determines which database to use with an AbstractJpaVendorAdapter
+jpa.database=HSQL
+
+
+#-------------------------------------------------------------------------------
+# MySQL Settings
+
+#jdbc.driverClassName=com.mysql.jdbc.Driver
+#jdbc.url=jdbc:mysql://localhost:3306/petclinic
+#jdbc.username=pc
+#jdbc.password=pc
+
+# Properties that control the population of schema and data for a new data source
+#jdbc.populate=false
+#jdbc.schemaLocation=
+#jdbc.dataLocation=
+#jdbc.dropLocation=
+
+# Property that determines which Hibernate dialect to use
+# (only applied with "applicationContext-hibernate.xml")
+#hibernate.dialect=org.hibernate.dialect.MySQLDialect
+
+# Property that determines which JPA DatabasePlatform to use with TopLink Essentials
+#jpa.databasePlatform=oracle.toplink.essentials.platform.database.MySQL4Platform
+
+# Property that determines which database to use with an AbstractJpaVendorAdapter
+#jpa.database=MYSQL
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 000000000..ebee551aa
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,18 @@
+# For JBoss: Avoid to setup Log4J outside $JBOSS_HOME/server/default/deploy/log4j.xml!
+# For all other servers: Comment out the Log4J listener in web.xml to activate Log4J.
+log4j.rootLogger=INFO, stdout, logfile
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
+
+log4j.appender.logfile=org.apache.log4j.RollingFileAppender
+log4j.appender.logfile.File=${petclinic.root}/WEB-INF/petclinic.log
+log4j.appender.logfile.MaxFileSize=512KB
+# Keep three backup files.
+log4j.appender.logfile.MaxBackupIndex=3
+# Pattern to output: date priority [category] - message
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
+
+log4j.logger.org.springframework.samples.petclinic.aspects=DEBUG
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
new file mode 100644
index 000000000..173417a10
--- /dev/null
+++ b/src/main/resources/messages.properties
@@ -0,0 +1,8 @@
+welcome=Welcome
+required=is required
+notFound=has not been found
+duplicate=is already in use
+nonNumeric=must be all numeric
+duplicateFormSubmission=Duplicate form submission is not allowed
+typeMismatch.date=invalid date
+typeMismatch.birthDate=invalid date
diff --git a/src/main/resources/messages_de.properties b/src/main/resources/messages_de.properties
new file mode 100644
index 000000000..124bee48b
--- /dev/null
+++ b/src/main/resources/messages_de.properties
@@ -0,0 +1,8 @@
+welcome=Willkommen
+required=muss angegeben werden
+notFound=wurde nicht gefunden
+duplicate=ist bereits vergeben
+nonNumeric=darf nur numerisch sein
+duplicateFormSubmission=Wiederholtes Absenden des Formulars ist nicht erlaubt
+typeMismatch.date=ungültiges Datum
+typeMismatch.birthDate=ungültiges Datum
diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties
new file mode 100644
index 000000000..05d519bb8
--- /dev/null
+++ b/src/main/resources/messages_en.properties
@@ -0,0 +1 @@
+# This file is intentionally empty. Message look-ups will fall back to the default "messages.properties" file.
\ No newline at end of file
diff --git a/src/main/resources/petclinic.hbm.xml b/src/main/resources/petclinic.hbm.xml
new file mode 100644
index 000000000..f9a993cf1
--- /dev/null
+++ b/src/main/resources/petclinic.hbm.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/META-INF/aop.xml b/src/main/webapp/META-INF/aop.xml
new file mode 100644
index 000000000..b49ffd8b1
--- /dev/null
+++ b/src/main/webapp/META-INF/aop.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/META-INF/context.xml b/src/main/webapp/META-INF/context.xml
new file mode 100644
index 000000000..d5deabaf7
--- /dev/null
+++ b/src/main/webapp/META-INF/context.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/src/main/webapp/META-INF/hsqldb/dropTables.txt b/src/main/webapp/META-INF/hsqldb/dropTables.txt
new file mode 100644
index 000000000..90ae6329f
--- /dev/null
+++ b/src/main/webapp/META-INF/hsqldb/dropTables.txt
@@ -0,0 +1,7 @@
+DROP TABLE visits;
+DROP TABLE pets;
+DROP TABLE owners;
+DROP TABLE types;
+DROP TABLE vet_specialties;
+DROP TABLE specialties;
+DROP TABLE vets;
diff --git a/src/main/webapp/META-INF/hsqldb/initDB.txt b/src/main/webapp/META-INF/hsqldb/initDB.txt
new file mode 100644
index 000000000..a75bfbbd8
--- /dev/null
+++ b/src/main/webapp/META-INF/hsqldb/initDB.txt
@@ -0,0 +1,55 @@
+CREATE TABLE vets (
+ id INTEGER NOT NULL IDENTITY PRIMARY KEY,
+ first_name VARCHAR(30),
+ last_name VARCHAR(30)
+);
+CREATE INDEX vets_last_name ON vets(last_name);
+
+CREATE TABLE specialties (
+ id INTEGER NOT NULL IDENTITY PRIMARY KEY,
+ name VARCHAR(80)
+);
+CREATE INDEX specialties_name ON specialties(name);
+
+CREATE TABLE vet_specialties (
+ vet_id INTEGER NOT NULL,
+ specialty_id INTEGER NOT NULL
+);
+alter table vet_specialties add constraint fk_vet_specialties_vets foreign key (vet_id) references vets(id);
+alter table vet_specialties add constraint fk_vet_specialties_specialties foreign key (specialty_id) references specialties(id);
+
+CREATE TABLE types (
+ id INTEGER NOT NULL IDENTITY PRIMARY KEY,
+ name VARCHAR(80)
+);
+CREATE INDEX types_name ON types(name);
+
+CREATE TABLE owners (
+ id INTEGER NOT NULL IDENTITY PRIMARY KEY,
+ first_name VARCHAR(30),
+ last_name VARCHAR(30),
+ address VARCHAR(255),
+ city VARCHAR(80),
+ telephone VARCHAR(20)
+);
+CREATE INDEX owners_last_name ON owners(last_name);
+
+CREATE TABLE pets (
+ id INTEGER NOT NULL IDENTITY PRIMARY KEY,
+ name VARCHAR(30),
+ birth_date DATE,
+ type_id INTEGER NOT NULL,
+ owner_id INTEGER NOT NULL
+);
+alter table pets add constraint fk_pets_owners foreign key (owner_id) references owners(id);
+alter table pets add constraint fk_pets_types foreign key (type_id) references types(id);
+CREATE INDEX pets_name ON pets(name);
+
+CREATE TABLE visits (
+ id INTEGER NOT NULL IDENTITY PRIMARY KEY,
+ pet_id INTEGER NOT NULL,
+ visit_date DATE,
+ description VARCHAR(255)
+);
+alter table visits add constraint fk_visits_pets foreign key (pet_id) references pets(id);
+CREATE INDEX visits_pet_id ON visits(pet_id);
diff --git a/src/main/webapp/META-INF/hsqldb/populateDB.txt b/src/main/webapp/META-INF/hsqldb/populateDB.txt
new file mode 100644
index 000000000..1bf0c4a6e
--- /dev/null
+++ b/src/main/webapp/META-INF/hsqldb/populateDB.txt
@@ -0,0 +1,53 @@
+INSERT INTO vets VALUES (1, 'James', 'Carter');
+INSERT INTO vets VALUES (2, 'Helen', 'Leary');
+INSERT INTO vets VALUES (3, 'Linda', 'Douglas');
+INSERT INTO vets VALUES (4, 'Rafael', 'Ortega');
+INSERT INTO vets VALUES (5, 'Henry', 'Stevens');
+INSERT INTO vets VALUES (6, 'Sharon', 'Jenkins');
+
+INSERT INTO specialties VALUES (1, 'radiology');
+INSERT INTO specialties VALUES (2, 'surgery');
+INSERT INTO specialties VALUES (3, 'dentistry');
+
+INSERT INTO vet_specialties VALUES (2, 1);
+INSERT INTO vet_specialties VALUES (3, 2);
+INSERT INTO vet_specialties VALUES (3, 3);
+INSERT INTO vet_specialties VALUES (4, 2);
+INSERT INTO vet_specialties VALUES (5, 1);
+
+INSERT INTO types VALUES (1, 'cat');
+INSERT INTO types VALUES (2, 'dog');
+INSERT INTO types VALUES (3, 'lizard');
+INSERT INTO types VALUES (4, 'snake');
+INSERT INTO types VALUES (5, 'bird');
+INSERT INTO types VALUES (6, 'hamster');
+
+INSERT INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
+INSERT INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');
+INSERT INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');
+INSERT INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');
+INSERT INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');
+INSERT INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');
+INSERT INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');
+INSERT INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');
+INSERT INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');
+INSERT INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');
+
+INSERT INTO pets VALUES (1, 'Leo', '2000-09-07', 1, 1);
+INSERT INTO pets VALUES (2, 'Basil', '2002-08-06', 6, 2);
+INSERT INTO pets VALUES (3, 'Rosy', '2001-04-17', 2, 3);
+INSERT INTO pets VALUES (4, 'Jewel', '2000-03-07', 2, 3);
+INSERT INTO pets VALUES (5, 'Iggy', '2000-11-30', 3, 4);
+INSERT INTO pets VALUES (6, 'George', '2000-01-20', 4, 5);
+INSERT INTO pets VALUES (7, 'Samantha', '1995-09-04', 1, 6);
+INSERT INTO pets VALUES (8, 'Max', '1995-09-04', 1, 6);
+INSERT INTO pets VALUES (9, 'Lucky', '1999-08-06', 5, 7);
+INSERT INTO pets VALUES (10, 'Mulligan', '1997-02-24', 2, 8);
+INSERT INTO pets VALUES (11, 'Freddy', '2000-03-09', 5, 9);
+INSERT INTO pets VALUES (12, 'Lucky', '2000-06-24', 2, 10);
+INSERT INTO pets VALUES (13, 'Sly', '2002-06-08', 1, 10);
+
+INSERT INTO visits VALUES (1, 7, '1996-03-04', 'rabies shot');
+INSERT INTO visits VALUES (2, 8, '1996-03-04', 'rabies shot');
+INSERT INTO visits VALUES (3, 8, '1996-06-04', 'neutered');
+INSERT INTO visits VALUES (4, 7, '1996-09-04', 'spayed');
diff --git a/src/main/webapp/META-INF/orm.xml b/src/main/webapp/META-INF/orm.xml
new file mode 100644
index 000000000..d7c8f7040
--- /dev/null
+++ b/src/main/webapp/META-INF/orm.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+ PROPERTY
+
+
+
+ org.springframework.samples.petclinic
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+