SPR-6448
+ commit the gross of the files
+ added maven pom
This commit is contained in:
Costin Leau 2009-11-25 18:45:52 +00:00 committed by Mic
parent 9dd07f05f3
commit 521d01db95
117 changed files with 6945 additions and 10 deletions

85
db/build.xml Normal file
View file

@ -0,0 +1,85 @@
<?xml version="1.0"?>
<project name="setupDB" basedir="." default="all">
<target name="dropHSQLTables" if="useHSQL">
<echo message="Drop tables using: ${db.driver} ${db.url}" />
<sql driver="${db.driver}" url="${db.url}" userid="${db.user}" password="${db.pw}" onerror="continue">
<classpath>
<fileset dir="${spring.root}/lib">
<include name="hsqldb/hsqldb.jar" />
</fileset>
</classpath>
<transaction src="${db.dir}/dropTables.txt" />
</sql>
</target>
<target name="createHSQLTables" if="useHSQL">
<echo message="Create tables using: ${db.driver} ${db.url}" />
<sql driver="${db.driver}" url="${db.url}" userid="${db.user}" password="${db.pw}" onerror="continue">
<classpath>
<fileset dir="${spring.root}/lib">
<include name="hsqldb/hsqldb.jar" />
</fileset>
</classpath>
<transaction src="${db.dir}/hsqldb/initDB.txt" />
</sql>
</target>
<target name="dropMYSQLTables" if="useMYSQL">
<echo message="Dropping tables using: ${db.driver} ${db.url}" />
<sql driver="${db.driver}" url="${db.url}" userid="${db.user}" password="${db.pw}" onerror="continue">
<classpath>
<fileset dir="${db.dir}/mysql">
<include name="mysql*.jar" />
</fileset>
</classpath>
<transaction src="${db.dir}/dropTables.txt" />
</sql>
</target>
<target name="createMYSQLTables" if="useMYSQL">
<echo message="Creating tables using: ${db.driver} ${db.url}" />
<sql driver="${db.driver}" url="${db.url}" userid="${db.user}" password="${db.pw}" onerror="continue">
<classpath>
<fileset dir="${db.dir}/mysql">
<include name="mysql*.jar" />
</fileset>
</classpath>
<transaction src="${db.dir}/mysql/initDB.txt" />
</sql>
</target>
<target name="emptyTables">
<echo message="Emptying tables using: ${db.driver} ${db.url}" />
<sql driver="${db.driver}" url="${db.url}" userid="${db.user}" password="${db.pw}">
<classpath>
<fileset dir="${spring.root}/lib">
<include name="hsqldb/hsqldb.jar" />
</fileset>
<fileset dir="${db.dir}/mysql">
<include name="mysql*.jar" />
</fileset>
</classpath>
<transaction src="${db.dir}/emptyDB.txt" />
</sql>
</target>
<target name="populateTables">
<echo message="Populating tables using: ${db.driver} ${db.url}" />
<sql driver="${db.driver}" url="${db.url}" userid="${db.user}" password="${db.pw}">
<classpath>
<fileset dir="${spring.root}/lib">
<include name="hsqldb/hsqldb.jar" />
</fileset>
<fileset dir="${db.dir}/mysql">
<include name="mysql*.jar" />
</fileset>
</classpath>
<transaction src="${db.dir}/populateDB.txt" />
</sql>
</target>
<target name="all" depends="dropHSQLTables,createHSQLTables,dropMYSQLTables,createMYSQLTables,emptyTables,populateTables" />
</project>

7
db/dropTables.txt Normal file
View file

@ -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;

7
db/emptyDB.txt Normal file
View file

@ -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;

3
db/mysql/createDB.txt Normal file
View file

@ -0,0 +1,3 @@
CREATE DATABASE petclinic;
GRANT ALL PRIVILEGES ON petclinic.* TO pc@localhost IDENTIFIED BY 'pc';

1
db/mysql/dropDB.txt Normal file
View file

@ -0,0 +1 @@
DROP DATABASE petclinic;

58
db/mysql/initDB.txt Normal file
View file

@ -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);

View file

@ -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.

View file

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Context path="/petclinic" docBase="petclinic" debug="4" reloadable="true">
<Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_petclinic_log." suffix=".txt" timestamp="true"/>
<!-- Define a database connection pool for MYSQL -->
<Resource name="jdbc/petclinicMYSQL" auth="Container" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/petclinicMYSQL">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<parameter>
<name>driverClassName</name>
<value>org.gjt.mm.mysql.Driver</value>
</parameter>
<!--
The JDBC connection url for connecting to your MySQL dB.
The autoReconnect=true argument to the url makes sure that the
mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
connection. mysqld by default closes idle connections after 8 hours.
-->
<parameter>
<name>url</name>
<value>jdbc:mysql://localhost:3306/petclinic?autoReconnect=true</value>
</parameter>
<parameter>
<name>username</name>
<value>pc</value>
</parameter>
<parameter>
<name>password</name>
<value>pc</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>50</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>10</value>
</parameter>
<parameter>
<name>maxWait</name>
<value>10000</value>
</parameter>
<parameter>
<name>removeAbandoned</name>
<value>true</value>
</parameter>
<parameter>
<name>removeAbandonedTimeout</name>
<value>60</value>
</parameter>
<parameter>
<name>logAbandoned</name>
<value>true</value>
</parameter>
</ResourceParams>
</Context>

112
db/petclinic_tomcat_all.xml Normal file
View file

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Context path="/petclinic" docBase="petclinic" debug="4" reloadable="true">
<Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_petclinic_log." suffix=".txt" timestamp="true"/>
<!-- Define a database connection pool for HSQL -->
<!-- NOTE: make sure that a copy of hsqldb.jar is in the TOMCAT common/lib directory -->
<Resource name="jdbc/petclinicHSQL" auth="Container" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/petclinicHSQL">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<parameter>
<name>driverClassName</name>
<value>org.hsqldb.jdbcDriver</value>
</parameter>
<parameter>
<name>url</name>
<value>jdbc:hsqldb:hsql://localhost:9001</value>
</parameter>
<parameter>
<name>username</name>
<value>sa</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>50</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>10</value>
</parameter>
<parameter>
<name>maxWait</name>
<value>10000</value>
</parameter>
<parameter>
<name>removeAbandoned</name>
<value>true</value>
</parameter>
<parameter>
<name>removeAbandonedTimeout</name>
<value>60</value>
</parameter>
<parameter>
<name>logAbandoned</name>
<value>true</value>
</parameter>
</ResourceParams>
<!-- Define a database connection pool for MYSQL -->
<Resource name="jdbc/petclinicMYSQL" auth="Container" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/petclinicMYSQL">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<parameter>
<name>driverClassName</name>
<value>org.gjt.mm.mysql.Driver</value>
</parameter>
<!--
The JDBC connection url for connecting to your MySQL dB.
The autoReconnect=true argument to the url makes sure that the
mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
connection. mysqld by default closes idle connections after 8 hours.
-->
<parameter>
<name>url</name>
<value>jdbc:mysql://localhost:3306/petclinic?autoReconnect=true</value>
</parameter>
<parameter>
<name>username</name>
<value>pc</value>
</parameter>
<parameter>
<name>password</name>
<value>pc</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>50</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>10</value>
</parameter>
<parameter>
<name>maxWait</name>
<value>10000</value>
</parameter>
<parameter>
<name>removeAbandoned</name>
<value>true</value>
</parameter>
<parameter>
<name>removeAbandonedTimeout</name>
<value>60</value>
</parameter>
<parameter>
<name>logAbandoned</name>
<value>true</value>
</parameter>
</ResourceParams>
</Context>

53
db/populateDB.txt Normal file
View file

@ -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');

233
pom.xml Normal file
View file

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>org.springframework.samples.petclinic</artifactId>
<name>petclinic-classic</name>
<packaging>war</packaging>
<version>1.0.0-SNAPSHOT</version>
<properties>
<spring.version>3.0.0.RC2</spring.version>
<slf4j.version>1.5.6</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>com.oracle.toplink.essentials</groupId>
<artifactId>com.springsource.oracle.toplink.essentials</artifactId>
<version>2.0.0.b41-beta2</version>
</dependency>
<dependency>
<groupId>com.sun.syndication</groupId>
<artifactId>com.springsource.com.sun.syndication</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>com.springsource.javax.persistence</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>com.springsource.javax.servlet</artifactId>
<version>2.5.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>com.springsource.javax.servlet.jsp</artifactId>
<version>2.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>com.springsource.javax.servlet.jsp.jstl</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>com.springsource.javax.xml.bind</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.dbcp</artifactId>
<version>1.2.2.osgi</version>
</dependency>
<dependency>
<groupId>org.apache.log4j</groupId>
<artifactId>com.springsource.org.apache.log4j</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>com.springsource.org.apache.openjpa</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>com.springsource.org.apache.taglibs.standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>com.springsource.org.hibernate</artifactId>
<version>3.3.1.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>com.springsource.org.hibernate.ejb</artifactId>
<version>3.4.0.GA</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>com.springsource.org.hsqldb</artifactId>
<version>1.8.0.9</version>
</dependency>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>com.springsource.org.jdom</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.asm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.web.servlet</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.junit</groupId>
<artifactId>com.springsource.org.junit</artifactId>
<version>4.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>com.springsource.javax.transaction</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>com.springsource.org.hibernate.annotations</artifactId>
<version>3.4.0.GA</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>com.springsource.slf4j.org.apache.commons.logging</artifactId>
<version>${slf4j.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>com.springsource.slf4j.api</artifactId>
<version>${slf4j.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>com.springsource.slf4j.log4j</artifactId>
<version>${slf4j.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.log4j</groupId>
<artifactId>com.springsource.org.apache.log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<repositories>
<repository>
<id>com.springsource.repository.bundles.release</id>
<name>SpringSource Enterprise Bundle Repository - SpringSource Releases</name>
<url>http://repository.springsource.com/maven/bundles/release</url>
</repository>
<repository>
<id>com.springsource.repository.bundles.milestone</id>
<name>SpringSource Enterprise Bundle Repository - SpringSource Milestones</name>
<url>http://repository.springsource.com/maven/bundles/milestone</url>
</repository>
<repository>
<id>com.springsource.repository.bundles.external</id>
<name>SpringSource Enterprise Bundle Repository - External Releases</name>
<url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
<repository>
<id>com.springsource.repository.bundles.snapshot</id>
<name>SpringSource Enterprise Bundle Repository - Snapshot Releases</name>
<url>http://repository.springsource.com/maven/bundles/snapshot</url>
</repository>
<repository>
<id>spring-release</id>
<name>Spring Portfolio Release Repository</name>
<url>http://maven.springframework.org/release</url>
</repository>
<repository>
<id>spring-external</id>
<name>Spring Portfolio External Repository</name>
<url>http://maven.springframework.org/external</url>
</repository>
<repository>
<id>spring-milestone</id>
<name>Spring Portfolio Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>install</id>
<phase>install</phase>
<goals>
<goal>sources</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -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.

View file

@ -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);
}
}

View file

@ -0,0 +1,82 @@
package org.springframework.samples.petclinic;
import java.util.Collection;
import org.springframework.dao.DataAccessException;
/**
* The high-level PetClinic business interface.
*
* <p>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 <code>Vet</code>s from the data store.
* @return a <code>Collection</code> of <code>Vet</code>s
*/
Collection<Vet> getVets() throws DataAccessException;
/**
* Retrieve all <code>PetType</code>s from the data store.
* @return a <code>Collection</code> of <code>PetType</code>s
*/
Collection<PetType> getPetTypes() throws DataAccessException;
/**
* Retrieve <code>Owner</code>s from the data store by last name,
* returning all owners whose last name <i>starts</i> with the given name.
* @param lastName Value to search for
* @return a <code>Collection</code> of matching <code>Owner</code>s
* (or an empty <code>Collection</code> if none found)
*/
Collection<Owner> findOwners(String lastName) throws DataAccessException;
/**
* Retrieve an <code>Owner</code> from the data store by id.
* @param id the id to search for
* @return the <code>Owner</code> if found
* @throws org.springframework.dao.DataRetrievalFailureException if not found
*/
Owner loadOwner(int id) throws DataAccessException;
/**
* Retrieve a <code>Pet</code> from the data store by id.
* @param id the id to search for
* @return the <code>Pet</code> if found
* @throws org.springframework.dao.DataRetrievalFailureException if not found
*/
Pet loadPet(int id) throws DataAccessException;
/**
* Save an <code>Owner</code> to the data store, either inserting or updating it.
* @param owner the <code>Owner</code> to save
* @see BaseEntity#isNew
*/
void storeOwner(Owner owner) throws DataAccessException;
/**
* Save a <code>Pet</code> to the data store, either inserting or updating it.
* @param pet the <code>Pet</code> to save
* @see BaseEntity#isNew
*/
void storePet(Pet pet) throws DataAccessException;
/**
* Save a <code>Visit</code> to the data store, either inserting or updating it.
* @param visit the <code>Visit</code> to save
* @see BaseEntity#isNew
*/
void storeVisit(Visit visit) throws DataAccessException;
/**
* Deletes a <code>Pet</code> from the data store.
*/
void deletePet(int id) throws DataAccessException;
}

View file

@ -0,0 +1,28 @@
package org.springframework.samples.petclinic;
/**
* Simple JavaBean domain object adds a name property to <code>BaseEntity</code>.
* 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();
}
}

View file

@ -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<Pet> 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<Pet> pets) {
this.pets = pets;
}
protected Set<Pet> getPetsInternal() {
if (this.pets == null) {
this.pets = new HashSet<Pet>();
}
return this.pets;
}
public List<Pet> getPets() {
List<Pet> sortedPets = new ArrayList<Pet>(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();
}
}

View file

@ -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;
}
}

View file

@ -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<Visit> 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<Visit> visits) {
this.visits = visits;
}
protected Set<Visit> getVisitsInternal() {
if (this.visits == null) {
this.visits = new HashSet<Visit>();
}
return this.visits;
}
public List<Visit> getVisits() {
List<Visit> sortedVisits = new ArrayList<Visit>(getVisitsInternal());
PropertyComparator.sort(sortedVisits, new MutableSortDefinition("date", false, false));
return Collections.unmodifiableList(sortedVisits);
}
public void addVisit(Visit visit) {
getVisitsInternal().add(visit);
visit.setPet(this);
}
}

View file

@ -0,0 +1,8 @@
package org.springframework.samples.petclinic;
/**
* @author Juergen Hoeller
*/
public class PetType extends NamedEntity {
}

View file

@ -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 {
}

View file

@ -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<Specialty> specialties;
protected void setSpecialtiesInternal(Set<Specialty> specialties) {
this.specialties = specialties;
}
protected Set<Specialty> getSpecialtiesInternal() {
if (this.specialties == null) {
this.specialties = new HashSet<Specialty>();
}
return this.specialties;
}
@XmlElement
public List<Specialty> getSpecialties() {
List<Specialty> sortedSpecs = new ArrayList<Specialty>(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);
}
}

View file

@ -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<Vet> vets;
@XmlElement
public List<Vet> getVetList() {
if (vets == null) {
vets = new ArrayList<Vet>();
}
return vets;
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}
}

View file

@ -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();
}
}
}

View file

@ -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<String> namesRequested = new ArrayList<String>(this.historySize);
public synchronized void setHistorySize(int historySize) {
this.historySize = historySize;
this.namesRequested = new ArrayList<String>(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<String> getNamesRequested() {
return Collections.unmodifiableList(this.namesRequested);
}
}

View file

@ -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<DataSource>, 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<DataSource> 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);
}
}
}

View file

@ -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.
*
* <p>The mappings are defined in "petclinic.hbm.xml", located in the root of the
* class path.
*
* <p>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<Vet> getVets() {
return sessionFactory.getCurrentSession().createQuery("from Vet vet order by vet.lastName, vet.firstName").list();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<PetType> getPetTypes() {
return sessionFactory.getCurrentSession().createQuery("from PetType type order by type.name").list();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<Owner> 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);
}
}

View file

@ -0,0 +1,9 @@
/**
*
* The classes in this package represent the Hibernate implementation
* of PetClinic's persistence layer.
*
*/
package org.springframework.samples.petclinic.hibernate;

View file

@ -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;
}
}

View file

@ -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.
*
* <p>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.
*
* <p>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<Vet> vets = new ArrayList<Vet>();
@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<Specialty> 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<Integer> vetSpecialtiesIds = this.simpleJdbcTemplate.query(
"SELECT specialty_id FROM vet_specialties WHERE vet_id=?",
new ParameterizedRowMapper<Integer>() {
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<Vet> getVets() throws DataAccessException {
synchronized (this.vets) {
if (this.vets.isEmpty()) {
refreshVetsCache();
}
return this.vets;
}
}
@Transactional(readOnly = true)
public Collection<PetType> 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 <i>starts</i> 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<Owner> findOwners(String lastName) throws DataAccessException {
List<Owner> 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 <code>id</code>; 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<Visit> visits = this.simpleJdbcTemplate.query(
"SELECT id, visit_date, description FROM visits WHERE pet_id=?",
new ParameterizedRowMapper<Visit>() {
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<JdbcPet> 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<Owner> 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<JdbcPet> {
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;
}
}
}

View file

@ -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();
}

View file

@ -0,0 +1,9 @@
/**
*
* The classes in this package represent the JDBC implementation
* of PetClinic's persistence layer.
*
*/
package org.springframework.samples.petclinic.jdbc;

View file

@ -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.
*
* <p>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<Vet> getVets() {
return this.em.createQuery("SELECT vet FROM Vet vet ORDER BY vet.lastName, vet.firstName").getResultList();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<PetType> getPetTypes() {
return this.em.createQuery("SELECT ptype FROM PetType ptype ORDER BY ptype.name").getResultList();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<Owner> 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);
}
}

View file

@ -0,0 +1,9 @@
/**
*
* The classes in this package represent the JPA implementation
* of PetClinic's persistence layer.
*
*/
package org.springframework.samples.petclinic.jpa;

View file

@ -0,0 +1,8 @@
/**
*
* The classes in this package represent PetClinic's business layer.
*
*/
package org.springframework.samples.petclinic;

View file

@ -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.
*
* <p>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 <a href="mailto:james.x.clark@oracle.com">James Clark</a>
* @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);
}
}
}

View file

@ -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;

View file

@ -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 extends BaseEntity> T getById(Collection<T> entities, Class<T> 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));
}
}

View file

@ -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;
/**
* <code>Validator</code> for <code>Owner</code> 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;
}
}
}
}
}

View file

@ -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;
/**
* <code>Validator</code> for <code>Pet</code> 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");
}
}
}

View file

@ -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;
/**
* <code>Validator</code> for <code>Visit</code> 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");
}
}
}

View file

@ -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;

View file

@ -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 <code>Owner</code> 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();
}
}
}

View file

@ -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 <code>Pet</code> 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<PetType> 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();
}
}
}

View file

@ -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 <code>Visit</code> 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();
}
}
}

View file

@ -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.
*
* <p>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));
}
}

View file

@ -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 <em>MultiActionController</em> 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.
* <p>
* Note that this handler relies on the RequestToViewNameTranslator to
* determine the logical view name based on the request URL: "/welcome.do"
* -&gt; "welcome".
*/
@RequestMapping("/")
public String welcomeHandler() {
return "welcome";
}
/**
* Custom handler for displaying vets.
*
* <p>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" -&gt; "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;
}
}

View file

@ -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 <code>Owner</code>.
*
* @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();
}
}
}

View file

@ -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 <code>Pet</code>.
*
* @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<PetType> 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();
}
}

View file

@ -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 <code>Owner</code>s 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<Owner> 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();
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -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<String, Object> model, Feed feed, HttpServletRequest request) {
feed.setId("tag:springsource.com");
feed.setTitle("Pet Clinic Visits");
@SuppressWarnings("unchecked")
List<Visit> visits = (List<Visit>) 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<Entry> buildFeedEntries(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) throws Exception {
@SuppressWarnings("unchecked")
List<Visit> visits = (List<Visit>) model.get("visits");
List<Entry> entries = new ArrayList<Entry>(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;
}
}

View file

@ -0,0 +1,8 @@
/**
*
* The classes in this package represent PetClinic's web presentation layer.
*
*/
package org.springframework.samples.petclinic.web;

View file

@ -0,0 +1,7 @@
<html>
<body>
<p>
The Spring Data Binding framework, an internal library used by Spring Web Flow.
</p>
</body>
</html>

View file

@ -0,0 +1,63 @@
# Properties file with JDBC and JPA settings.
#
# Applied by <context:property-placeholder location="jdbc.properties"/> 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1 @@
# This file is intentionally empty. Message look-ups will fall back to the default "messages.properties" file.

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
- Mapping file for the Hibernate implementation of the Clinic interface.
-->
<hibernate-mapping auto-import="true" default-lazy="false">
<class name="org.springframework.samples.petclinic.Vet" table="vets">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
<set name="specialtiesInternal" table="vet_specialties">
<key column="vet_id"/>
<many-to-many column="specialty_id" class="org.springframework.samples.petclinic.Specialty"/>
</set>
</class>
<class name="org.springframework.samples.petclinic.Specialty" table="specialties">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
</class>
<class name="org.springframework.samples.petclinic.Owner" table="owners">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
<property name="address" column="address"/>
<property name="city" column="city"/>
<property name="telephone" column="telephone"/>
<set name="petsInternal" inverse="true" cascade="all">
<key column="owner_id"/>
<one-to-many class="org.springframework.samples.petclinic.Pet"/>
</set>
</class>
<class name="org.springframework.samples.petclinic.Pet" table="pets">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
<property name="birthDate" column="birth_date" type="date"/>
<many-to-one name="owner" column="owner_id" class="org.springframework.samples.petclinic.Owner"/>
<many-to-one name="type" column="type_id" class="org.springframework.samples.petclinic.PetType"/>
<set name="visitsInternal" inverse="true" cascade="all">
<key column="pet_id"/>
<one-to-many class="org.springframework.samples.petclinic.Visit"/>
</set>
</class>
<class name="org.springframework.samples.petclinic.PetType" table="types">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
</class>
<class name="org.springframework.samples.petclinic.Visit" table="visits">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="date" column="visit_date" type="date"/>
<property name="description" column="description"/>
<many-to-one name="pet" column="pet_id" class="org.springframework.samples.petclinic.Pet"/>
</class>
</hibernate-mapping>

View file

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!-- Custom aspects for the PetClinic sample application -->
<aspectj>
<weaver>
<include within="org.springframework.samples.petclinic..*"/>
</weaver>
<aspects>
<aspect name="org.springframework.samples.petclinic.aspects.UsageLogAspect"/>
<concrete-aspect name="org.springframework.samples.petclinic.aspects.ApplicationTraceAspect"
extends="org.springframework.samples.petclinic.aspects.AbstractTraceAspect">
<pointcut name="traced" expression="execution(* org.springframework.samples..*.*(..))"/>
</concrete-aspect>
</aspects>
</aspectj>

View file

@ -0,0 +1,7 @@
<!-- Tomcat context descriptor used for specifying a custom ClassLoader -->
<Context path="/petclinic" reloadable="false">
<!-- please note that useSystemClassLoaderAsParent is available since Tomcat 5.5.20 / remove if previous versions are being used -->
<!--
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" useSystemClassLoaderAsParent="false"/>
-->
</Context>

View file

@ -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;

View file

@ -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);

View file

@ -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');

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
version="1.0">
<persistence-unit-metadata>
<xml-mapping-metadata-complete/>
<persistence-unit-defaults>
<access>PROPERTY</access>
</persistence-unit-defaults>
</persistence-unit-metadata>
<package>org.springframework.samples.petclinic</package>
<mapped-superclass class="BaseEntity">
<attributes>
<id name="id">
<generated-value strategy="IDENTITY"/>
</id>
<transient name="new"/>
</attributes>
</mapped-superclass>
<mapped-superclass class="NamedEntity">
<attributes>
<basic name="name">
<column name="NAME"/>
</basic>
</attributes>
</mapped-superclass>
<mapped-superclass class="Person">
<attributes>
<basic name="firstName">
<column name="FIRST_NAME"/>
</basic>
<basic name="lastName">
<column name="LAST_NAME"/>
</basic>
</attributes>
</mapped-superclass>
<entity class="Vet">
<table name="VETS"/>
<attributes>
<many-to-many name="specialtiesInternal" target-entity="Specialty" fetch="EAGER">
<join-table name="VET_SPECIALTIES">
<join-column name="VET_ID"/>
<inverse-join-column name="SPECIALTY_ID"/>
</join-table>
</many-to-many>
<transient name="specialties"/>
<transient name="nrOfSpecialties"/>
</attributes>
</entity>
<entity class="Specialty">
<table name="SPECIALTIES"/>
</entity>
<entity class="Owner">
<table name="OWNERS"/>
<attributes>
<basic name="address"/>
<basic name="city"/>
<basic name="telephone"/>
<one-to-many name="petsInternal" target-entity="Pet" mapped-by="owner" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
<transient name="pets"/>
</attributes>
</entity>
<entity class="Pet">
<table name="PETS"/>
<attributes>
<basic name="birthDate">
<column name="BIRTH_DATE"/>
<temporal>DATE</temporal>
</basic>
<many-to-one name="owner" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</many-to-one>
<many-to-one name="type" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</many-to-one>
<one-to-many name="visitsInternal" target-entity="Visit" mapped-by="pet" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
<transient name="visits"/>
</attributes>
</entity>
<entity class="PetType">
<table name="TYPES"/>
</entity>
<entity class="Visit">
<table name="VISITS"/>
<attributes>
<basic name="date">
<column name="VISIT_DATE"/>
<temporal>DATE</temporal>
</basic>
<many-to-one name="pet" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</many-to-one>
</attributes>
</entity>
</entity-mappings>

View file

@ -0,0 +1,16 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="PetClinic" transaction-type="RESOURCE_LOCAL">
<!-- Explicitly define mapping file path, else Hibernate won't find the default -->
<mapping-file>META-INF/orm.xml</mapping-file>
<!-- Prevent annotation scanning. In this app we are purely driven by orm.xml -->
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Application context definition for PetClinic on Hibernate.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, JDBC-related settings for the dataSource definition below) -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--
Uses Apache Commons DBCP for connection pooling. See Commons DBCP documentation
for the required JAR files. Alternatively you can use another connection pool
such as C3P0, similarly configured using Spring.
-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!-- JNDI DataSource for JEE environments -->
<!--
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic"/>
-->
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
p:dataSource-ref="dataSource" p:mappingResources="petclinic.hbm.xml">
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
</props>
</property>
<property name="eventListeners">
<map>
<entry key="merge">
<bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
</entry>
</map>
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>
<!-- Transaction manager that delegates to JTA (for a transactional JNDI DataSource) -->
<!--
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
-->
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!--
Activates various annotations to be detected in bean classes:
Spring's @Required and @Autowired, as well as JSR 250's @Resource.
-->
<context:annotation-config/>
<!--
Instruct Spring to perform declarative transaction management
automatically on annotated classes.
-->
<tx:annotation-driven/>
<!--
Exporter that exposes the Hibernate statistics service via JMX. Autodetects the
service MBean, using its bean name as JMX object name.
-->
<context:mbean-export/>
<!-- PetClinic's central data access object: Hibernate implementation -->
<bean id="clinic" class="org.springframework.samples.petclinic.hibernate.HibernateClinic"/>
<!-- Hibernate's JMX statistics service -->
<bean name="petclinic:type=HibernateStatistics" class="org.hibernate.jmx.StatisticsService" autowire="byName"/>
</beans>

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Application context definition for PetClinic on JDBC.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, JDBC-related settings for the dataSource definition below) -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--
Spring FactoryBean that creates a DataSource using Apache Commons DBCP for connection
pooling. See Commons DBCP documentation for the required JAR files. This factory bean
can populate the data source with a schema and data scripts if configured to do so.
An alternate factory bean can be created for different connection pool implementations,
C3P0 for example.
-->
<bean id="dataSource" class="org.springframework.samples.petclinic.config.DbcpDataSourceFactory"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"
p:username="${jdbc.username}" p:password="${jdbc.password}" p:populate="${jdbc.populate}"
p:schemaLocation="${jdbc.schemaLocation}" p:dataLocation="${jdbc.dataLocation}"/>
<!-- JNDI DataSource for JEE environments -->
<!--
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic"/>
-->
<!-- Transaction manager for a single JDBC DataSource (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<!-- Transaction manager that delegates to JTA (for a transactional JNDI DataSource) -->
<!--
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
-->
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!--
Activates various annotations to be detected in bean classes: Spring's
@Required and @Autowired, as well as JSR 250's @PostConstruct,
@PreDestroy and @Resource (if available) and JPA's @PersistenceContext
and @PersistenceUnit (if available).
-->
<context:annotation-config/>
<!--
Instruct Spring to retrieve and apply @AspectJ aspects which are defined
as beans in this context (such as the CallMonitoringAspect below).
-->
<aop:aspectj-autoproxy/>
<!--
Instruct Spring to perform automatic transaction management on annotated classes.
The SimpleJdbcClinic implementation declares @Transactional annotations.
"proxy-target-class" is set because of SimpleJdbcClinic's @ManagedOperation usage.
-->
<tx:annotation-driven/>
<!--
Exporter that exposes the Clinic DAO and the CallMonitoringAspect via JMX,
based on the @ManagedResource, @ManagedAttribute, and @ManagedOperation annotations.
-->
<context:mbean-export/>
<!-- PetClinic's central data access object using Spring's SimpleJdbcTemplate -->
<bean id="clinic" class="org.springframework.samples.petclinic.jdbc.SimpleJdbcClinic"/>
<!-- Call monitoring aspect that monitors call count and call invocation time -->
<bean id="callMonitor" class="org.springframework.samples.petclinic.aspects.CallMonitoringAspect"/>
</beans>

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Application context definition for PetClinic on JPA.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!--
Activates a load-time weaver for the context. Any bean within the context that
implements LoadTimeWeaverAware (such as LocalContainerEntityManagerFactoryBean)
will receive a reference to the autodetected load-time weaver.
-->
<context:load-time-weaver/>
<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, JDBC-related settings for the dataSource definition below) -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--
Uses Apache Commons DBCP for connection pooling. See Commons DBCP documentation
for the required JAR files. Alternatively you can use another connection pool
such as C3P0, similarly configured using Spring.
-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!-- JNDI DataSource for JEE environments -->
<!--
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic"/>
-->
<!-- JPA EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource">
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
p:databasePlatform="${jpa.databasePlatform}" p:showSql="${jpa.showSql}"/>
<!--
<bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"
p:database="${jpa.database}" p:showSql="${jpa.showSql}"/>
-->
<!--
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:database="${jpa.database}" p:showSql="${jpa.showSql}"/>
-->
</property>
</bean>
<!-- Transaction manager for a single JPA EntityManagerFactory (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!--
Activates various annotations to be detected in bean classes: Spring's
@Required and @Autowired, as well as JSR 250's @PostConstruct,
@PreDestroy and @Resource (if available) and JPA's @PersistenceContext
and @PersistenceUnit (if available).
-->
<context:annotation-config/>
<!--
Instruct Spring to perform declarative transaction management
automatically on annotated classes.
-->
<tx:annotation-driven mode="aspectj"/>
<!--
Simply defining this bean will cause requests to owner names to be saved.
This aspect is defined in petclinic.jar's META-INF/aop.xml file.
Note that we can dependency inject this bean like any other bean.
-->
<bean class="org.springframework.samples.petclinic.aspects.UsageLogAspect" p:historySize="300"/>
<!--
Post-processor to perform exception translation on @Repository classes (from native
exceptions such as JPA PersistenceExceptions to Spring's DataAccessException hierarchy).
-->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<!--
Will automatically be transactional due to @Transactional.
EntityManager will be auto-injected due to @PersistenceContext.
PersistenceExceptions will be auto-translated due to @Repository.
-->
<bean id="clinic" class="org.springframework.samples.petclinic.jpa.EntityManagerClinic"/>
</beans>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1 @@
# This file is intentionally empty. Message look-ups will fall back to the default "messages.properties" file.

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-1.0"
configId="org/springframework/samples/petclinic">
<context-root>/petclinic</context-root>
<context-priority-classloader>true</context-priority-classloader>
</web-app>

View file

@ -0,0 +1,19 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<%
Exception ex = (Exception) request.getAttribute("exception");
%>
<h2>Data access failure: <%= ex.getMessage() %></h2>
<p/>
<%
ex.printStackTrace(new java.io.PrintWriter(out));
%>
<p/>
<br/>
<a href="<spring:url value="/" htmlEscape="true" />">Home</a>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,12 @@
<table class="footer">
<tr>
<td><a href="<spring:url value="/" htmlEscape="true" />">Home</a></td>
<td align="right"><img src="<spring:url value="/static/images/springsource-logo.png" htmlEscape="true" />" alt="Sponsored by SpringSource"/></td>
</tr>
</table>
</div>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!--
PetClinic :: a Spring Framework demonstration
-->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="<spring:url value="/static/styles/petclinic.css" htmlEscape="true" />" type="text/css"/>
<title>PetClinic :: a Spring Framework demonstration</title>
</head>
<body>
<div id="main">

View file

@ -0,0 +1,5 @@
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

View file

@ -0,0 +1,61 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<c:choose>
<c:when test="${owner.new}"><c:set var="method" value="post"/></c:when>
<c:otherwise><c:set var="method" value="put"/></c:otherwise>
</c:choose>
<h2><c:if test="${owner.new}">New </c:if>Owner:</h2>
<form:form modelAttribute="owner" method="${method}">
<table>
<tr>
<th>
First Name: <form:errors path="firstName" cssClass="errors"/>
<br/>
<form:input path="firstName" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<th>
Last Name: <form:errors path="lastName" cssClass="errors"/>
<br/>
<form:input path="lastName" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<th>
Address: <form:errors path="address" cssClass="errors"/>
<br/>
<form:input path="address" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<th>
City: <form:errors path="city" cssClass="errors"/>
<br/>
<form:input path="city" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<th>
Telephone: <form:errors path="telephone" cssClass="errors"/>
<br/>
<form:input path="telephone" size="20" maxlength="20"/>
</th>
</tr>
<tr>
<td>
<c:choose>
<c:when test="${owner.new}">
<p class="submit"><input type="submit" value="Add Owner"/></p>
</c:when>
<c:otherwise>
<p class="submit"><input type="submit" value="Update Owner"/></p>
</c:otherwise>
</c:choose>
</td>
</tr>
</table>
</form:form>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,34 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2>Owners:</h2>
<table>
<thead>
<th>Name</th>
<th>Address</th>
<th>City</th>
<th>Telephone</th>
<th>Pets</th>
</thead>
<c:forEach var="owner" items="${selections}">
<tr>
<td>
<spring:url value="owners/{ownerId}" var="ownerUrl">
<spring:param name="ownerId" value="${owner.id}"/>
</spring:url>
<a href="${fn:escapeXml(ownerUrl)}">${owner.firstName} ${owner.lastName}</a>
</td>
<td>${owner.address}</td>
<td>${owner.city}</td>
<td>${owner.telephone}</td>
<td>
<c:forEach var="pet" items="${owner.pets}">
${pet.name} &nbsp;
</c:forEach>
</td>
</tr>
</c:forEach>
</table>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,26 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2>Find Owners:</h2>
<spring:url value="/owners" var="formUrl"/>
<form:form modelAttribute="owner" action="${fn:escapeXml(formUrl)}" method="get">
<table>
<tr>
<th>
Last Name: <form:errors path="*" cssClass="errors"/>
<br/>
<form:input path="lastName" size="30" maxlength="80" />
</th>
</tr>
<tr>
<td><p class="submit"><input type="submit" value="Find Owners"/></p></td>
</tr>
</table>
</form:form>
<br/>
<a href='<spring:url value="/owners/new" htmlEscape="true"/>'>Add Owner</a>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,108 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<h2>Owner Information</h2>
<table>
<tr>
<th>Name</th>
<td><b>${owner.firstName} ${owner.lastName}</b></td>
</tr>
<tr>
<th>Address</th>
<td>${owner.address}</td>
</tr>
<tr>
<th>City</th>
<td>${owner.city}</td>
</tr>
<tr>
<th>Telephone </th>
<td>${owner.telephone}</td>
</tr>
</table>
<table class="table-buttons">
<tr>
<td colspan="2" align="center">
<spring:url value="{ownerId}/edit" var="editUrl">
<spring:param name="ownerId" value="${owner.id}" />
</spring:url>
<a href="${fn:escapeXml(editUrl)}">Edit Owner</a>
</td>
<td>
<spring:url value="{ownerId}/pets/new" var="addUrl">
<spring:param name="ownerId" value="${owner.id}" />
</spring:url>
<a href="${fn:escapeXml(addUrl)}">Add New Pet</a>
</td>
</tr>
</table>
<h2>Pets and Visits</h2>
<c:forEach var="pet" items="${owner.pets}">
<table width="94%">
<tr>
<td valign="top">
<table>
<tr>
<th>Name</th>
<td><b>${pet.name}</b></td>
</tr>
<tr>
<th>Birth Date</th>
<td><fmt:formatDate value="${pet.birthDate}" pattern="yyyy-MM-dd"/></td>
</tr>
<tr>
<th>Type</th>
<td>${pet.type.name}</td>
</tr>
</table>
</td>
<td valign="top">
<table>
<thead>
<th>Visit Date</th>
<th>Description</th>
</thead>
<c:forEach var="visit" items="${pet.visits}">
<tr>
<td><fmt:formatDate value="${visit.date}" pattern="yyyy-MM-dd"/></td>
<td>${visit.description}</td>
</tr>
</c:forEach>
</table>
</td>
</tr>
</table>
<table class="table-buttons">
<tr>
<td>
<spring:url value="{ownerId}/pets/{petId}/edit" var="petUrl">
<spring:param name="ownerId" value="${owner.id}"/>
<spring:param name="petId" value="${pet.id}"/>
</spring:url>
<a href="${fn:escapeXml(petUrl)}">Edit Pet</a>
</td>
<td></td>
<td>
<spring:url value="{ownerId}/pets/{petId}/visits/new" var="visitUrl">
<spring:param name="ownerId" value="${owner.id}"/>
<spring:param name="petId" value="${pet.id}"/>
</spring:url>
<a href="${fn:escapeXml(visitUrl)}">Add Visit</a>
</td>
<td></td>
<td>
<spring:url value="{ownerId}/pets/{petId}/visits.atom" var="feedUrl">
<spring:param name="ownerId" value="${owner.id}"/>
<spring:param name="petId" value="${pet.id}"/>
</spring:url>
<a href="${fn:escapeXml(feedUrl)}" rel="alternate" type="application/atom+xml">Atom Feed</a>
</td>
</tr>
</table>
</c:forEach>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,56 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<c:choose>
<c:when test="${pet.new}"><c:set var="method" value="post"/></c:when>
<c:otherwise><c:set var="method" value="put"/></c:otherwise>
</c:choose>
<h2><c:if test="${pet.new}">New </c:if>Pet</h2>
<b>Owner:</b> ${pet.owner.firstName} ${pet.owner.lastName}
<br/>
<form:form modelAttribute="pet" method="${method}">
<table>
<tr>
<th>
Name: <form:errors path="name" cssClass="errors"/>
<br/>
<form:input path="name" size="30" maxlength="30"/>
</th>
</tr>
<tr>
<th>
Birth Date: <form:errors path="birthDate" cssClass="errors"/>
<br/>
<form:input path="birthDate" size="10" maxlength="10"/> (yyyy-mm-dd)
</th>
</tr>
<tr>
<th>
Type: <form:errors path="type" cssClass="errors"/>
<br/>
<form:select path="type" items="${types}"/>
</th>
</tr>
<tr>
<td>
<c:choose>
<c:when test="${pet.new}">
<p class="submit"><input type="submit" value="Add Pet"/></p>
</c:when>
<c:otherwise>
<p class="submit"><input type="submit" value="Update Pet"/></p>
</c:otherwise>
</c:choose>
</td>
</tr>
</table>
</form:form>
<c:if test="${!pet.new}">
<form:form method="delete">
<p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>
</c:if>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,68 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2><c:if test="${visit.new}">New </c:if>Visit:</h2>
<form:form modelAttribute="visit">
<b>Pet:</b>
<table width="333">
<thead>
<th>Name</th>
<th>Birth Date</th>
<th>Type</th>
<th>Owner</th>
</thead>
<tr>
<td>${visit.pet.name}</td>
<td><fmt:formatDate value="${visit.pet.birthDate}" pattern="yyyy-MM-dd"/></td>
<td>${visit.pet.type.name}</td>
<td>${visit.pet.owner.firstName} ${visit.pet.owner.lastName}</td>
</tr>
</table>
<table width="333">
<tr>
<th>
Date:
<br/><form:errors path="date" cssClass="errors"/>
</th>
<td>
<form:input path="date" size="10" maxlength="10"/> (yyyy-mm-dd)
</td>
<tr/>
<tr>
<th valign="top">
Description:
<br/><form:errors path="description" cssClass="errors"/>
</th>
<td>
<form:textarea path="description" rows="10" cols="25"/>
</td>
</tr>
<tr>
<td colspan="2">
<input type="hidden" name="petId" value="${visit.pet.id}"/>
<p class="submit"><input type="submit" value="Add Visit"/></p>
</td>
</tr>
</table>
</form:form>
<br/>
<b>Previous Visits:</b>
<table width="333">
<tr>
<th>Date</th>
<th>Description</th>
</tr>
<c:forEach var="visit" items="${visit.pet.visits}">
<c:if test="${!visit.new}">
<tr>
<td><fmt:formatDate value="${visit.date}" pattern="yyyy-MM-dd"/></td>
<td>${visit.description}</td>
</tr>
</c:if>
</c:forEach>
</table>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,49 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2/>Internal error</h2>
<p/>
<%
try {
// The Servlet spec guarantees this attribute will be available
Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");
if (exception != null) {
if (exception instanceof ServletException) {
// It's a ServletException: we should extract the root cause
ServletException sex = (ServletException) exception;
Throwable rootCause = sex.getRootCause();
if (rootCause == null)
rootCause = sex;
out.println("** Root cause is: "+ rootCause.getMessage());
rootCause.printStackTrace(new java.io.PrintWriter(out));
}
else {
// It's not a ServletException, so we'll just show it
exception.printStackTrace(new java.io.PrintWriter(out));
}
}
else {
out.println("No error information available");
}
// Display cookies
out.println("\nCookies:\n");
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
out.println(cookies[i].getName() + "=[" + cookies[i].getValue() + "]");
}
}
} catch (Exception ex) {
ex.printStackTrace(new java.io.PrintWriter(out));
}
%>
<p/>
<br/>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,31 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2>Veterinarians:</h2>
<table>
<thead>
<th>Name</th>
<th>Specialties</th>
</thead>
<c:forEach var="vet" items="${vets.vetList}">
<tr>
<td>${vet.firstName} ${vet.lastName}</td>
<td>
<c:forEach var="specialty" items="${vet.specialties}">
${specialty.name}
</c:forEach>
<c:if test="${vet.nrOfSpecialties == 0}">none</c:if>
</td>
</tr>
</c:forEach>
</table>
<table class="table-buttons">
<tr>
<td>
<a href="<spring:url value="/vets.xml" htmlEscape="true" />">View as XML</a>
</td>
</tr>
</table>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,15 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<img src="<spring:url value="/static/images/pets.png" htmlEscape="true" />" align="right" style="position:relative;right:30px;">
<h2><fmt:message key="welcome"/></h2>
<ul>
<li><a href="<spring:url value="/owners/search" htmlEscape="true" />">Find owner</a></li>
<li><a href="<spring:url value="/vets" htmlEscape="true" />">Display all veterinarians</a></li>
<li><a href="<spring:url value="/static/html/tutorial.html" htmlEscape="true" />">Tutorial</a></li>
</ul>
<p>&nbsp;</p>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View file

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- DispatcherServlet application context for PetClinic's web tier.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">
<!--
- The controllers are autodetected POJOs labeled with the @Controller annotation.
-->
<context:component-scan base-package="org.springframework.samples.petclinic.web"/>
<!--
- The form-based controllers within this application provide @RequestMapping
- annotations at the type level for path mapping URLs and @RequestMapping
- at the method level for request type mappings (e.g., GET and POST).
- In contrast, ClinicController - which is not form-based - provides
- @RequestMapping only at the method level for path mapping URLs.
-
- DefaultAnnotationHandlerMapping is driven by these annotations and is
- enabled by default with Java 5+.
-->
<!--
- This bean processes annotated handler methods, applying PetClinic-specific PropertyEditors
- for request parameter binding. It overrides the default AnnotationMethodHandlerAdapter.
-->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer"/>
</property>
</bean>
<!--
- This bean resolves specific types of exceptions to corresponding logical
- view names for error views. The default behaviour of DispatcherServlet
- is to propagate all exceptions to the servlet container: this will happen
- here with all other types of exceptions.
-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.web.servlet.PageNotFound">pageNotFound</prop>
<prop key="org.springframework.dao.DataAccessException">dataAccessFailure</prop>
<prop key="org.springframework.transaction.TransactionException">dataAccessFailure</prop>
</props>
</property>
</bean>
<!--
- This view resolver delegates to the InternalResourceViewResolver and BeanNameViewResolver,
- and uses the requested media type to pick a matching view. When the media type is 'text/html',
- it will delegate to the InternalResourceViewResolver's JstlView, otherwise to the
- BeanNameViewResolver. Note the use of the expression language to refer to the contentType
- property of the vets view bean, setting it to 'application/vnd.springsource.samples.petclinic+xml'.
-->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="xml" value="#{vets.contentType}"/>
<entry key="atom" value="#{visits.contentType}"/>
</map>
</property>
<property name="order" value="0"/>
</bean>
<!--
- The BeanNameViewResolver is used to pick up the visits view name (below).
- It has the order property set to 2, which means that this will
- be the first view resolver to be used after the delegating content
- negotiating view resolver.
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="1"/>
<!--
- This bean configures the 'prefix' and 'suffix' properties of
- InternalResourceViewResolver, which resolves logical view names
- returned by Controllers. For example, a logical view name of "vets"
- will be mapped to "/WEB-INF/jsp/vets.jsp".
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" p:order="2"/>
<!--
- The AtomView rendering a Atom feed of the visits
-->
<bean id="visits" class="org.springframework.samples.petclinic.web.VisitsAtomView"/>
<bean id="vets" class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="contentType" value="application/vnd.springsource.samples.petclinic+xml"/>
<property name="marshaller" ref="marshaller"/>
</bean>
<oxm:jaxb2-marshaller id="marshaller">
<oxm:class-to-be-bound name="org.springframework.samples.petclinic.Vets"/>
</oxm:jaxb2-marshaller>
<!--
- Message source for this context, loaded from localized "messages_xx" files.
- Could also reside in the root application context, as it is generic,
- but is currently just used within PetClinic's web tier.
-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename="messages"/>
</beans>

View file

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring PetClinic</display-name>
<description>Spring PetClinic sample application</description>
<!--
Key of the system property that should specify the root directory of this
web app. Applied by WebAppRootListener or Log4jConfigListener.
-->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>petclinic.root</param-value>
</context-param>
<!--
Location of the Log4J config file, for initialization and refresh checks.
Applied by Log4jConfigListener.
-->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<!--
- Location of the XML file that defines the root application context.
- Applied by ContextLoaderServlet.
-
- Can be set to:
- "/WEB-INF/applicationContext-hibernate.xml" for the Hibernate implementation,
- "/WEB-INF/applicationContext-jpa.xml" for the JPA one, or
- "/WEB-INF/applicationContext-jdbc.xml" for the JDBC one.
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-jdbc.xml</param-value>
<!--
<param-value>/WEB-INF/applicationContext-hibernate.xml</param-value>
<param-value>/WEB-INF/applicationContext-jpa.xml</param-value>
-->
<!--
To use the JPA variant above, you will need to enable Spring load-time
weaving in your server environment. See PetClinic's readme and/or
Spring's JPA documentation for information on how to do this.
-->
</context-param>
<!--
- Configures Log4J for this web app.
- As this context specifies a context-param "log4jConfigLocation", its file path
- is used to load the Log4J configuration, including periodic refresh checks.
-
- Would fall back to default Log4J initialization (non-refreshing) if no special
- context-params are given.
-
- Exports a "web app root key", i.e. a system property that specifies the root
- directory of this web app, for usage in log file paths.
- This web app specifies "petclinic.root" (see log4j.properties file).
-->
<!-- Leave the listener commented-out if using JBoss -->
<!--
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
-->
<!--
- Loads the root application context of this web app at startup,
- by default from "/WEB-INF/applicationContext.xml".
- Note that you need to fall back to Spring's ContextLoaderServlet for
- J2EE servers that do not follow the Servlet 2.4 initialization order.
-
- Use WebApplicationContextUtils.getWebApplicationContext(servletContext)
- to access it anywhere in the web application, outside of the framework.
-
- The root context is the parent of all servlet-specific contexts.
- This means that its beans are automatically available in these child contexts,
- both for getBean(name) calls and (external) bean references.
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
- Map static resources to the default servlet
- examples:
- http://localhost:8080/static/images/pets.png
- http://localhost:8080/static/styles/petclinic.css
-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
<!--
- Servlet that dispatches request to registered handlers (Controller implementations).
- Has its own application context, by default defined in "{servlet-name}-servlet.xml",
- i.e. "petclinic-servlet.xml".
-
- A web app can contain any number of such servlets.
- Note that this web app has a shared root application context, serving as parent
- of all DispatcherServlet contexts.
-->
<servlet>
<servlet-name>petclinic</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<!--
- Maps the petclinic dispatcher to "*.do". All handler mappings in
- petclinic-servlet.xml will by default be applied to this subpath.
- If a mapping isn't a /* subpath, the handler mappings are considered
- relative to the web app root.
-
- NOTE: A single dispatcher can be mapped to multiple paths, like any servlet.
-->
<servlet-mapping>
<servlet-name>petclinic</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<servlet-name>petclinic</servlet-name>
</filter-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<!-- Displays a stack trace -->
<location>/WEB-INF/jsp/uncaughtException.jsp</location>
</error-page>
<!--
- Reference to PetClinic database.
- Only needed if not using a local DataSource but a JNDI one instead.
-->
<!--
<resource-ref>
<res-ref-name>jdbc/petclinic</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
-->
</web-app>

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,234 @@
/* main elements */
body,div,td {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
color: #666;
}
body {
background-color: #fff;
background-image: url(../images/banner-graphic.png);
background-position: top center;
background-repeat: no-repeat;
text-align: center;
min-width: 600px;
margin-top: 60px;
margin-left: auto;
margin-right: auto;
}
div {
margin: 5px 25px 5px 25px;
text-align: left;
}
/* header and footer elements */
#main {
margin:0 auto;
position:relative;
top: 35px;
left:0px;
width:560px;
text-align:left;
}
.footer {
background:#fff;
border:none;
margin-top:20px;
border-top:1px solid #999999;
width:100%;
}
.footer td {color:#999999;}
.footer a:link {color: #7db223;}
/* text styles */
h1,h2,h3 {
font-family: Helvetica, sans-serif;
color: #7db223;
}
h1 {
font-size: 20px;
line-height: 26px;
}
h2 {
font-size: 18px;
line-height: 24px;
}
h3 {
font-size: 15px;
line-height: 21px;
color:#555;
}
h4 {
font-size: 14px;
line-height: 20px;
}
.errors {
color: red;
font-weight: bold;
}
a {
text-decoration: underline;
font-size: 13px;
}
a:link {
color: #7db223;
}
a:hover {
color: #456314;
}
a:active {
color: #7db223;
}
a:visited {
color: #7db223;
}
ul {
list-style: disc url(../images/bullet-arrow.png);
}
li {
padding-top: 5px;
text-align: left;
}
li ul {
list-style: square url(../images/bullet-arrow.png);
}
li ul li ul {
list-style: circle none;
}
/* table elements */
table {
background: #d6e2c3;
margin: 3px 0 0 0;
border: 4px solid #d6e2c3;
border-collapse: collapse;
}
table table {
margin: -5px 0;
border: 0px solid #e0e7d3;
width: 100%;
}
table td,table th {
padding: 8px;
}
table th {
font-size: 12px;
text-align: left;
font-weight: bold;
}
table thead {
font-weight: bold;
font-style: italic;
background-color: #c2ceaf;
}
table a:link {color: #303030;}
caption {
caption-side: top;
width: auto;
text-align: left;
font-size: 12px;
color: #848f73;
padding-bottom: 4px;
}
fieldset {
background: #e0e7d3;
padding: 8px;
padding-bottom: 22px;
border: none;
width: 560px;
}
fieldset label {
width: 70px;
float: left;
margin-top: 1.7em;
margin-left: 20px;
}
fieldset textfield {
margin: 3px;
height: 20px;
background: #e0e7d3;
}
fieldset textarea {
margin: 3px;
height: 165px;
background: #e0e7d3;
}
fieldset input {
margin: 3px;
height: 20px;
background: #e0e7d3;
}
fieldset table {
width: 100%;
}
fieldset th {
padding-left: 25px;
}
.table-buttons {
background-color:#fff;
border:none;
}
.table-buttons td {
border:none;
}
.submit input {
background:url(../images/submit-bg.png) repeat-x;
border: 2px outset #d7b9c9;
color:#383838;
padding:2px 10px;
font-size:11px;
text-transform:uppercase;
font-weight:bold;
}
.updated {
background:#ecf1e5;
font-size:11px;
margin-left:2px;
border:4px solid #ecf1e5;
}
.updated td {
padding:2px 8px;
font-size:11px;
color:#888888;
}

View file

@ -0,0 +1,224 @@
package org.springframework.samples.petclinic;
import java.util.Collection;
import java.util.Date;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.util.EntityUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
/**
* <p>
* Base class for {@link Clinic} integration tests.
* </p>
* <p>
* &quot;AbstractClinicTests-context.xml&quot; declares a common
* {@link javax.sql.DataSource DataSource}. Subclasses should specify
* additional context locations which declare a
* {@link org.springframework.transaction.PlatformTransactionManager PlatformTransactionManager}
* and a concrete implementation of {@link Clinic}.
* </p>
* <p>
* This class extends {@link AbstractTransactionalJUnit4SpringContextTests},
* one of the valuable testing support classes provided by the
* <em>Spring TestContext Framework</em> found in the
* <code>org.springframework.test.context</code> package. The
* annotation-driven configuration used here represents best practice for
* integration tests with Spring. Note, however, that
* AbstractTransactionalJUnit4SpringContextTests serves only as a convenience
* for extension. For example, if you do not wish for your test classes to be
* tied to a Spring-specific class hierarchy, you may configure your tests with
* annotations such as {@link ContextConfiguration @ContextConfiguration},
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners},
* {@link org.springframework.transaction.annotation.Transactional @Transactional},
* etc.
* </p>
* <p>
* AbstractClinicTests and its subclasses benefit from the following services
* provided by the Spring TestContext Framework:
* </p>
* <ul>
* <li><strong>Spring IoC container caching</strong> which spares us
* unnecessary set up time between test execution.</li>
* <li><strong>Dependency Injection</strong> of test fixture instances,
* meaning that we don't need to perform application context lookups. See the
* use of {@link Autowired @Autowired} on the <code>clinic</code> instance
* variable, which uses autowiring <em>by type</em>. As an alternative, we
* could annotate <code>clinic</code> with
* {@link javax.annotation.Resource @Resource} to achieve dependency injection
* <em>by name</em>.
* <em>(see: {@link ContextConfiguration @ContextConfiguration},
* {@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener DependencyInjectionTestExecutionListener})</em></li>
* <li><strong>Transaction management</strong>, meaning each test method is
* executed in its own transaction, which is automatically rolled back by
* default. Thus, even if tests insert or otherwise change database state, there
* is no need for a teardown or cleanup script.
* <em>(see: {@link org.springframework.test.context.transaction.TransactionConfiguration @TransactionConfiguration},
* {@link org.springframework.transaction.annotation.Transactional @Transactional},
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener TransactionalTestExecutionListener})</em></li>
* <li><strong>Useful inherited protected fields</strong>, such as a
* {@link org.springframework.jdbc.core.simple.SimpleJdbcTemplate SimpleJdbcTemplate}
* that can be used to verify database state after test operations or to verify
* the results of queries performed by application code. An
* {@link org.springframework.context.ApplicationContext ApplicationContext} is
* also inherited and can be used for explicit bean lookup if necessary.
* <em>(see: {@link org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests AbstractJUnit4SpringContextTests},
* {@link AbstractTransactionalJUnit4SpringContextTests})</em></li>
* </ul>
* <p>
* The Spring TestContext Framework and related unit and integration testing
* support classes are shipped in <code>spring-test.jar</code>.
* </p>
*
* @author Ken Krebs
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
*/
@ContextConfiguration
public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
protected Clinic clinic;
@Test
public void getVets() {
Collection<Vet> vets = this.clinic.getVets();
// Use the inherited countRowsInTable() convenience method (from
// AbstractTransactionalJUnit4SpringContextTests) to verify the results.
assertEquals("JDBC query must show the same number of vets", super.countRowsInTable("VETS"), vets.size());
Vet v1 = EntityUtils.getById(vets, Vet.class, 2);
assertEquals("Leary", v1.getLastName());
assertEquals(1, v1.getNrOfSpecialties());
assertEquals("radiology", (v1.getSpecialties().get(0)).getName());
Vet v2 = EntityUtils.getById(vets, Vet.class, 3);
assertEquals("Douglas", v2.getLastName());
assertEquals(2, v2.getNrOfSpecialties());
assertEquals("dentistry", (v2.getSpecialties().get(0)).getName());
assertEquals("surgery", (v2.getSpecialties().get(1)).getName());
}
@Test
public void getPetTypes() {
Collection<PetType> petTypes = this.clinic.getPetTypes();
assertEquals("JDBC query must show the same number of pet types", super.countRowsInTable("TYPES"),
petTypes.size());
PetType t1 = EntityUtils.getById(petTypes, PetType.class, 1);
assertEquals("cat", t1.getName());
PetType t4 = EntityUtils.getById(petTypes, PetType.class, 4);
assertEquals("snake", t4.getName());
}
@Test
public void findOwners() {
Collection<Owner> owners = this.clinic.findOwners("Davis");
assertEquals(2, owners.size());
owners = this.clinic.findOwners("Daviss");
assertEquals(0, owners.size());
}
@Test
public void loadOwner() {
Owner o1 = this.clinic.loadOwner(1);
assertTrue(o1.getLastName().startsWith("Franklin"));
Owner o10 = this.clinic.loadOwner(10);
assertEquals("Carlos", o10.getFirstName());
// XXX: Add programmatic support for ending transactions with the
// TestContext Framework.
// Check lazy loading, by ending the transaction:
// endTransaction();
// Now Owners are "disconnected" from the data store.
// We might need to touch this collection if we switched to lazy loading
// in mapping files, but this test would pick this up.
o1.getPets();
}
@Test
public void insertOwner() {
Collection<Owner> owners = this.clinic.findOwners("Schultz");
int found = owners.size();
Owner owner = new Owner();
owner.setLastName("Schultz");
this.clinic.storeOwner(owner);
// assertTrue(!owner.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
owners = this.clinic.findOwners("Schultz");
assertEquals("Verifying number of owners after inserting a new one.", found + 1, owners.size());
}
@Test
public void updateOwner() throws Exception {
Owner o1 = this.clinic.loadOwner(1);
String old = o1.getLastName();
o1.setLastName(old + "X");
this.clinic.storeOwner(o1);
o1 = this.clinic.loadOwner(1);
assertEquals(old + "X", o1.getLastName());
}
@Test
public void loadPet() {
Collection<PetType> types = this.clinic.getPetTypes();
Pet p7 = this.clinic.loadPet(7);
assertTrue(p7.getName().startsWith("Samantha"));
assertEquals(EntityUtils.getById(types, PetType.class, 1).getId(), p7.getType().getId());
assertEquals("Jean", p7.getOwner().getFirstName());
Pet p6 = this.clinic.loadPet(6);
assertEquals("George", p6.getName());
assertEquals(EntityUtils.getById(types, PetType.class, 4).getId(), p6.getType().getId());
assertEquals("Peter", p6.getOwner().getFirstName());
}
@Test
public void insertPet() {
Owner o6 = this.clinic.loadOwner(6);
int found = o6.getPets().size();
Pet pet = new Pet();
pet.setName("bowser");
Collection<PetType> types = this.clinic.getPetTypes();
pet.setType(EntityUtils.getById(types, PetType.class, 2));
pet.setBirthDate(new Date());
o6.addPet(pet);
assertEquals(found + 1, o6.getPets().size());
// both storePet and storeOwner are necessary to cover all ORM tools
this.clinic.storePet(pet);
this.clinic.storeOwner(o6);
// assertTrue(!pet.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
o6 = this.clinic.loadOwner(6);
assertEquals(found + 1, o6.getPets().size());
}
@Test
public void updatePet() throws Exception {
Pet p7 = this.clinic.loadPet(7);
String old = p7.getName();
p7.setName(old + "X");
this.clinic.storePet(p7);
p7 = this.clinic.loadPet(7);
assertEquals(old + "X", p7.getName());
}
@Test
public void insertVisit() {
Pet p7 = this.clinic.loadPet(7);
int found = p7.getVisits().size();
Visit visit = new Visit();
p7.addVisit(visit);
visit.setDescription("test");
// both storeVisit and storePet are necessary to cover all ORM tools
this.clinic.storeVisit(visit);
this.clinic.storePet(p7);
// assertTrue(!visit.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
p7 = this.clinic.loadPet(7);
assertEquals(found + 1, p7.getVisits().size());
}
}

View file

@ -0,0 +1,27 @@
package org.springframework.samples.petclinic;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
/**
* JUnit test for the {@link Owner} class.
*
* @author Ken Krebs
*/
public class OwnerTests {
@Test
public void testHasPet() {
Owner owner = new Owner();
Pet fido = new Pet();
fido.setName("Fido");
assertNull(owner.getPet("Fido"));
assertNull(owner.getPet("fido"));
owner.addPet(fido);
assertEquals(fido, owner.getPet("Fido"));
assertEquals(fido, owner.getPet("fido"));
}
}

Some files were not shown because too many files have changed in this diff Show more