1
0
Fork 0

Import spring petclinic backend

This commit is contained in:
Chris Dombroski 2024-11-21 20:22:38 -05:00
commit c6920bb4f7
139 changed files with 11950 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake path:flake;

9
.gitattributes vendored Normal file
View file

@ -0,0 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf

190
.gitignore vendored Normal file
View file

@ -0,0 +1,190 @@
.direnv/
.idea/
.vscode/
# Created by https://www.toptal.com/developers/gitignore/api/intellij,linux,node,gradle
# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,linux,node,gradle
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
### Gradle ###
.gradle
**/build/
!src/**/build/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties
# Cache of project
.gradletasknamecache
# Eclipse Gradle plugin generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
### Gradle Patch ###
# Java heap dump
*.hprof
# End of https://www.toptal.com/developers/gitignore/api/intellij,linux,node,gradle

12
backend/.editorconfig Normal file
View file

@ -0,0 +1,12 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
[*.{java,xml}]
indent_size = 4
trim_trailing_whitespace = true

13
backend/.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
target/*
.settings/*
.classpath
.project
.idea
*.iml
/target
generated/
# Easier branch switching
springboot-petclinic-client/
springboot-petclinic-server/

201
backend/LICENSE.txt Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

120
backend/build.gradle.kts Normal file
View file

@ -0,0 +1,120 @@
plugins {
java
id("org.springframework.boot") version "3.3.6"
id("io.spring.dependency-management") version "1.1.6"
id("org.openapi.generator") version "7.10.0"
idea
jacoco
}
group = "org.springframework.samples"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.data:spring-data-jdbc-core:1.2.1.RELEASE")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0")
implementation("org.mapstruct:mapstruct:${project.property("mapstructVersion")}")
// implementation("org.openapitools:jackson-databind-nullable:0.2.6")
annotationProcessor("org.mapstruct:mapstruct-processor:${project.property("mapstructVersion")}")
runtimeOnly("org.hsqldb:hsqldb")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("com.jayway.jsonpath:json-path")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
openApiGenerate {
inputSpec.set("${projectDir}/src/main/resources/openapi.yml")
generatorName.set("spring")
library.set("spring-boot")
modelNameSuffix.set("Dto")
apiPackage.set("org.springframework.samples.petclinic.rest.api")
modelPackage.set("org.springframework.samples.petclinic.rest.dto")
supportingFilesConstrainedTo.set(listOf("ApiUtil.java"))
globalProperties.set(
mapOf(
"apis" to "",
"models" to ""
)
)
configOptions.set(
mutableMapOf(
"verbose" to "true",
"interfaceOnly" to "true",
"performBeanValidation" to "true",
"dateLibrary" to "java8",
"useSpringBoot3" to "true",
"openApiNullable" to "false",
"serializationLibrary" to "jackson",
"documentationProvider" to "springdoc",
)
)
}
tasks.jacocoTestReport {
dependsOn(tasks.test) // tests are required to run before generating the report
classDirectories.setFrom(
files(classDirectories.files.map {
fileTree(it) {
exclude(
"**/org/springframework/samples/petclinic/rest/dto/**",
"**/org/springframework/samples/petclinic/rest/api/**"
)
}
})
)
}
tasks.jacocoTestCoverageVerification {
violationRules {
rule {
element = "BUNDLE"
limit {
counter = "LINE"
value = "COVEREDRATIO"
minimum = "0.85".toBigDecimal()
}
limit {
counter = "BRANCH"
value = "COVEREDRATIO"
minimum = "0.66".toBigDecimal()
}
}
}
}
tasks.withType<Test> {
useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
configure<JacocoTaskExtension> {
}
}
java.sourceSets["main"].java {
srcDir(layout.buildDirectory.dir("generate-resources/main/src/main/java"))
}
tasks.withType<JavaCompile> {
dependsOn(tasks.openApiGenerate)
options.compilerArgs = listOf(
"-Amapstruct.suppressGeneratorTimestamp=true",
"-Amapstruct.suppressGeneratorVersionInfoComment=true",
"-Amapstruct.defaultComponentModel=spring"
)
}

View file

@ -0,0 +1,26 @@
services:
mysql:
image: mysql:8.4
command: --mysql-native-password=ON
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=
- MYSQL_ALLOW_EMPTY_PASSWORD=true
- MYSQL_USER=petclinic
- MYSQL_PASSWORD=petclinic
- MYSQL_DATABASE=petclinic
volumes:
- "./conf.d:/etc/mysql/conf.d:ro"
profiles:
- mysql
postgres:
image: postgres:16.3
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=petclinic
- POSTGRES_USER=petclinic
- POSTGRES_DB=petclinic
profiles:
- postgres

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

223
backend/readme.md Normal file
View file

@ -0,0 +1,223 @@
# REST version of Spring PetClinic Sample Application (spring-framework-petclinic extend )
[![Java Build Status](https://github.com/spring-petclinic/spring-petclinic-rest/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-petclinic/spring-petclinic-rest/actions/workflows/maven-build.yml)
[![Docker Build Status](https://github.com/spring-petclinic/spring-petclinic-rest/actions/workflows/docker-build.yml/badge.svg)](https://github.com/spring-petclinic/spring-petclinic-rest/actions/workflows/docker-build.yml)
This backend version of the Spring Petclinic application only provides a REST API. **There is no UI**.
The [spring-petclinic-angular project](https://github.com/spring-petclinic/spring-petclinic-angular) is a Angular front-end application which consumes the REST API.
## Understanding the Spring Petclinic application with a few diagrams
[See the presentation of the Spring Petclinic Framework version](http://fr.slideshare.net/AntoineRey/spring-framework-petclinic-sample-application)
### Petclinic ER Model
![alt petclinic-ermodel](petclinic-ermodel.png)
## Running Petclinic locally
### With Maven command line
```sh
git clone https://github.com/spring-petclinic/spring-petclinic-rest.git
cd spring-petclinic-rest
./mvnw spring-boot:run
```
### With Docker
```sh
docker run -p 9966:9966 springcommunity/spring-petclinic-rest
```
You can then access petclinic here: [http://localhost:9966/petclinic/](http://localhost:9966/petclinic/)
There is an actuator health check route as well:
* [http://localhost:9966/petclinic/actuator/health](http://localhost:9966/petclinic/actuator/health)
## OpenAPI REST API documentation
You can reach the Swagger UI with this URL (after application start):
[http://localhost:9966/petclinic/](http://localhost:9966/petclinic/swagger-ui.html).
You then can get the Open API description reaching this URL: [localhost:9966/petclinic/v3/api-docs](localhost:9966/petclinic/v3/api-docs).
## Screenshot of the Angular client
See its repository here: https://github.com/spring-petclinic/spring-petclinic-angular
<img width="1427" alt="spring-petclinic-angular2" src="https://cloud.githubusercontent.com/assets/838318/23263243/f4509c4a-f9dd-11e6-951b-69d0ef72d8bd.png">
## In case you find a bug/suggested improvement for Spring Petclinic
Our issue tracker is available here: https://github.com/spring-petclinic/spring-petclinic-rest/issues
## Database configuration
In its default configuration, Petclinic uses an in-memory database (HSQLDB) which gets populated at startup with data.
A similar setup is provided for MySQL and PostgreSQL if a persistent database configuration is needed.
Note that whenever the database type changes, the app needs to run with a different profile: `spring.profiles.active=mysql` for MySQL or `spring.profiles.active=postgres` for PostgreSQL.
See the [Spring Boot documentation](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html#howto.properties-and-configuration.set-active-spring-profiles) for more detail on how to set the active profile.
You can also change profile defined in the `application.properties` file.
For MySQL database, it is needed to change param `hsqldb` to `mysql` in the following line of `application.properies` file:
```properties
spring.profiles.active=hsqldb,spring-data-jpa
```
You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker:
```bash
docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:8.4
```
or
```bash
docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:16.3
```
Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt)
and [PostgreSQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/postgres/petclinic_db_setup_postgres.txt).
Instead of vanilla `docker` you can also use the provided `docker-compose.yml` file to start the database containers. Each one has a profile just like the Spring profile:
```bash
docker-compose --profile mysql up
```
or
```bash
docker-compose --profile postgres up
```
## API First Approach
This API is built following some [API First approach principles](https://swagger.io/resources/articles/adopting-an-api-first-approach/).
It is specified through the [OpenAPI](https://oai.github.io/Documentation/).
It is specified in this [file](./src/main/resources/openapi.yml).
Some of the required classes are generated during the build time.
Here are the generated file types:
* DTOs
* API template interfaces specifying methods to override in the controllers
To see how to get them generated you can read the next chapter.
## Generated code
Some of the required classes are generated during the build time using maven or any IDE (e.g., IntelliJ Idea or Eclipse).
All of these classes are generated into the ``target/generated-sources`` folder.
Here is a list of the generated packages and the corresponding tooling:
| Package name | Tool |
|------------------------------------------------|------------------|
| org.springframework.samples.petclinic.mapper | [MapStruct](https://mapstruct.org/) |
| org.springframework.samples.petclinic.rest.dto | [OpenAPI Generator maven plugin](https://github.com/OpenAPITools/openapi-generator/) |
To get both, you have to run the following command:
```jshelllanguage
mvn clean install
```
## Security configuration
In its default configuration, Petclinic doesn't have authentication and authorization enabled.
### Basic Authentication
In order to use the basic authentication functionality, turn in on from the `application.properties` file
```properties
petclinic.security.enable=true
```
This will secure all APIs and in order to access them, basic authentication is required.
Apart from authentication, APIs also require authorization. This is done via roles that a user can have.
The existing roles are listed below with the corresponding permissions
* `OWNER_ADMIN` -> `OwnerController`, `PetController`, `PetTypeController` (`getAllPetTypes` and `getPetType`), `VisitController`
* `VET_ADMIN` -> `PetTypeController`, `SpecialityController`, `VetController`
* `ADMIN` -> `UserController`
There is an existing user with the username `admin` and password `admin` that has access to all APIs.
In order to add a new user, please make `POST /api/users` request with the following payload:
```json
{
"username": "secondAdmin",
"password": "password",
"enabled": true,
"roles": [
{ "name" : "OWNER_ADMIN" }
]
}
```
## Working with Petclinic in Eclipse/STS
### prerequisites
The following items should be installed in your system:
* Maven 3 (https://maven.apache.org/install.html)
* git command line tool (https://help.github.com/articles/set-up-git)
* Eclipse with the m2e plugin (m2e is installed by default when using the STS (http://www.springsource.org/sts) distribution of Eclipse)
Note: when m2e is available, there is an m2 icon in Help -> About dialog.
If m2e is not there, just follow the install process here: http://eclipse.org/m2e/download/
* Eclipse with the [mapstruct plugin](https://mapstruct.org/documentation/ide-support/) installed.
### Steps:
1) In the command line
```sh
git clone https://github.com/spring-petclinic/spring-petclinic-rest.git
```
2) Inside Eclipse
```
File -> Import -> Maven -> Existing Maven project
```
## Looking for something in particular?
| Layer | Source |
|--|--|
| REST API controllers | [REST folder](src/main/java/org/springframework/samples/petclinic/rest) |
| Service | [ClinicServiceImpl.java](src/main/java/org/springframework/samples/petclinic/service/ClinicServiceImpl.java) |
| JDBC | [jdbc folder](src/main/java/org/springframework/samples/petclinic/repository/jdbc) |
| JPA | [jpa folder](src/main/java/org/springframework/samples/petclinic/repository/jpa) |
| Spring Data JPA | [springdatajpa folder](src/main/java/org/springframework/samples/petclinic/repository/springdatajpa) |
| Tests | [AbstractClinicServiceTests.java](src/test/java/org/springframework/samples/petclinic/service/clinicService/AbstractClinicServiceTests.java) |
## Publishing a Docker image
This application uses [Google Jib]([https://github.com/GoogleContainerTools/jib) to build an optimized Docker image into the [Docker Hub](https://cloud.docker.com/u/springcommunity/repository/docker/springcommunity/spring-petclinic-rest/) repository.
The [pom.xml](pom.xml) has been configured to publish the image with a the `springcommunity/spring-petclinic-rest`image name.
Command line to run:
```sh
mvn compile jib:build -X -DjibSerialize=true -Djib.to.auth.username=xxx -Djib.to.auth.password=xxxxx
```
## Interesting Spring Petclinic forks
The Spring Petclinic master branch in the main [spring-projects](https://github.com/spring-projects/spring-petclinic)
GitHub org is the "canonical" implementation, currently based on Spring Boot and Thymeleaf.
This [spring-petclinic-rest](https://github.com/spring-petclinic/spring-petclinic-rest/) project is one of the [several forks](https://spring-petclinic.github.io/docs/forks.html)
hosted in a special GitHub org: [spring-petclinic](https://github.com/spring-petclinic).
If you have a special interest in a different technology stack
that could be used to implement the Pet Clinic then please join the community there.
# Contributing
The [issue tracker](https://github.com/spring-petclinic/spring-petclinic-rest/issues) is the preferred channel for bug reports, features requests and submitting pull requests.
For pull requests, editor preferences are available in the [editor config](https://github.com/spring-petclinic/spring-petclinic-rest/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <http://editorconfig.org>.

View file

@ -0,0 +1,14 @@
package org.springframework.samples.petclinic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class PetClinicApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(PetClinicApplication.class, args);
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2016 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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
/**
* Java config for Springfox swagger documentation plugin
*
* @author Vitaliy Fedoriv
*/
@Configuration
public class SwaggerConfig {
@Bean
OpenAPI customOpenAPI() {
return new OpenAPI().components(new Components()).info(new Info()
.title("REST Petclinic backend Api Documentation").version("1.0")
.termsOfService("Petclinic backend terms of service")
.description(
"This is REST API documentation of the Spring Petclinic backend. If authentication is enabled, when calling the APIs use admin/admin")
.license(swaggerLicense()).contact(swaggerContact()));
}
private Contact swaggerContact() {
Contact petclinicContact = new Contact();
petclinicContact.setName("Vitaliy Fedoriv");
petclinicContact.setEmail("vitaliy.fedoriv@gmail.com");
petclinicContact.setUrl("https://github.com/spring-petclinic/spring-petclinic-rest");
return petclinicContact;
}
private License swaggerLicense() {
License petClinicLicense = new License();
petClinicLicense.setName("Apache 2.0");
petClinicLicense.setUrl("http://www.apache.org/licenses/LICENSE-2.0");
petClinicLicense.setExtensions(Collections.emptyMap());
return petClinicLicense;
}
}

View file

@ -0,0 +1,29 @@
package org.springframework.samples.petclinic.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.rest.dto.OwnerDto;
import org.springframework.samples.petclinic.rest.dto.OwnerFieldsDto;
import java.util.Collection;
import java.util.List;
/**
* Maps Owner & OwnerDto using Mapstruct
*/
@Mapper(uses = PetMapper.class)
public interface OwnerMapper {
OwnerDto toOwnerDto(Owner owner);
Owner toOwner(OwnerDto ownerDto);
@Mapping(target = "id", ignore = true)
@Mapping(target = "pets", ignore = true)
Owner toOwner(OwnerFieldsDto ownerDto);
List<OwnerDto> toOwnerDtoCollection(Collection<Owner> ownerCollection);
Collection<Owner> toOwners(Collection<OwnerDto> ownerDtos);
}

View file

@ -0,0 +1,39 @@
package org.springframework.samples.petclinic.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.rest.dto.PetDto;
import org.springframework.samples.petclinic.rest.dto.PetFieldsDto;
import org.springframework.samples.petclinic.rest.dto.PetTypeDto;
import java.util.Collection;
/**
* Map Pet & PetDto using mapstruct
*/
@Mapper(uses = VisitMapper.class)
public interface PetMapper {
@Mapping(source = "owner.id", target = "ownerId")
PetDto toPetDto(Pet pet);
Collection<PetDto> toPetsDto(Collection<Pet> pets);
Collection<Pet> toPets(Collection<PetDto> pets);
@Mapping(source = "ownerId", target = "owner.id")
Pet toPet(PetDto petDto);
@Mapping(target = "id", ignore = true)
@Mapping(target = "owner", ignore = true)
@Mapping(target = "visits", ignore = true)
Pet toPet(PetFieldsDto petFieldsDto);
PetTypeDto toPetTypeDto(PetType petType);
PetType toPetType(PetTypeDto petTypeDto);
Collection<PetTypeDto> toPetTypeDtos(Collection<PetType> petTypes);
}

View file

@ -0,0 +1,27 @@
package org.springframework.samples.petclinic.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.rest.dto.PetTypeDto;
import org.springframework.samples.petclinic.rest.dto.PetTypeFieldsDto;
import java.util.Collection;
import java.util.List;
/**
* Map PetType & PetTypeDto using mapstruct
*/
@Mapper
public interface PetTypeMapper {
PetType toPetType(PetTypeDto petTypeDto);
@Mapping(target = "id", ignore = true)
PetType toPetType(PetTypeFieldsDto petTypeFieldsDto);
PetTypeDto toPetTypeDto(PetType petType);
PetTypeFieldsDto toPetTypeFieldsDto(PetType petType);
List<PetTypeDto> toPetTypeDtos(Collection<PetType> petTypes);
}

View file

@ -0,0 +1,22 @@
package org.springframework.samples.petclinic.mapper;
import org.mapstruct.Mapper;
import org.springframework.samples.petclinic.rest.dto.SpecialtyDto;
import org.springframework.samples.petclinic.model.Specialty;
import java.util.Collection;
/**
* Map Specialty & SpecialtyDto using mapstruct
*/
@Mapper
public interface SpecialtyMapper {
Specialty toSpecialty(SpecialtyDto specialtyDto);
SpecialtyDto toSpecialtyDto(Specialty specialty);
Collection<SpecialtyDto> toSpecialtyDtos(Collection<Specialty> specialties);
Collection<Specialty> toSpecialtys(Collection<SpecialtyDto> specialties);
}

View file

@ -0,0 +1,32 @@
package org.springframework.samples.petclinic.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.samples.petclinic.model.Role;
import org.springframework.samples.petclinic.model.User;
import org.springframework.samples.petclinic.rest.dto.RoleDto;
import org.springframework.samples.petclinic.rest.dto.UserDto;
import java.util.Collection;
/**
* Map User/Role & UserDto/RoleDto using mapstruct
*/
@Mapper
public interface UserMapper {
@Mapping(target = "id", ignore = true)
@Mapping(target = "user", ignore = true)
Role toRole(RoleDto roleDto);
RoleDto toRoleDto(Role role);
Collection<RoleDto> toRoleDtos(Collection<Role> roles);
User toUser(UserDto userDto);
UserDto toUserDto(User user);
Collection<Role> toRoles(Collection<RoleDto> roleDtos);
}

View file

@ -0,0 +1,24 @@
package org.springframework.samples.petclinic.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.rest.dto.VetDto;
import org.springframework.samples.petclinic.rest.dto.VetFieldsDto;
import java.util.Collection;
/**
* Map Vet & VetoDto using mapstruct
*/
@Mapper(uses = SpecialtyMapper.class)
public interface VetMapper {
Vet toVet(VetDto vetDto);
@Mapping(target = "id", ignore = true)
Vet toVet(VetFieldsDto vetFieldsDto);
VetDto toVetDto(Vet vet);
Collection<VetDto> toVetDtos(Collection<Vet> vets);
}

View file

@ -0,0 +1,28 @@
package org.springframework.samples.petclinic.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.rest.dto.VisitDto;
import org.springframework.samples.petclinic.rest.dto.VisitFieldsDto;
import java.util.Collection;
/**
* Map Visit & VisitDto using mapstruct
*/
@Mapper(uses = PetMapper.class)
public interface VisitMapper {
@Mapping(source = "petId", target = "pet.id")
Visit toVisit(VisitDto visitDto);
@Mapping(target = "id", ignore = true)
@Mapping(target = "pet", ignore = true)
Visit toVisit(VisitFieldsDto visitFieldsDto);
@Mapping(source = "pet.id", target = "petId")
VisitDto toVisitDto(Visit visit);
Collection<VisitDto> toVisitsDto(Collection<Visit> visits);
}

View file

@ -0,0 +1,49 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* Simple JavaBean domain object with an id property. Used as a base class for objects needing this property.
*
* @author Ken Krebs
* @author Juergen Hoeller
*/
@MappedSuperclass
public class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@JsonIgnore
public boolean isNew() {
return this.id == null;
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotEmpty;
/**
* 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
*/
@MappedSuperclass
public class NamedEntity extends BaseEntity {
@Column(name = "name")
@NotEmpty
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return this.getName();
}
}

View file

@ -0,0 +1,153 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.core.style.ToStringCreator;
import jakarta.persistence.*;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotEmpty;
import java.util.*;
/**
* Simple JavaBean domain object representing an owner.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
* @author Michael Isvy
*/
@Entity
@Table(name = "owners")
public class Owner extends Person {
@Column(name = "address")
@NotEmpty
private String address;
@Column(name = "city")
@NotEmpty
private String city;
@Column(name = "telephone")
@NotEmpty
@Digits(fraction = 0, integer = 10)
private String telephone;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "owner", fetch = FetchType.EAGER)
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 Set<Pet> getPetsInternal() {
if (this.pets == null) {
this.pets = new HashSet<>();
}
return this.pets;
}
protected void setPetsInternal(Set<Pet> pets) {
this.pets = pets;
}
public List<Pet> getPets() {
List<Pet> sortedPets = new ArrayList<>(getPetsInternal());
PropertyComparator.sort(sortedPets, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedPets);
}
public void setPets(List<Pet> pets) {
this.pets = new HashSet<>(pets);
}
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;
}
public Pet getPet(Integer petId) {
return getPetsInternal().stream().filter(p -> p.getId().equals(petId)).findFirst().orElse(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,56 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotEmpty;
/**
* Simple JavaBean domain object representing an person.
*
* @author Ken Krebs
*/
@MappedSuperclass
public class Person extends BaseEntity {
@Column(name = "first_name")
@NotEmpty
protected String firstName;
@Column(name = "last_name")
@NotEmpty
protected 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,102 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.persistence.*;
import java.time.LocalDate;
import java.util.*;
/**
* Simple business object representing a pet.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
*/
@Entity
@Table(name = "pets")
public class Pet extends NamedEntity {
@Column(name = "birth_date", columnDefinition = "DATE")
private LocalDate birthDate;
@ManyToOne
@JoinColumn(name = "type_id")
private PetType type;
@ManyToOne
@JoinColumn(name = "owner_id")
private Owner owner;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "pet", fetch = FetchType.EAGER)
private Set<Visit> visits;
public LocalDate getBirthDate() {
return this.birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
public PetType getType() {
return this.type;
}
public void setType(PetType type) {
this.type = type;
}
public Owner getOwner() {
return this.owner;
}
public void setOwner(Owner owner) {
this.owner = owner;
}
protected Set<Visit> getVisitsInternal() {
if (this.visits == null) {
this.visits = new HashSet<>();
}
return this.visits;
}
protected void setVisitsInternal(Set<Visit> visits) {
this.visits = visits;
}
public List<Visit> getVisits() {
List<Visit> sortedVisits = new ArrayList<>(getVisitsInternal());
PropertyComparator.sort(sortedVisits, new MutableSortDefinition("date", false, false));
return Collections.unmodifiableList(sortedVisits);
}
public void setVisits(List<Visit> visits) {
this.visits = new HashSet<>(visits);
}
public void addVisit(Visit visit) {
getVisitsInternal().add(visit);
visit.setPet(this);
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
/**
* @author Juergen Hoeller
* Can be Cat, Dog, Hamster...
*/
@Entity
@Table(name = "types")
public class PetType extends NamedEntity {
}

View file

@ -0,0 +1,39 @@
package org.springframework.samples.petclinic.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Table(name = "roles" ,uniqueConstraints = @UniqueConstraint(columnNames = {"username", "role"}))
public class Role extends BaseEntity {
@ManyToOne
@JoinColumn(name = "username")
@JsonIgnore
private User user;
@Column( name = "role")
private String name;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
/**
* Models a {@link Vet Vet's} specialty (for example, dentistry).
*
* @author Juergen Hoeller
*/
@Entity
@Table(name = "specialties")
public class Specialty extends NamedEntity {
}

View file

@ -0,0 +1,74 @@
package org.springframework.samples.petclinic.model;
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Table(name = "users")
public class User {
@Id
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@Column(name = "enabled")
private Boolean enabled;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.EAGER)
private Set<Role> roles;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
@JsonIgnore
public void addRole(String roleName) {
if(this.roles == null) {
this.roles = new HashSet<>();
}
Role role = new Role();
role.setName(roleName);
this.roles.add(role);
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright 2002-2018 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.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import jakarta.persistence.*;
import jakarta.xml.bind.annotation.XmlElement;
import java.util.*;
/**
* Simple JavaBean domain object representing a veterinarian.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
* @author Arjen Poutsma
*/
@Entity
@Table(name = "vets")
public class Vet extends Person {
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"),
inverseJoinColumns = @JoinColumn(name = "specialty_id"))
private Set<Specialty> specialties;
@JsonIgnore
protected Set<Specialty> getSpecialtiesInternal() {
if (this.specialties == null) {
this.specialties = new HashSet<>();
}
return this.specialties;
}
protected void setSpecialtiesInternal(Set<Specialty> specialties) {
this.specialties = specialties;
}
@XmlElement
public List<Specialty> getSpecialties() {
List<Specialty> sortedSpecs = new ArrayList<>(getSpecialtiesInternal());
PropertyComparator.sort(sortedSpecs, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedSpecs);
}
public void setSpecialties(List<Specialty> specialties) {
this.specialties = new HashSet<>(specialties);
}
@JsonIgnore
public int getNrOfSpecialties() {
return getSpecialtiesInternal().size();
}
public void addSpecialty(Specialty specialty) {
getSpecialtiesInternal().add(specialty);
}
public void clearSpecialties() {
getSpecialtiesInternal().clear();
}
}

View file

@ -0,0 +1,116 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import java.time.LocalDate;
/**
* Simple JavaBean domain object representing a visit.
*
* @author Ken Krebs
*/
@Entity
@Table(name = "visits")
public class Visit extends BaseEntity {
/**
* Holds value of property date.
*/
@Column(name = "visit_date", columnDefinition = "DATE")
private LocalDate date;
/**
* Holds value of property description.
*/
@NotEmpty
@Column(name = "description")
private String description;
/**
* Holds value of property pet.
*/
@ManyToOne
@JoinColumn(name = "pet_id")
private Pet pet;
/**
* Creates a new instance of Visit for the current date
*/
public Visit() {
this.date = LocalDate.now();
}
/**
* Getter for property date.
*
* @return Value of property date.
*/
public LocalDate getDate() {
return this.date;
}
/**
* Setter for property date.
*
* @param date New value of property date.
*/
public void setDate(LocalDate 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,5 @@
/**
* The classes in this package represent PetClinic's business layer.
*/
package org.springframework.samples.petclinic.model;

View file

@ -0,0 +1,81 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository;
import java.util.Collection;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.BaseEntity;
import org.springframework.samples.petclinic.model.Owner;
/**
* Repository class for <code>Owner</code> domain objects All method names are compliant with Spring Data naming
* conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
public interface OwnerRepository {
/**
* 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> findByLastName(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 findById(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 save(Owner owner) throws DataAccessException;
/**
* Retrieve <code>Owner</code>s from the data store, returning all owners
*
* @return a <code>Collection</code> of <code>Owner</code>s (or an empty <code>Collection</code> if none
* found)
*/
Collection<Owner> findAll() throws DataAccessException;
/**
* Delete an <code>Owner</code> to the data store by <code>Owner</code>.
*
* @param owner the <code>Owner</code> to delete
*
*/
void delete(Owner owner) throws DataAccessException;
}

View file

@ -0,0 +1,78 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository;
import java.util.Collection;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.BaseEntity;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
/**
* Repository class for <code>Pet</code> domain objects All method names are compliant with Spring Data naming
* conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
public interface PetRepository {
/**
* Retrieve all <code>PetType</code>s from the data store.
*
* @return a <code>Collection</code> of <code>PetType</code>s
*/
List<PetType> findPetTypes() 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 findById(int id) 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 save(Pet pet) throws DataAccessException;
/**
* Retrieve <code>Pet</code>s from the data store, returning all owners
*
* @return a <code>Collection</code> of <code>Pet</code>s (or an empty <code>Collection</code> if none
* found)
*/
Collection<Pet> findAll() throws DataAccessException;
/**
* Delete an <code>Pet</code> to the data store by <code>Pet</code>.
*
* @param pet the <code>Pet</code> to delete
*
*/
void delete(Pet pet) throws DataAccessException;
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository;
import java.util.Collection;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.PetType;
/**
* @author Vitaliy Fedoriv
*
*/
public interface PetTypeRepository {
PetType findById(int id) throws DataAccessException;
PetType findByName(String name) throws DataAccessException;
Collection<PetType> findAll() throws DataAccessException;
void save(PetType petType) throws DataAccessException;
void delete(PetType petType) throws DataAccessException;
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Specialty;
/**
* @author Vitaliy Fedoriv
*
*/
public interface SpecialtyRepository {
Specialty findById(int id) throws DataAccessException;
List<Specialty> findSpecialtiesByNameIn(Set<String> names);
Collection<Specialty> findAll() throws DataAccessException;
void save(Specialty specialty) throws DataAccessException;
void delete(Specialty specialty) throws DataAccessException;
}

View file

@ -0,0 +1,9 @@
package org.springframework.samples.petclinic.repository;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.User;
public interface UserRepository {
void save(User user) throws DataAccessException;
}

View file

@ -0,0 +1,49 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository;
import java.util.Collection;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Vet;
/**
* Repository class for <code>Vet</code> domain objects All method names are compliant with Spring Data naming
* conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
public interface VetRepository {
/**
* Retrieve all <code>Vet</code>s from the data store.
*
* @return a <code>Collection</code> of <code>Vet</code>s
*/
Collection<Vet> findAll() throws DataAccessException;
Vet findById(int id) throws DataAccessException;
void save(Vet vet) throws DataAccessException;
void delete(Vet vet) throws DataAccessException;
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository;
import java.util.Collection;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.BaseEntity;
import org.springframework.samples.petclinic.model.Visit;
/**
* Repository class for <code>Visit</code> domain objects All method names are compliant with Spring Data naming
* conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
public interface VisitRepository {
/**
* 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 save(Visit visit) throws DataAccessException;
List<Visit> findByPetId(Integer petId);
Visit findById(int id) throws DataAccessException;
Collection<Visit> findAll() throws DataAccessException;
void delete(Visit visit) throws DataAccessException;
}

View file

@ -0,0 +1,196 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.repository.OwnerRepository;
import org.springframework.samples.petclinic.util.EntityUtils;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import jakarta.transaction.Transactional;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A simple JDBC-based implementation of the {@link OwnerRepository} interface.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Rob Harrop
* @author Sam Brannen
* @author Thomas Risberg
* @author Mark Fisher
* @author Antoine Rey
* @author Vitaliy Fedoriv
*/
@Repository
@Profile("jdbc")
public class JdbcOwnerRepositoryImpl implements OwnerRepository {
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private SimpleJdbcInsert insertOwner;
@Autowired
public JdbcOwnerRepositoryImpl(DataSource dataSource) {
this.insertOwner = new SimpleJdbcInsert(dataSource)
.withTableName("owners")
.usingGeneratedKeyColumns("id");
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
/**
* 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.
*/
@Override
public Collection<Owner> findByLastName(String lastName) throws DataAccessException {
Map<String, Object> params = new HashMap<>();
params.put("lastName", lastName + "%");
List<Owner> owners = this.namedParameterJdbcTemplate.query(
"SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE last_name like :lastName",
params,
BeanPropertyRowMapper.newInstance(Owner.class)
);
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.
*/
@Override
public Owner findById(int id) throws DataAccessException {
Owner owner;
try {
Map<String, Object> params = new HashMap<>();
params.put("id", id);
owner = this.namedParameterJdbcTemplate.queryForObject(
"SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE id= :id",
params,
BeanPropertyRowMapper.newInstance(Owner.class)
);
} catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(Owner.class, id);
}
loadPetsAndVisits(owner);
return owner;
}
public void loadPetsAndVisits(final Owner owner) {
Map<String, Object> params = new HashMap<>();
params.put("id", owner.getId());
final List<JdbcPet> pets = this.namedParameterJdbcTemplate.query(
"SELECT pets.id as pets_id, name, birth_date, type_id, owner_id, visits.id as visit_id, visit_date, description, visits.pet_id as visits_pet_id FROM pets LEFT OUTER JOIN visits ON pets.id = visits.pet_id WHERE owner_id=:id ORDER BY pets.id",
params,
new JdbcPetVisitExtractor()
);
Collection<PetType> petTypes = getPetTypes();
for (JdbcPet pet : pets) {
pet.setType(EntityUtils.getById(petTypes, PetType.class, pet.getTypeId()));
owner.addPet(pet);
}
}
@Override
public void save(Owner owner) throws DataAccessException {
BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(owner);
if (owner.isNew()) {
Number newKey = this.insertOwner.executeAndReturnKey(parameterSource);
owner.setId(newKey.intValue());
} else {
this.namedParameterJdbcTemplate.update(
"UPDATE owners SET first_name=:firstName, last_name=:lastName, address=:address, " +
"city=:city, telephone=:telephone WHERE id=:id",
parameterSource);
}
}
public Collection<PetType> getPetTypes() throws DataAccessException {
return this.namedParameterJdbcTemplate.query(
"SELECT id, name FROM types ORDER BY name", new HashMap<String, Object>(),
BeanPropertyRowMapper.newInstance(PetType.class));
}
/**
* 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);
}
}
@Override
public Collection<Owner> findAll() throws DataAccessException {
List<Owner> owners = this.namedParameterJdbcTemplate.query(
"SELECT id, first_name, last_name, address, city, telephone FROM owners",
new HashMap<String, Object>(),
BeanPropertyRowMapper.newInstance(Owner.class));
for (Owner owner : owners) {
loadPetsAndVisits(owner);
}
return owners;
}
@Override
@Transactional
public void delete(Owner owner) throws DataAccessException {
Map<String, Object> owner_params = new HashMap<>();
owner_params.put("id", owner.getId());
List<Pet> pets = owner.getPets();
// cascade delete pets
for (Pet pet : pets){
Map<String, Object> pet_params = new HashMap<>();
pet_params.put("id", pet.getId());
// cascade delete visits
List<Visit> visits = pet.getVisits();
for (Visit visit : visits){
Map<String, Object> visit_params = new HashMap<>();
visit_params.put("id", visit.getId());
this.namedParameterJdbcTemplate.update("DELETE FROM visits WHERE id=:id", visit_params);
}
this.namedParameterJdbcTemplate.update("DELETE FROM pets WHERE id=:id", pet_params);
}
this.namedParameterJdbcTemplate.update("DELETE FROM owners WHERE id=:id", owner_params);
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import org.springframework.samples.petclinic.model.Pet;
/**
* Subclass of Pet that carries temporary id properties which are only relevant for a JDBC implementation of the
* PetRepository.
*
* @author Juergen Hoeller
*/
public class JdbcPet extends Pet {
private int typeId;
private int ownerId;
public int getTypeId() {
return this.typeId;
}
public void setTypeId(int typeId) {
this.typeId = typeId;
}
public int getOwnerId() {
return this.ownerId;
}
public void setOwnerId(int ownerId) {
this.ownerId = ownerId;
}
}

View file

@ -0,0 +1,169 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.repository.OwnerRepository;
import org.springframework.samples.petclinic.repository.PetRepository;
import org.springframework.samples.petclinic.repository.VisitRepository;
import org.springframework.samples.petclinic.util.EntityUtils;
import org.springframework.stereotype.Repository;
/**
* @author Ken Krebs
* @author Juergen Hoeller
* @author Rob Harrop
* @author Sam Brannen
* @author Thomas Risberg
* @author Mark Fisher
* @author Vitaliy Fedoriv
*/
@Repository
@Profile("jdbc")
public class JdbcPetRepositoryImpl implements PetRepository {
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private SimpleJdbcInsert insertPet;
private OwnerRepository ownerRepository;
private VisitRepository visitRepository;
@Autowired
public JdbcPetRepositoryImpl(DataSource dataSource,
OwnerRepository ownerRepository,
VisitRepository visitRepository) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
this.insertPet = new SimpleJdbcInsert(dataSource)
.withTableName("pets")
.usingGeneratedKeyColumns("id");
this.ownerRepository = ownerRepository;
this.visitRepository = visitRepository;
}
@Override
public List<PetType> findPetTypes() throws DataAccessException {
Map<String, Object> params = new HashMap<>();
return this.namedParameterJdbcTemplate.query(
"SELECT id, name FROM types ORDER BY name",
params,
BeanPropertyRowMapper.newInstance(PetType.class));
}
@Override
public Pet findById(int id) throws DataAccessException {
Integer ownerId;
try {
Map<String, Object> params = new HashMap<>();
params.put("id", id);
ownerId = this.namedParameterJdbcTemplate.queryForObject("SELECT owner_id FROM pets WHERE id=:id", params, Integer.class);
} catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(Pet.class, id);
}
Owner owner = this.ownerRepository.findById(ownerId);
return EntityUtils.getById(owner.getPets(), Pet.class, id);
}
@Override
public void save(Pet pet) throws DataAccessException {
if (pet.isNew()) {
Number newKey = this.insertPet.executeAndReturnKey(
createPetParameterSource(pet));
pet.setId(newKey.intValue());
} else {
this.namedParameterJdbcTemplate.update(
"UPDATE pets SET name=:name, birth_date=:birth_date, type_id=:type_id, " +
"owner_id=:owner_id WHERE id=:id",
createPetParameterSource(pet));
}
}
/**
* 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());
}
@Override
public Collection<Pet> findAll() throws DataAccessException {
Map<String, Object> params = new HashMap<>();
Collection<Pet> pets = new ArrayList<Pet>();
Collection<JdbcPet> jdbcPets = new ArrayList<JdbcPet>();
jdbcPets = this.namedParameterJdbcTemplate
.query("SELECT pets.id as pets_id, name, birth_date, type_id, owner_id FROM pets",
params,
new JdbcPetRowMapper());
Collection<PetType> petTypes = this.namedParameterJdbcTemplate.query("SELECT id, name FROM types ORDER BY name",
new HashMap<String,
Object>(), BeanPropertyRowMapper.newInstance(PetType.class));
Collection<Owner> owners = this.namedParameterJdbcTemplate.query(
"SELECT id, first_name, last_name, address, city, telephone FROM owners ORDER BY last_name",
new HashMap<String, Object>(),
BeanPropertyRowMapper.newInstance(Owner.class));
for (JdbcPet jdbcPet : jdbcPets) {
jdbcPet.setType(EntityUtils.getById(petTypes, PetType.class, jdbcPet.getTypeId()));
jdbcPet.setOwner(EntityUtils.getById(owners, Owner.class, jdbcPet.getOwnerId()));
// TODO add visits
pets.add(jdbcPet);
}
return pets;
}
@Override
public void delete(Pet pet) throws DataAccessException {
Map<String, Object> pet_params = new HashMap<>();
pet_params.put("id", pet.getId());
List<Visit> visits = pet.getVisits();
// cascade delete visits
for (Visit visit : visits) {
Map<String, Object> visit_params = new HashMap<>();
visit_params.put("id", visit.getId());
this.namedParameterJdbcTemplate.update("DELETE FROM visits WHERE id=:id", visit_params);
}
this.namedParameterJdbcTemplate.update("DELETE FROM pets WHERE id=:id", pet_params);
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Date;
/**
* {@link RowMapper} implementation mapping data from a {@link ResultSet} to the corresponding properties
* of the {@link JdbcPet} class.
*/
public class JdbcPetRowMapper implements RowMapper<JdbcPet> {
@Override
public JdbcPet mapRow(ResultSet rs, int rownum) throws SQLException {
JdbcPet pet = new JdbcPet();
pet.setId(rs.getInt("pets_id"));
pet.setName(rs.getString("name"));
pet.setBirthDate(rs.getObject("birth_date", LocalDate.class));
pet.setTypeId(rs.getInt("type_id"));
pet.setOwnerId(rs.getInt("owner_id"));
return pet;
}
}

View file

@ -0,0 +1,145 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.repository.PetTypeRepository;
import org.springframework.stereotype.Repository;
/**
* @author Vitaliy Fedoriv
*
*/
@Repository
@Profile("jdbc")
public class JdbcPetTypeRepositoryImpl implements PetTypeRepository {
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private SimpleJdbcInsert insertPetType;
@Autowired
public JdbcPetTypeRepositoryImpl(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
this.insertPetType = new SimpleJdbcInsert(dataSource)
.withTableName("types")
.usingGeneratedKeyColumns("id");
}
@Override
public PetType findById(int id) {
PetType petType;
try {
Map<String, Object> params = new HashMap<>();
params.put("id", id);
petType = this.namedParameterJdbcTemplate.queryForObject(
"SELECT id, name FROM types WHERE id= :id",
params,
BeanPropertyRowMapper.newInstance(PetType.class));
} catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(PetType.class, id);
}
return petType;
}
@Override
public PetType findByName(String name) throws DataAccessException {
PetType petType;
try {
Map<String, Object> params = new HashMap<>();
params.put("name", name);
petType = this.namedParameterJdbcTemplate.queryForObject(
"SELECT id, name FROM types WHERE name= :name",
params,
BeanPropertyRowMapper.newInstance(PetType.class));
} catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(PetType.class, name);
}
return petType;
}
@Override
public Collection<PetType> findAll() throws DataAccessException {
Map<String, Object> params = new HashMap<>();
return this.namedParameterJdbcTemplate.query(
"SELECT id, name FROM types",
params,
BeanPropertyRowMapper.newInstance(PetType.class));
}
@Override
public void save(PetType petType) throws DataAccessException {
BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(petType);
if (petType.isNew()) {
Number newKey = this.insertPetType.executeAndReturnKey(parameterSource);
petType.setId(newKey.intValue());
} else {
this.namedParameterJdbcTemplate.update("UPDATE types SET name=:name WHERE id=:id",
parameterSource);
}
}
@Override
public void delete(PetType petType) throws DataAccessException {
Map<String, Object> pettype_params = new HashMap<>();
pettype_params.put("id", petType.getId());
List<Pet> pets = new ArrayList<Pet>();
pets = this.namedParameterJdbcTemplate.
query("SELECT pets.id, name, birth_date, type_id, owner_id FROM pets WHERE type_id=:id",
pettype_params,
BeanPropertyRowMapper.newInstance(Pet.class));
// cascade delete pets
for (Pet pet : pets){
Map<String, Object> pet_params = new HashMap<>();
pet_params.put("id", pet.getId());
List<Visit> visits = new ArrayList<Visit>();
visits = this.namedParameterJdbcTemplate.query(
"SELECT id, pet_id, visit_date, description FROM visits WHERE pet_id = :id",
pet_params,
BeanPropertyRowMapper.newInstance(Visit.class));
// cascade delete visits
for (Visit visit : visits){
Map<String, Object> visit_params = new HashMap<>();
visit_params.put("id", visit.getId());
this.namedParameterJdbcTemplate.update("DELETE FROM visits WHERE id=:id", visit_params);
}
this.namedParameterJdbcTemplate.update("DELETE FROM pets WHERE id=:id", pet_params);
}
this.namedParameterJdbcTemplate.update("DELETE FROM types WHERE id=:id", pettype_params);
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import org.springframework.data.jdbc.core.OneToManyResultSetExtractor;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.samples.petclinic.model.Visit;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* {@link ResultSetExtractor} implementation by using the
* {@link OneToManyResultSetExtractor} of Spring Data Core JDBC Extensions.
*/
public class JdbcPetVisitExtractor extends
OneToManyResultSetExtractor<JdbcPet, Visit, Integer> {
public JdbcPetVisitExtractor() {
super(new JdbcPetRowMapper(), new JdbcVisitRowMapper());
}
@Override
protected Integer mapPrimaryKey(ResultSet rs) throws SQLException {
return rs.getInt("pets_id");
}
@Override
protected Integer mapForeignKey(ResultSet rs) throws SQLException {
if (rs.getObject("visits_pet_id") == null) {
return null;
} else {
return rs.getInt("visits_pet_id");
}
}
@Override
protected void addChild(JdbcPet root, Visit child) {
root.addVisit(child);
}
}

View file

@ -0,0 +1,121 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import java.util.*;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.repository.SpecialtyRepository;
import org.springframework.stereotype.Repository;
/**
* @author Vitaliy Fedoriv
*
*/
@Repository
@Profile("jdbc")
public class JdbcSpecialtyRepositoryImpl implements SpecialtyRepository {
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private SimpleJdbcInsert insertSpecialty;
@Autowired
public JdbcSpecialtyRepositoryImpl(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
this.insertSpecialty = new SimpleJdbcInsert(dataSource)
.withTableName("specialties")
.usingGeneratedKeyColumns("id");
}
@Override
public Specialty findById(int id) {
Specialty specialty;
try {
Map<String, Object> params = new HashMap<>();
params.put("id", id);
specialty = this.namedParameterJdbcTemplate.queryForObject(
"SELECT id, name FROM specialties WHERE id= :id",
params,
BeanPropertyRowMapper.newInstance(Specialty.class));
} catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(Specialty.class, id);
}
return specialty;
}
@Override
public List<Specialty> findSpecialtiesByNameIn(Set<String> names) {
List<Specialty> specialties;
try{
String sql = "SELECT id, name FROM specialties WHERE specialties.name IN (:names)";
Map<String, Object> params = new HashMap<>();
params.put("names", names);
specialties = this.namedParameterJdbcTemplate.query(
sql,
params,
new BeanPropertyRowMapper<>(Specialty.class));
} catch (EmptyResultDataAccessException ex){
throw new ObjectRetrievalFailureException(Specialty.class, names);
}
return specialties;
}
@Override
public Collection<Specialty> findAll() throws DataAccessException {
Map<String, Object> params = new HashMap<>();
return this.namedParameterJdbcTemplate.query(
"SELECT id, name FROM specialties",
params,
BeanPropertyRowMapper.newInstance(Specialty.class));
}
@Override
public void save(Specialty specialty) throws DataAccessException {
BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(specialty);
if (specialty.isNew()) {
Number newKey = this.insertSpecialty.executeAndReturnKey(parameterSource);
specialty.setId(newKey.intValue());
} else {
this.namedParameterJdbcTemplate.update("UPDATE specialties SET name=:name WHERE id=:id",
parameterSource);
}
}
@Override
public void delete(Specialty specialty) throws DataAccessException {
Map<String, Object> params = new HashMap<>();
params.put("id", specialty.getId());
this.namedParameterJdbcTemplate.update("DELETE FROM vet_specialties WHERE specialty_id=:id", params);
this.namedParameterJdbcTemplate.update("DELETE FROM specialties WHERE id=:id", params);
}
}

View file

@ -0,0 +1,68 @@
package org.springframework.samples.petclinic.repository.jdbc;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.samples.petclinic.model.Role;
import org.springframework.samples.petclinic.model.User;
import org.springframework.samples.petclinic.repository.UserRepository;
import org.springframework.stereotype.Repository;
@Repository
@Profile("jdbc")
public class JdbcUserRepositoryImpl implements UserRepository {
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private SimpleJdbcInsert insertUser;
@Autowired
public JdbcUserRepositoryImpl(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
this.insertUser = new SimpleJdbcInsert(dataSource).withTableName("users");
}
@Override
public void save(User user) throws DataAccessException {
BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user);
try {
getByUsername(user.getUsername());
this.namedParameterJdbcTemplate.update("UPDATE users SET password=:password, enabled=:enabled WHERE username=:username", parameterSource);
} catch (EmptyResultDataAccessException e) {
this.insertUser.execute(parameterSource);
} finally {
updateUserRoles(user);
}
}
private User getByUsername(String username) {
Map<String, Object> params = new HashMap<>();
params.put("username", username);
return this.namedParameterJdbcTemplate.queryForObject("SELECT * FROM users WHERE username=:username",
params, BeanPropertyRowMapper.newInstance(User.class));
}
private void updateUserRoles(User user) {
Map<String, Object> params = new HashMap<>();
params.put("username", user.getUsername());
this.namedParameterJdbcTemplate.update("DELETE FROM roles WHERE username=:username", params);
for (Role role : user.getRoles()) {
params.put("role", role.getName());
if (role.getName() != null) {
this.namedParameterJdbcTemplate.update("INSERT INTO roles(username, role) VALUES (:username, :role)", params);
}
}
}
}

View file

@ -0,0 +1,174 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.repository.VetRepository;
import org.springframework.samples.petclinic.util.EntityUtils;
import org.springframework.stereotype.Repository;
/**
* A simple JDBC-based implementation of the {@link VetRepository} interface.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Rob Harrop
* @author Sam Brannen
* @author Thomas Risberg
* @author Mark Fisher
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Repository
@Profile("jdbc")
public class JdbcVetRepositoryImpl implements VetRepository {
private JdbcTemplate jdbcTemplate;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private SimpleJdbcInsert insertVet;
@Autowired
public JdbcVetRepositoryImpl(DataSource dataSource, JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.insertVet = new SimpleJdbcInsert(dataSource).withTableName("vets").usingGeneratedKeyColumns("id");
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
/**
* Refresh the cache of Vets that the ClinicService is holding.
*/
@Override
public Collection<Vet> findAll() throws DataAccessException {
List<Vet> vets = new ArrayList<>();
// Retrieve the list of all vets.
vets.addAll(this.jdbcTemplate.query(
"SELECT id, first_name, last_name FROM vets ORDER BY last_name,first_name",
BeanPropertyRowMapper.newInstance(Vet.class)));
// Retrieve the list of all possible specialties.
final List<Specialty> specialties = this.jdbcTemplate.query(
"SELECT id, name FROM specialties",
BeanPropertyRowMapper.newInstance(Specialty.class));
// Build each vet's list of specialties.
for (Vet vet : vets) {
final List<Integer> vetSpecialtiesIds = this.jdbcTemplate.query(
"SELECT specialty_id FROM vet_specialties WHERE vet_id=?",
new BeanPropertyRowMapper<Integer>() {
@Override
public Integer mapRow(ResultSet rs, int row) throws SQLException {
return rs.getInt(1);
}
},
vet.getId());
for (int specialtyId : vetSpecialtiesIds) {
Specialty specialty = EntityUtils.getById(specialties, Specialty.class, specialtyId);
vet.addSpecialty(specialty);
}
}
return vets;
}
@Override
public Vet findById(int id) throws DataAccessException {
Vet vet;
try {
Map<String, Object> vet_params = new HashMap<>();
vet_params.put("id", id);
vet = this.namedParameterJdbcTemplate.queryForObject(
"SELECT id, first_name, last_name FROM vets WHERE id= :id",
vet_params,
BeanPropertyRowMapper.newInstance(Vet.class));
final List<Specialty> specialties = this.namedParameterJdbcTemplate.query(
"SELECT id, name FROM specialties", vet_params, BeanPropertyRowMapper.newInstance(Specialty.class));
final List<Integer> vetSpecialtiesIds = this.namedParameterJdbcTemplate.query(
"SELECT specialty_id FROM vet_specialties WHERE vet_id=:id",
vet_params,
new BeanPropertyRowMapper<Integer>() {
@Override
public Integer mapRow(ResultSet rs, int row) throws SQLException {
return rs.getInt(1);
}
});
for (int specialtyId : vetSpecialtiesIds) {
Specialty specialty = EntityUtils.getById(specialties, Specialty.class, specialtyId);
vet.addSpecialty(specialty);
}
} catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(Vet.class, id);
}
return vet;
}
@Override
public void save(Vet vet) throws DataAccessException {
BeanPropertySqlParameterSource parameterSource = new BeanPropertySqlParameterSource(vet);
if (vet.isNew()) {
Number newKey = this.insertVet.executeAndReturnKey(parameterSource);
vet.setId(newKey.intValue());
updateVetSpecialties(vet);
} else {
this.namedParameterJdbcTemplate
.update("UPDATE vets SET first_name=:firstName, last_name=:lastName WHERE id=:id", parameterSource);
updateVetSpecialties(vet);
}
}
@Override
public void delete(Vet vet) throws DataAccessException {
Map<String, Object> params = new HashMap<>();
params.put("id", vet.getId());
this.namedParameterJdbcTemplate.update("DELETE FROM vet_specialties WHERE vet_id=:id", params);
this.namedParameterJdbcTemplate.update("DELETE FROM vets WHERE id=:id", params);
}
private void updateVetSpecialties(Vet vet) throws DataAccessException {
Map<String, Object> params = new HashMap<>();
params.put("id", vet.getId());
this.namedParameterJdbcTemplate.update("DELETE FROM vet_specialties WHERE vet_id=:id", params);
for (Specialty spec : vet.getSpecialties()) {
params.put("spec_id", spec.getId());
if(!(spec.getId() == null)) {
this.namedParameterJdbcTemplate.update("INSERT INTO vet_specialties VALUES (:id, :spec_id)", params);
}
}
}
}

View file

@ -0,0 +1,177 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.repository.VisitRepository;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
/**
* A simple JDBC-based implementation of the {@link VisitRepository} interface.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Rob Harrop
* @author Sam Brannen
* @author Thomas Risberg
* @author Mark Fisher
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Repository
@Profile("jdbc")
public class JdbcVisitRepositoryImpl implements VisitRepository {
protected SimpleJdbcInsert insertVisit;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Autowired
public JdbcVisitRepositoryImpl(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
this.insertVisit = new SimpleJdbcInsert(dataSource)
.withTableName("visits")
.usingGeneratedKeyColumns("id");
}
/**
* Creates a {@link MapSqlParameterSource} based on data values from the supplied {@link Visit} instance.
*/
protected 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());
}
@Override
public List<Visit> findByPetId(Integer petId) {
Map<String, Object> params = new HashMap<>();
params.put("id", petId);
JdbcPet pet = this.namedParameterJdbcTemplate.queryForObject(
"SELECT id as pets_id, name, birth_date, type_id, owner_id FROM pets WHERE id=:id",
params,
new JdbcPetRowMapper());
List<Visit> visits = this.namedParameterJdbcTemplate.query(
"SELECT id as visit_id, visit_date, description FROM visits WHERE pet_id=:id",
params, new JdbcVisitRowMapper());
for (Visit visit : visits) {
visit.setPet(pet);
}
return visits;
}
@Override
public Visit findById(int id) throws DataAccessException {
Visit visit;
try {
Map<String, Object> params = new HashMap<>();
params.put("id", id);
visit = this.namedParameterJdbcTemplate.queryForObject(
"SELECT id as visit_id, visits.pet_id as pets_id, visit_date, description FROM visits WHERE id= :id",
params,
new JdbcVisitRowMapperExt());
} catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(Visit.class, id);
}
return visit;
}
@Override
public Collection<Visit> findAll() throws DataAccessException {
Map<String, Object> params = new HashMap<>();
return this.namedParameterJdbcTemplate.query(
"SELECT id as visit_id, pets.id as pets_id, visit_date, description FROM visits LEFT JOIN pets ON visits.pet_id = pets.id",
params, new JdbcVisitRowMapperExt());
}
@Override
public void save(Visit visit) throws DataAccessException {
if (visit.isNew()) {
Number newKey = this.insertVisit.executeAndReturnKey(createVisitParameterSource(visit));
visit.setId(newKey.intValue());
} else {
this.namedParameterJdbcTemplate.update(
"UPDATE visits SET visit_date=:visit_date, description=:description, pet_id=:pet_id WHERE id=:id ",
createVisitParameterSource(visit));
}
}
@Override
public void delete(Visit visit) throws DataAccessException {
Map<String, Object> params = new HashMap<>();
params.put("id", visit.getId());
this.namedParameterJdbcTemplate.update("DELETE FROM visits WHERE id=:id", params);
}
protected class JdbcVisitRowMapperExt implements RowMapper<Visit> {
@Override
public Visit mapRow(ResultSet rs, int rowNum) throws SQLException {
Visit visit = new Visit();
JdbcPet pet = new JdbcPet();
PetType petType = new PetType();
Owner owner = new Owner();
visit.setId(rs.getInt("visit_id"));
Date visitDate = rs.getDate("visit_date");
visit.setDate(new java.sql.Date(visitDate.getTime()).toLocalDate());
visit.setDescription(rs.getString("description"));
Map<String, Object> params = new HashMap<>();
params.put("id", rs.getInt("pets_id"));
pet = JdbcVisitRepositoryImpl.this.namedParameterJdbcTemplate.queryForObject(
"SELECT pets.id as pets_id, name, birth_date, type_id, owner_id FROM pets WHERE pets.id=:id",
params,
new JdbcPetRowMapper());
params.put("type_id", pet.getTypeId());
petType = JdbcVisitRepositoryImpl.this.namedParameterJdbcTemplate.queryForObject(
"SELECT id, name FROM types WHERE id= :type_id",
params,
BeanPropertyRowMapper.newInstance(PetType.class));
pet.setType(petType);
params.put("owner_id", pet.getOwnerId());
owner = JdbcVisitRepositoryImpl.this.namedParameterJdbcTemplate.queryForObject(
"SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE id= :owner_id",
params,
BeanPropertyRowMapper.newInstance(Owner.class));
pet.setOwner(owner);
visit.setPet(pet);
return visit;
}
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jdbc;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.samples.petclinic.model.Visit;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Date;
/**
* {@link RowMapper} implementation mapping data from a {@link ResultSet} to the corresponding properties
* of the {@link Visit} class.
*/
class JdbcVisitRowMapper implements RowMapper<Visit> {
@Override
public Visit mapRow(ResultSet rs, int row) throws SQLException {
Visit visit = new Visit();
visit.setId(rs.getInt("visit_id"));
visit.setDate(rs.getObject("visit_date", LocalDate.class));
visit.setDescription(rs.getString("description"));
return visit;
}
}

View file

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

View file

@ -0,0 +1,96 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jpa;
import java.util.Collection;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate5.support.OpenSessionInViewFilter;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.repository.OwnerRepository;
import org.springframework.stereotype.Repository;
/**
* JPA implementation of the {@link OwnerRepository} interface.
*
* @author Mike Keith
* @author Rod Johnson
* @author Sam Brannen
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Repository
@Profile("jpa")
public class JpaOwnerRepositoryImpl implements OwnerRepository {
@PersistenceContext
private EntityManager em;
/**
* Important: in the current version of this method, we load Owners with all their Pets and Visits while
* we do not need Visits at all and we only need one property from the Pet objects (the 'name' property).
* There are some ways to improve it such as:
* - creating a Ligtweight class (example here: https://community.jboss.org/wiki/LightweightClass)
* - Turning on lazy-loading and using {@link OpenSessionInViewFilter}
*/
@SuppressWarnings("unchecked")
public Collection<Owner> findByLastName(String lastName) {
// using 'join fetch' because a single query should load both owners and pets
// using 'left join fetch' because it might happen that an owner does not have pets yet
Query query = this.em.createQuery("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName");
query.setParameter("lastName", lastName + "%");
return query.getResultList();
}
@Override
public Owner findById(int id) {
// using 'join fetch' because a single query should load both owners and pets
// using 'left join fetch' because it might happen that an owner does not have pets yet
Query query = this.em.createQuery("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id");
query.setParameter("id", id);
return (Owner) query.getSingleResult();
}
@Override
public void save(Owner owner) {
if (owner.getId() == null) {
this.em.persist(owner);
} else {
this.em.merge(owner);
}
}
@SuppressWarnings("unchecked")
@Override
public Collection<Owner> findAll() throws DataAccessException {
Query query = this.em.createQuery("SELECT owner FROM Owner owner");
return query.getResultList();
}
@Override
public void delete(Owner owner) throws DataAccessException {
this.em.remove(this.em.contains(owner) ? owner : this.em.merge(owner));
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jpa;
import java.util.Collection;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.repository.PetRepository;
import org.springframework.stereotype.Repository;
/**
* JPA implementation of the {@link PetRepository} interface.
*
* @author Mike Keith
* @author Rod Johnson
* @author Sam Brannen
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Repository
@Profile("jpa")
public class JpaPetRepositoryImpl implements PetRepository {
@PersistenceContext
private EntityManager em;
@Override
@SuppressWarnings("unchecked")
public List<PetType> findPetTypes() {
return this.em.createQuery("SELECT ptype FROM PetType ptype ORDER BY ptype.name").getResultList();
}
@Override
public Pet findById(int id) {
return this.em.find(Pet.class, id);
}
@Override
public void save(Pet pet) {
if (pet.getId() == null) {
this.em.persist(pet);
} else {
this.em.merge(pet);
}
}
@SuppressWarnings("unchecked")
@Override
public Collection<Pet> findAll() throws DataAccessException {
return this.em.createQuery("SELECT pet FROM Pet pet").getResultList();
}
@Override
public void delete(Pet pet) throws DataAccessException {
//this.em.remove(this.em.contains(pet) ? pet : this.em.merge(pet));
String petId = pet.getId().toString();
this.em.createQuery("DELETE FROM Visit visit WHERE pet.id=" + petId).executeUpdate();
this.em.createQuery("DELETE FROM Pet pet WHERE id=" + petId).executeUpdate();
if (em.contains(pet)) {
em.remove(pet);
}
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jpa;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.repository.PetTypeRepository;
import org.springframework.stereotype.Repository;
/**
* @author Vitaliy Fedoriv
*
*/
@Repository
@Profile("jpa")
public class JpaPetTypeRepositoryImpl implements PetTypeRepository {
@PersistenceContext
private EntityManager em;
@Override
public PetType findById(int id) {
return this.em.find(PetType.class, id);
}
@Override
public PetType findByName(String name) throws DataAccessException {
return this.em.createQuery("SELECT p FROM PetType p WHERE p.name = :name", PetType.class)
.setParameter("name", name)
.getSingleResult();
}
@SuppressWarnings("unchecked")
@Override
public Collection<PetType> findAll() throws DataAccessException {
return this.em.createQuery("SELECT ptype FROM PetType ptype").getResultList();
}
@Override
public void save(PetType petType) throws DataAccessException {
if (petType.getId() == null) {
this.em.persist(petType);
} else {
this.em.merge(petType);
}
}
@SuppressWarnings("unchecked")
@Override
public void delete(PetType petType) throws DataAccessException {
this.em.remove(this.em.contains(petType) ? petType : this.em.merge(petType));
Integer petTypeId = petType.getId();
List<Pet> pets = this.em.createQuery("SELECT pet FROM Pet pet WHERE type.id=" + petTypeId).getResultList();
for (Pet pet : pets){
List<Visit> visits = pet.getVisits();
for (Visit visit : visits){
this.em.createQuery("DELETE FROM Visit visit WHERE id=" + visit.getId()).executeUpdate();
}
this.em.createQuery("DELETE FROM Pet pet WHERE id=" + pet.getId()).executeUpdate();
}
this.em.createQuery("DELETE FROM PetType pettype WHERE id=" + petTypeId).executeUpdate();
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jpa;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.repository.SpecialtyRepository;
import org.springframework.stereotype.Repository;
/**
* @author Vitaliy Fedoriv
*
*/
@Repository
@Profile("jpa")
public class JpaSpecialtyRepositoryImpl implements SpecialtyRepository {
@PersistenceContext
private EntityManager em;
@Override
public Specialty findById(int id) {
return this.em.find(Specialty.class, id);
}
@Override
public List<Specialty> findSpecialtiesByNameIn(Set<String> names) {
final String jpql = "SELECT s FROM Specialty s WHERE s.name IN :names";
return em.createQuery(jpql, Specialty.class)
.setParameter("names", names)
.getResultList();
}
@SuppressWarnings("unchecked")
@Override
public Collection<Specialty> findAll() throws DataAccessException {
return this.em.createQuery("SELECT s FROM Specialty s").getResultList();
}
@Override
public void save(Specialty specialty) throws DataAccessException {
if (specialty.getId() == null) {
this.em.persist(specialty);
} else {
this.em.merge(specialty);
}
}
@Override
public void delete(Specialty specialty) throws DataAccessException {
this.em.remove(this.em.contains(specialty) ? specialty : this.em.merge(specialty));
Integer specId = specialty.getId();
this.em.createNativeQuery("DELETE FROM vet_specialties WHERE specialty_id=" + specId).executeUpdate();
this.em.createQuery("DELETE FROM Specialty specialty WHERE id=" + specId).executeUpdate();
}
}

View file

@ -0,0 +1,27 @@
package org.springframework.samples.petclinic.repository.jpa;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.User;
import org.springframework.samples.petclinic.repository.UserRepository;
import org.springframework.stereotype.Repository;
@Repository
@Profile("jpa")
public class JpaUserRepositoryImpl implements UserRepository {
@PersistenceContext
private EntityManager em;
@Override
public void save(User user) throws DataAccessException {
if (this.em.find(User.class, user.getUsername()) == null) {
this.em.persist(user);
} else {
this.em.merge(user);
}
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jpa;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.repository.VetRepository;
import org.springframework.stereotype.Repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.Collection;
/**
* JPA implementation of the {@link VetRepository} interface.
*
* @author Mike Keith
* @author Rod Johnson
* @author Sam Brannen
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Repository
@Profile("jpa")
public class JpaVetRepositoryImpl implements VetRepository {
@PersistenceContext
private EntityManager em;
@Override
public Vet findById(int id) throws DataAccessException {
return this.em.find(Vet.class, id);
}
@SuppressWarnings("unchecked")
@Override
public Collection<Vet> findAll() throws DataAccessException {
return this.em.createQuery("SELECT vet FROM Vet vet").getResultList();
}
@Override
public void save(Vet vet) throws DataAccessException {
if (vet.getId() == null) {
this.em.persist(vet);
} else {
this.em.merge(vet);
}
}
@Override
public void delete(Vet vet) throws DataAccessException {
this.em.remove(this.em.contains(vet) ? vet : this.em.merge(vet));
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.jpa;
import java.util.Collection;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.repository.VisitRepository;
import org.springframework.stereotype.Repository;
/**
* JPA implementation of the ClinicService interface using EntityManager.
* <p/>
* <p>The mappings are defined in "orm.xml" located in the META-INF directory.
*
* @author Mike Keith
* @author Rod Johnson
* @author Sam Brannen
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Repository
@Profile("jpa")
public class JpaVisitRepositoryImpl implements VisitRepository {
@PersistenceContext
private EntityManager em;
@Override
public void save(Visit visit) {
if (visit.getId() == null) {
this.em.persist(visit);
} else {
this.em.merge(visit);
}
}
@Override
@SuppressWarnings("unchecked")
public List<Visit> findByPetId(Integer petId) {
Query query = this.em.createQuery("SELECT v FROM Visit v where v.pet.id= :id");
query.setParameter("id", petId);
return query.getResultList();
}
@Override
public Visit findById(int id) throws DataAccessException {
return this.em.find(Visit.class, id);
}
@SuppressWarnings("unchecked")
@Override
public Collection<Visit> findAll() throws DataAccessException {
return this.em.createQuery("SELECT v FROM Visit v").getResultList();
}
@Override
public void delete(Visit visit) throws DataAccessException {
this.em.remove(this.em.contains(visit) ? visit : this.em.merge(visit));
}
}

View file

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

View file

@ -0,0 +1,32 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.samples.petclinic.model.Pet;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public interface PetRepositoryOverride {
void delete(Pet pet);
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.samples.petclinic.model.PetType;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public interface PetTypeRepositoryOverride {
void delete(PetType petType);
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.samples.petclinic.model.Specialty;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public interface SpecialtyRepositoryOverride {
void delete(Specialty specialty);
}

View file

@ -0,0 +1,44 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import java.util.Collection;
import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.repository.OwnerRepository;
/**
* Spring Data JPA specialization of the {@link OwnerRepository} interface
*
* @author Michael Isvy
* @since 15.1.2013
*/
@Profile("spring-data-jpa")
public interface SpringDataOwnerRepository extends OwnerRepository, Repository<Owner, Integer> {
@Override
@Query("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName%")
Collection<Owner> findByLastName(@Param("lastName") String lastName);
@Override
@Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id")
Owner findById(@Param("id") int id);
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import java.util.List;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.repository.PetRepository;
/**
* Spring Data JPA specialization of the {@link PetRepository} interface
*
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Profile("spring-data-jpa")
public interface SpringDataPetRepository extends PetRepository, Repository<Pet, Integer>, PetRepositoryOverride {
@Override
@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
List<PetType> findPetTypes() throws DataAccessException;
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Profile;
import org.springframework.samples.petclinic.model.Pet;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public class SpringDataPetRepositoryImpl implements PetRepositoryOverride {
@PersistenceContext
private EntityManager em;
@Override
public void delete(Pet pet) {
String petId = pet.getId().toString();
this.em.createQuery("DELETE FROM Visit visit WHERE pet.id=" + petId).executeUpdate();
this.em.createQuery("DELETE FROM Pet pet WHERE id=" + petId).executeUpdate();
if (em.contains(pet)) {
em.remove(pet);
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.repository.PetTypeRepository;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public interface SpringDataPetTypeRepository extends PetTypeRepository, Repository<PetType, Integer>, PetTypeRepositoryOverride {
}

View file

@ -0,0 +1,56 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.Visit;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.List;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public class SpringDataPetTypeRepositoryImpl implements PetTypeRepositoryOverride {
@PersistenceContext
private EntityManager em;
@SuppressWarnings("unchecked")
@Override
public void delete(PetType petType) {
this.em.remove(this.em.contains(petType) ? petType : this.em.merge(petType));
Integer petTypeId = petType.getId();
List<Pet> pets = this.em.createQuery("SELECT pet FROM Pet pet WHERE type.id=" + petTypeId).getResultList();
for (Pet pet : pets){
List<Visit> visits = pet.getVisits();
for (Visit visit : visits){
this.em.createQuery("DELETE FROM Visit visit WHERE id=" + visit.getId()).executeUpdate();
}
this.em.createQuery("DELETE FROM Pet pet WHERE id=" + pet.getId()).executeUpdate();
}
this.em.createQuery("DELETE FROM PetType pettype WHERE id=" + petTypeId).executeUpdate();
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.repository.SpecialtyRepository;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public interface SpringDataSpecialtyRepository extends SpecialtyRepository, Repository<Specialty, Integer>, SpecialtyRepositoryOverride {
}

View file

@ -0,0 +1,44 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Profile;
import org.springframework.samples.petclinic.model.Specialty;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public class SpringDataSpecialtyRepositoryImpl implements SpecialtyRepositoryOverride {
@PersistenceContext
private EntityManager em;
@Override
public void delete(Specialty specialty) {
this.em.remove(this.em.contains(specialty) ? specialty : this.em.merge(specialty));
Integer specId = specialty.getId();
this.em.createNativeQuery("DELETE FROM vet_specialties WHERE specialty_id=" + specId).executeUpdate();
this.em.createQuery("DELETE FROM Specialty specialty WHERE id=" + specId).executeUpdate();
}
}

View file

@ -0,0 +1,11 @@
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.User;
import org.springframework.samples.petclinic.repository.UserRepository;
@Profile("spring-data-jpa")
public interface SpringDataUserRepository extends UserRepository, Repository<User, String> {
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.repository.VetRepository;
/**
* Spring Data JPA specialization of the {@link VetRepository} interface
*
* @author Michael Isvy
* @since 15.1.2013
*/
@Profile("spring-data-jpa")
public interface SpringDataVetRepository extends VetRepository, Repository<Vet, Integer> {
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.repository.VisitRepository;
/**
* Spring Data JPA specialization of the {@link VisitRepository} interface
*
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Profile("spring-data-jpa")
public interface SpringDataVisitRepository extends VisitRepository, Repository<Visit, Integer>, VisitRepositoryOverride {
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Profile;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Visit;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public class SpringDataVisitRepositoryImpl implements VisitRepositoryOverride {
@PersistenceContext
private EntityManager em;
@Override
public void delete(Visit visit) throws DataAccessException {
String visitId = visit.getId().toString();
this.em.createQuery("DELETE FROM Visit visit WHERE id=" + visitId).executeUpdate();
if (em.contains(visit)) {
em.remove(visit);
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.repository.springdatajpa;
import org.springframework.context.annotation.Profile;
import org.springframework.samples.petclinic.model.Visit;
/**
* @author Vitaliy Fedoriv
*
*/
@Profile("spring-data-jpa")
public interface VisitRepositoryOverride {
void delete(Visit visit);
}

View file

@ -0,0 +1,109 @@
/*
* Copyright 2016 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.rest.advice;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.rest.controller.BindingErrorsResponse;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
/**
* Global Exception handler for REST controllers.
* <p>
* This class handles exceptions thrown by REST controllers and returns
* appropriate HTTP responses to the client.
*
* @author Vitaliy Fedoriv
* @author Alexander Dudkin
*/
@ControllerAdvice
public class ExceptionControllerAdvice {
/**
* Record for storing error information.
* <p>
* This record encapsulates the class name and message of the exception.
*
* @param className The name of the exception class
* @param exMessage The message of the exception
*/
private record ErrorInfo(String className, String exMessage) {
public ErrorInfo(Exception ex) {
this(ex.getClass().getName(), ex.getLocalizedMessage());
}
}
/**
* Handles all general exceptions by returning a 500 Internal Server Error status with error details.
*
* @param e The exception to be handled
* @return A {@link ResponseEntity} containing the error information and a 500 Internal Server Error status
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorInfo> handleGeneralException(Exception e) {
ErrorInfo info = new ErrorInfo(e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(info);
}
/**
* Handles {@link DataIntegrityViolationException} which typically indicates database constraint violations.
* This method returns a 404 Not Found status if an entity does not exist.
*
* @param ex The {@link DataIntegrityViolationException} to be handled
* @return A {@link ResponseEntity} containing the error information and a 404 Not Found status
*/
@ExceptionHandler(DataIntegrityViolationException.class)
@ResponseStatus(code = HttpStatus.NOT_FOUND)
@ResponseBody
public ResponseEntity<ErrorInfo> handleDataIntegrityViolationException(DataIntegrityViolationException ex) {
ErrorInfo errorInfo = new ErrorInfo(ex);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorInfo);
}
/**
* Handles exception thrown by Bean Validation on controller methods parameters
*
* @param ex The thrown exception
*
* @return an empty response entity
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(BAD_REQUEST)
@ResponseBody
public ResponseEntity<ErrorInfo> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingErrorsResponse errors = new BindingErrorsResponse();
BindingResult bindingResult = ex.getBindingResult();
if (bindingResult.hasErrors()) {
errors.addAllErrors(bindingResult);
return ResponseEntity.badRequest().body(new ErrorInfo("MethodArgumentNotValidException", "Validation failed"));
}
return ResponseEntity.badRequest().build();
}
}

View file

@ -0,0 +1,137 @@
/*
* Copyright 2016 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.rest.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Vitaliy Fedoriv
*
*/
public class BindingErrorsResponse {
public BindingErrorsResponse() {
this(null);
}
public BindingErrorsResponse(Integer id) {
this(null, id);
}
public BindingErrorsResponse(Integer pathId, Integer bodyId) {
boolean onlyBodyIdSpecified = pathId == null && bodyId != null;
if (onlyBodyIdSpecified) {
addBodyIdError(bodyId, "must not be specified");
}
boolean bothIdsSpecified = pathId != null && bodyId != null;
if (bothIdsSpecified && !pathId.equals(bodyId)) {
addBodyIdError(bodyId, String.format("does not match pathId: %d", pathId));
}
}
private void addBodyIdError(Integer bodyId, String message) {
BindingError error = new BindingError();
error.setObjectName("body");
error.setFieldName("id");
error.setFieldValue(bodyId.toString());
error.setErrorMessage(message);
addError(error);
}
private final List<BindingError> bindingErrors = new ArrayList<BindingError>();
public void addError(BindingError bindingError) {
this.bindingErrors.add(bindingError);
}
public void addAllErrors(BindingResult bindingResult) {
for (FieldError fieldError : bindingResult.getFieldErrors()) {
BindingError error = new BindingError();
error.setObjectName(fieldError.getObjectName());
error.setFieldName(fieldError.getField());
error.setFieldValue(String.valueOf(fieldError.getRejectedValue()));
error.setErrorMessage(fieldError.getDefaultMessage());
addError(error);
}
}
public String toJSON() {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
String errorsAsJSON = "";
try {
errorsAsJSON = mapper.writeValueAsString(bindingErrors);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return errorsAsJSON;
}
@Override
public String toString() {
return "BindingErrorsResponse [bindingErrors=" + bindingErrors + "]";
}
protected static class BindingError {
private String objectName;
private String fieldName;
private String fieldValue;
private String errorMessage;
public BindingError() {
this.objectName = "";
this.fieldName = "";
this.fieldValue = "";
this.errorMessage = "";
}
protected void setObjectName(String objectName) {
this.objectName = objectName;
}
protected void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
protected void setFieldValue(String fieldValue) {
this.fieldValue = fieldValue;
}
protected void setErrorMessage(String error_message) {
this.errorMessage = error_message;
}
@Override
public String toString() {
return "BindingError [objectName=" + objectName + ", fieldName=" + fieldName + ", fieldValue=" + fieldValue
+ ", errorMessage=" + errorMessage + "]";
}
}
}

View file

@ -0,0 +1,177 @@
/*
* Copyright 2016-2017 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.rest.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.mapper.OwnerMapper;
import org.springframework.samples.petclinic.mapper.PetMapper;
import org.springframework.samples.petclinic.mapper.VisitMapper;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.rest.api.OwnersApi;
import org.springframework.samples.petclinic.rest.dto.*;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import jakarta.transaction.Transactional;
import java.util.Collection;
import java.util.List;
/**
* @author Vitaliy Fedoriv
*/
@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
@RequestMapping("/api")
public class OwnerRestController implements OwnersApi {
private final ClinicService clinicService;
private final OwnerMapper ownerMapper;
private final PetMapper petMapper;
private final VisitMapper visitMapper;
public OwnerRestController(ClinicService clinicService,
OwnerMapper ownerMapper,
PetMapper petMapper,
VisitMapper visitMapper) {
this.clinicService = clinicService;
this.ownerMapper = ownerMapper;
this.petMapper = petMapper;
this.visitMapper = visitMapper;
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<List<OwnerDto>> listOwners(String lastName) {
Collection<Owner> owners;
if (lastName != null) {
owners = this.clinicService.findOwnerByLastName(lastName);
} else {
owners = this.clinicService.findAllOwners();
}
if (owners.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(ownerMapper.toOwnerDtoCollection(owners), HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<OwnerDto> getOwner(Integer ownerId) {
Owner owner = this.clinicService.findOwnerById(ownerId);
if (owner == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(ownerMapper.toOwnerDto(owner), HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<OwnerDto> addOwner(OwnerFieldsDto ownerFieldsDto) {
HttpHeaders headers = new HttpHeaders();
Owner owner = ownerMapper.toOwner(ownerFieldsDto);
this.clinicService.saveOwner(owner);
OwnerDto ownerDto = ownerMapper.toOwnerDto(owner);
headers.setLocation(UriComponentsBuilder.newInstance()
.path("/api/owners/{id}").buildAndExpand(owner.getId()).toUri());
return new ResponseEntity<>(ownerDto, headers, HttpStatus.CREATED);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<OwnerDto> updateOwner(Integer ownerId, OwnerFieldsDto ownerFieldsDto) {
Owner currentOwner = this.clinicService.findOwnerById(ownerId);
if (currentOwner == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
currentOwner.setAddress(ownerFieldsDto.getAddress());
currentOwner.setCity(ownerFieldsDto.getCity());
currentOwner.setFirstName(ownerFieldsDto.getFirstName());
currentOwner.setLastName(ownerFieldsDto.getLastName());
currentOwner.setTelephone(ownerFieldsDto.getTelephone());
this.clinicService.saveOwner(currentOwner);
return new ResponseEntity<>(ownerMapper.toOwnerDto(currentOwner), HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Transactional
@Override
public ResponseEntity<OwnerDto> deleteOwner(Integer ownerId) {
Owner owner = this.clinicService.findOwnerById(ownerId);
if (owner == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
this.clinicService.deleteOwner(owner);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<PetDto> addPetToOwner(Integer ownerId, PetFieldsDto petFieldsDto) {
HttpHeaders headers = new HttpHeaders();
Pet pet = petMapper.toPet(petFieldsDto);
Owner owner = new Owner();
owner.setId(ownerId);
pet.setOwner(owner);
this.clinicService.savePet(pet);
PetDto petDto = petMapper.toPetDto(pet);
headers.setLocation(UriComponentsBuilder.newInstance().path("/api/pets/{id}")
.buildAndExpand(pet.getId()).toUri());
return new ResponseEntity<>(petDto, headers, HttpStatus.CREATED);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<VisitDto> addVisitToOwner(Integer ownerId, Integer petId, VisitFieldsDto visitFieldsDto) {
HttpHeaders headers = new HttpHeaders();
Visit visit = visitMapper.toVisit(visitFieldsDto);
Pet pet = new Pet();
pet.setId(petId);
visit.setPet(pet);
this.clinicService.saveVisit(visit);
VisitDto visitDto = visitMapper.toVisitDto(visit);
headers.setLocation(UriComponentsBuilder.newInstance().path("/api/visits/{id}")
.buildAndExpand(visit.getId()).toUri());
return new ResponseEntity<>(visitDto, headers, HttpStatus.CREATED);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<PetDto> getOwnersPet(Integer ownerId, Integer petId) {
Owner owner = this.clinicService.findOwnerById(ownerId);
if (owner != null) {
Pet pet = owner.getPet(petId);
if (pet != null) {
return new ResponseEntity<>(petMapper.toPetDto(pet), HttpStatus.OK);
}
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright 2016-2017 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.rest.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.mapper.PetMapper;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.rest.api.PetsApi;
import org.springframework.samples.petclinic.rest.dto.PetDto;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.ArrayList;
import java.util.List;
/**
* @author Vitaliy Fedoriv
*/
@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
@RequestMapping("api")
public class PetRestController implements PetsApi {
private final ClinicService clinicService;
private final PetMapper petMapper;
public PetRestController(ClinicService clinicService, PetMapper petMapper) {
this.clinicService = clinicService;
this.petMapper = petMapper;
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<PetDto> getPet(Integer petId) {
PetDto pet = petMapper.toPetDto(this.clinicService.findPetById(petId));
if (pet == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(pet, HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<List<PetDto>> listPets() {
List<PetDto> pets = new ArrayList<>(petMapper.toPetsDto(this.clinicService.findAllPets()));
if (pets.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(pets, HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<PetDto> updatePet(Integer petId, PetDto petDto) {
Pet currentPet = this.clinicService.findPetById(petId);
if (currentPet == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
currentPet.setBirthDate(petDto.getBirthDate());
currentPet.setName(petDto.getName());
currentPet.setType(petMapper.toPetType(petDto.getType()));
this.clinicService.savePet(currentPet);
return new ResponseEntity<>(petMapper.toPetDto(currentPet), HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<PetDto> deletePet(Integer petId) {
Pet pet = this.clinicService.findPetById(petId);
if (pet == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
this.clinicService.deletePet(pet);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<PetDto> addPet(PetDto petDto) {
HttpHeaders headers = new HttpHeaders();
Pet pet = petMapper.toPet(petDto);
this.clinicService.savePet(pet);
headers.setLocation(UriComponentsBuilder.newInstance().path("/api/pets/{id}").buildAndExpand(pet.getId()).toUri());
return new ResponseEntity<>(petMapper.toPetDto(pet), headers, HttpStatus.CREATED);
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright 2016-2017 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.rest.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.mapper.PetTypeMapper;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.rest.api.PettypesApi;
import org.springframework.samples.petclinic.rest.dto.PetTypeDto;
import org.springframework.samples.petclinic.rest.dto.PetTypeFieldsDto;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
@RequestMapping("api")
public class PetTypeRestController implements PettypesApi {
private final ClinicService clinicService;
private final PetTypeMapper petTypeMapper;
public PetTypeRestController(ClinicService clinicService, PetTypeMapper petTypeMapper) {
this.clinicService = clinicService;
this.petTypeMapper = petTypeMapper;
}
@PreAuthorize("hasAnyRole(@roles.OWNER_ADMIN, @roles.VET_ADMIN)")
@Override
public ResponseEntity<List<PetTypeDto>> listPetTypes() {
List<PetType> petTypes = new ArrayList<>(this.clinicService.findAllPetTypes());
if (petTypes.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(petTypeMapper.toPetTypeDtos(petTypes), HttpStatus.OK);
}
@PreAuthorize("hasAnyRole(@roles.OWNER_ADMIN, @roles.VET_ADMIN)")
@Override
public ResponseEntity<PetTypeDto> getPetType(Integer petTypeId) {
PetType petType = this.clinicService.findPetTypeById(petTypeId);
if (petType == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(petTypeMapper.toPetTypeDto(petType), HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<PetTypeDto> addPetType(PetTypeFieldsDto petTypeFieldsDto) {
HttpHeaders headers = new HttpHeaders();
final PetType type = petTypeMapper.toPetType(petTypeFieldsDto);
this.clinicService.savePetType(type);
headers.setLocation(UriComponentsBuilder.newInstance().path("/api/pettypes/{id}").buildAndExpand(type.getId()).toUri());
return new ResponseEntity<>(petTypeMapper.toPetTypeDto(type), headers, HttpStatus.CREATED);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<PetTypeDto> updatePetType(Integer petTypeId, PetTypeDto petTypeDto) {
PetType currentPetType = this.clinicService.findPetTypeById(petTypeId);
if (currentPetType == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
currentPetType.setName(petTypeDto.getName());
this.clinicService.savePetType(currentPetType);
return new ResponseEntity<>(petTypeMapper.toPetTypeDto(currentPetType), HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Transactional
@Override
public ResponseEntity<PetTypeDto> deletePetType(Integer petTypeId) {
PetType petType = this.clinicService.findPetTypeById(petTypeId);
if (petType == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
this.clinicService.deletePetType(petType);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2016-2018 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.rest.controller;
import java.io.IOException;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Vitaliy Fedoriv
*
*/
@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
@RequestMapping("/")
public class RootRestController {
@Value("#{servletContext.contextPath}")
private String servletContextPath;
@RequestMapping(value = "/")
public void redirectToSwagger(HttpServletResponse response) throws IOException {
response.sendRedirect(this.servletContextPath + "/swagger-ui/index.html");
}
}

View file

@ -0,0 +1,108 @@
/*
* Copyright 2016-2017 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.rest.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.mapper.SpecialtyMapper;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.rest.api.SpecialtiesApi;
import org.springframework.samples.petclinic.rest.dto.SpecialtyDto;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* @author Vitaliy Fedoriv
*/
@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
@RequestMapping("api")
public class SpecialtyRestController implements SpecialtiesApi {
private final ClinicService clinicService;
private final SpecialtyMapper specialtyMapper;
public SpecialtyRestController(ClinicService clinicService, SpecialtyMapper specialtyMapper) {
this.clinicService = clinicService;
this.specialtyMapper = specialtyMapper;
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<List<SpecialtyDto>> listSpecialties() {
List<SpecialtyDto> specialties = new ArrayList<>();
specialties.addAll(specialtyMapper.toSpecialtyDtos(this.clinicService.findAllSpecialties()));
if (specialties.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(specialties, HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<SpecialtyDto> getSpecialty(Integer specialtyId) {
Specialty specialty = this.clinicService.findSpecialtyById(specialtyId);
if (specialty == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(specialtyMapper.toSpecialtyDto(specialty), HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<SpecialtyDto> addSpecialty(SpecialtyDto specialtyDto) {
HttpHeaders headers = new HttpHeaders();
Specialty specialty = specialtyMapper.toSpecialty(specialtyDto);
this.clinicService.saveSpecialty(specialty);
headers.setLocation(UriComponentsBuilder.newInstance().path("/api/specialties/{id}").buildAndExpand(specialty.getId()).toUri());
return new ResponseEntity<>(specialtyMapper.toSpecialtyDto(specialty), headers, HttpStatus.CREATED);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<SpecialtyDto> updateSpecialty(Integer specialtyId, SpecialtyDto specialtyDto) {
Specialty currentSpecialty = this.clinicService.findSpecialtyById(specialtyId);
if (currentSpecialty == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
currentSpecialty.setName(specialtyDto.getName());
this.clinicService.saveSpecialty(currentSpecialty);
return new ResponseEntity<>(specialtyMapper.toSpecialtyDto(currentSpecialty), HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Transactional
@Override
public ResponseEntity<SpecialtyDto> deleteSpecialty(Integer specialtyId) {
Specialty specialty = this.clinicService.findSpecialtyById(specialtyId);
if (specialty == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
this.clinicService.deleteSpecialty(specialty);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright 2016-2017 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.rest.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.mapper.UserMapper;
import org.springframework.samples.petclinic.model.User;
import org.springframework.samples.petclinic.rest.api.UsersApi;
import org.springframework.samples.petclinic.rest.dto.UserDto;
import org.springframework.samples.petclinic.service.UserService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.validation.Valid;
@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
@RequestMapping("api")
public class UserRestController implements UsersApi {
private final UserService userService;
private final UserMapper userMapper;
public UserRestController(UserService userService, UserMapper userMapper) {
this.userService = userService;
this.userMapper = userMapper;
}
@PreAuthorize( "hasRole(@roles.ADMIN)" )
@Override
public ResponseEntity<UserDto> addUser(UserDto userDto) {
HttpHeaders headers = new HttpHeaders();
User user = userMapper.toUser(userDto);
this.userService.saveUser(user);
return new ResponseEntity<>(userMapper.toUserDto(user), headers, HttpStatus.CREATED);
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright 2016-2018 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.rest.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.mapper.SpecialtyMapper;
import org.springframework.samples.petclinic.mapper.VetMapper;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.rest.api.VetsApi;
import org.springframework.samples.petclinic.rest.dto.VetDto;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Vitaliy Fedoriv
*/
@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
@RequestMapping("api")
public class VetRestController implements VetsApi {
private final ClinicService clinicService;
private final VetMapper vetMapper;
private final SpecialtyMapper specialtyMapper;
public VetRestController(ClinicService clinicService, VetMapper vetMapper, SpecialtyMapper specialtyMapper) {
this.clinicService = clinicService;
this.vetMapper = vetMapper;
this.specialtyMapper = specialtyMapper;
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<List<VetDto>> listVets() {
List<VetDto> vets = new ArrayList<>();
vets.addAll(vetMapper.toVetDtos(this.clinicService.findAllVets()));
if (vets.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(vets, HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<VetDto> getVet(Integer vetId) {
Vet vet = this.clinicService.findVetById(vetId);
if (vet == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(vetMapper.toVetDto(vet), HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<VetDto> addVet(VetDto vetDto) {
HttpHeaders headers = new HttpHeaders();
Vet vet = vetMapper.toVet(vetDto);
if(vet.getNrOfSpecialties() > 0){
List<Specialty> vetSpecialities = this.clinicService.findSpecialtiesByNameIn(vet.getSpecialties().stream().map(Specialty::getName).collect(Collectors.toSet()));
vet.setSpecialties(vetSpecialities);
}
this.clinicService.saveVet(vet);
headers.setLocation(UriComponentsBuilder.newInstance().path("/api/vets/{id}").buildAndExpand(vet.getId()).toUri());
return new ResponseEntity<>(vetMapper.toVetDto(vet), headers, HttpStatus.CREATED);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Override
public ResponseEntity<VetDto> updateVet(Integer vetId,VetDto vetDto) {
Vet currentVet = this.clinicService.findVetById(vetId);
if (currentVet == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
currentVet.setFirstName(vetDto.getFirstName());
currentVet.setLastName(vetDto.getLastName());
currentVet.clearSpecialties();
for (Specialty spec : specialtyMapper.toSpecialtys(vetDto.getSpecialties())) {
currentVet.addSpecialty(spec);
}
if(currentVet.getNrOfSpecialties() > 0){
List<Specialty> vetSpecialities = this.clinicService.findSpecialtiesByNameIn(currentVet.getSpecialties().stream().map(Specialty::getName).collect(Collectors.toSet()));
currentVet.setSpecialties(vetSpecialities);
}
this.clinicService.saveVet(currentVet);
return new ResponseEntity<>(vetMapper.toVetDto(currentVet), HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasRole(@roles.VET_ADMIN)")
@Transactional
@Override
public ResponseEntity<VetDto> deleteVet(Integer vetId) {
Vet vet = this.clinicService.findVetById(vetId);
if (vet == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
this.clinicService.deleteVet(vet);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}

View file

@ -0,0 +1,110 @@
/*
* Copyright 2016-2017 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.rest.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.mapper.VisitMapper;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.rest.api.VisitsApi;
import org.springframework.samples.petclinic.rest.dto.VisitDto;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* @author Vitaliy Fedoriv
*/
@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
@RequestMapping("api")
public class VisitRestController implements VisitsApi {
private final ClinicService clinicService;
private final VisitMapper visitMapper;
public VisitRestController(ClinicService clinicService, VisitMapper visitMapper) {
this.clinicService = clinicService;
this.visitMapper = visitMapper;
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<List<VisitDto>> listVisits() {
List<Visit> visits = new ArrayList<>(this.clinicService.findAllVisits());
if (visits.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(new ArrayList<>(visitMapper.toVisitsDto(visits)), HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<VisitDto> getVisit( Integer visitId) {
Visit visit = this.clinicService.findVisitById(visitId);
if (visit == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(visitMapper.toVisitDto(visit), HttpStatus.OK);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<VisitDto> addVisit(VisitDto visitDto) {
HttpHeaders headers = new HttpHeaders();
Visit visit = visitMapper.toVisit(visitDto);
this.clinicService.saveVisit(visit);
visitDto = visitMapper.toVisitDto(visit);
headers.setLocation(UriComponentsBuilder.newInstance().path("/api/visits/{id}").buildAndExpand(visit.getId()).toUri());
return new ResponseEntity<>(visitDto, headers, HttpStatus.CREATED);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Override
public ResponseEntity<VisitDto> updateVisit(Integer visitId, VisitDto visitDto) {
Visit currentVisit = this.clinicService.findVisitById(visitId);
if (currentVisit == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
currentVisit.setDate(visitDto.getDate());
currentVisit.setDescription(visitDto.getDescription());
this.clinicService.saveVisit(currentVisit);
return new ResponseEntity<>(visitMapper.toVisitDto(currentVisit), HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@Transactional
@Override
public ResponseEntity<VisitDto> deleteVisit(Integer visitId) {
Visit visit = this.clinicService.findVisitById(visitId);
if (visit == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
this.clinicService.deleteVisit(visit);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}

View file

@ -0,0 +1,5 @@
/**
* The classes in this package represent PetClinic's REST API.
*/
package org.springframework.samples.petclinic.rest;

View file

@ -0,0 +1,58 @@
package org.springframework.samples.petclinic.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableMethodSecurity(prePostEnabled = true) // Enable @PreAuthorize method-level security
@ConditionalOnProperty(name = "petclinic.security.enable", havingValue = "true")
public class BasicAuthenticationConfig {
@Autowired
private DataSource dataSource;
@Bean
public PasswordEncoder passwordEncoder() {
var encoders = Map.of("noop", NoOpPasswordEncoder.getInstance());
var passwordEncoder = new DelegatingPasswordEncoder("noop", encoders);
passwordEncoder.setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance());
return passwordEncoder;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
// @formatter:on
return http.build();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username,password,enabled from users where username=?")
.authoritiesByUsernameQuery("select username,role from roles where username=?");
// @formatter:on
}
}

View file

@ -0,0 +1,29 @@
package org.springframework.samples.petclinic.security;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
/**
* Starting from Spring Boot 2, if Spring Security is present, endpoints are secured by default
* using Spring Securitys content-negotiation strategy.
*/
@Configuration
@ConditionalOnProperty(name = "petclinic.security.enable", havingValue = "false")
public class DisableSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((authz) -> authz
.anyRequest().permitAll()
);
// @formatter:on
return http.build();
}
}

View file

@ -0,0 +1,11 @@
package org.springframework.samples.petclinic.security;
import org.springframework.stereotype.Component;
@Component
public class Roles {
public final String OWNER_ADMIN = "ROLE_OWNER_ADMIN";
public final String VET_ADMIN = "ROLE_VET_ADMIN";
public final String ADMIN = "ROLE_ADMIN";
}

View file

@ -0,0 +1,70 @@
/*
* Copyright 2002-2017 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.service;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.springframework.dao.DataAccessException;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.model.Visit;
/**
* Mostly used as a facade so all controllers have a single point of entry
*
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
public interface ClinicService {
Pet findPetById(int id) throws DataAccessException;
Collection<Pet> findAllPets() throws DataAccessException;
void savePet(Pet pet) throws DataAccessException;
void deletePet(Pet pet) throws DataAccessException;
Collection<Visit> findVisitsByPetId(int petId);
Visit findVisitById(int visitId) throws DataAccessException;
Collection<Visit> findAllVisits() throws DataAccessException;
void saveVisit(Visit visit) throws DataAccessException;
void deleteVisit(Visit visit) throws DataAccessException;
Vet findVetById(int id) throws DataAccessException;
Collection<Vet> findVets() throws DataAccessException;
Collection<Vet> findAllVets() throws DataAccessException;
void saveVet(Vet vet) throws DataAccessException;
void deleteVet(Vet vet) throws DataAccessException;
Owner findOwnerById(int id) throws DataAccessException;
Collection<Owner> findAllOwners() throws DataAccessException;
void saveOwner(Owner owner) throws DataAccessException;
void deleteOwner(Owner owner) throws DataAccessException;
Collection<Owner> findOwnerByLastName(String lastName) throws DataAccessException;
PetType findPetTypeById(int petTypeId);
Collection<PetType> findAllPetTypes() throws DataAccessException;
Collection<PetType> findPetTypes() throws DataAccessException;
void savePetType(PetType petType) throws DataAccessException;
void deletePetType(PetType petType) throws DataAccessException;
Specialty findSpecialtyById(int specialtyId);
Collection<Specialty> findAllSpecialties() throws DataAccessException;
void saveSpecialty(Specialty specialty) throws DataAccessException;
void deleteSpecialty(Specialty specialty) throws DataAccessException;
List<Specialty> findSpecialtiesByNameIn(Set<String> names) throws DataAccessException;
}

View file

@ -0,0 +1,250 @@
/*
* Copyright 2002-2017 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.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.*;
import org.springframework.samples.petclinic.repository.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
/**
* Mostly used as a facade for all Petclinic controllers
* Also a placeholder for @Transactional and @Cacheable annotations
*
* @author Michael Isvy
* @author Vitaliy Fedoriv
*/
@Service
public class ClinicServiceImpl implements ClinicService {
private final PetRepository petRepository;
private final VetRepository vetRepository;
private final OwnerRepository ownerRepository;
private final VisitRepository visitRepository;
private final SpecialtyRepository specialtyRepository;
private final PetTypeRepository petTypeRepository;
@Autowired
public ClinicServiceImpl(
PetRepository petRepository,
VetRepository vetRepository,
OwnerRepository ownerRepository,
VisitRepository visitRepository,
SpecialtyRepository specialtyRepository,
PetTypeRepository petTypeRepository) {
this.petRepository = petRepository;
this.vetRepository = vetRepository;
this.ownerRepository = ownerRepository;
this.visitRepository = visitRepository;
this.specialtyRepository = specialtyRepository;
this.petTypeRepository = petTypeRepository;
}
@Override
@Transactional(readOnly = true)
public Collection<Pet> findAllPets() throws DataAccessException {
return petRepository.findAll();
}
@Override
@Transactional
public void deletePet(Pet pet) throws DataAccessException {
petRepository.delete(pet);
}
@Override
@Transactional(readOnly = true)
public Visit findVisitById(int visitId) throws DataAccessException {
return findEntityById(() -> visitRepository.findById(visitId));
}
@Override
@Transactional(readOnly = true)
public Collection<Visit> findAllVisits() throws DataAccessException {
return visitRepository.findAll();
}
@Override
@Transactional
public void deleteVisit(Visit visit) throws DataAccessException {
visitRepository.delete(visit);
}
@Override
@Transactional(readOnly = true)
public Vet findVetById(int id) throws DataAccessException {
return findEntityById(() -> vetRepository.findById(id));
}
@Override
@Transactional(readOnly = true)
public Collection<Vet> findAllVets() throws DataAccessException {
return vetRepository.findAll();
}
@Override
@Transactional
public void saveVet(Vet vet) throws DataAccessException {
vetRepository.save(vet);
}
@Override
@Transactional
public void deleteVet(Vet vet) throws DataAccessException {
vetRepository.delete(vet);
}
@Override
@Transactional(readOnly = true)
public Collection<Owner> findAllOwners() throws DataAccessException {
return ownerRepository.findAll();
}
@Override
@Transactional
public void deleteOwner(Owner owner) throws DataAccessException {
ownerRepository.delete(owner);
}
@Override
@Transactional(readOnly = true)
public PetType findPetTypeById(int petTypeId) {
return findEntityById(() -> petTypeRepository.findById(petTypeId));
}
@Override
@Transactional(readOnly = true)
public Collection<PetType> findAllPetTypes() throws DataAccessException {
return petTypeRepository.findAll();
}
@Override
@Transactional
public void savePetType(PetType petType) throws DataAccessException {
petTypeRepository.save(petType);
}
@Override
@Transactional
public void deletePetType(PetType petType) throws DataAccessException {
petTypeRepository.delete(petType);
}
@Override
@Transactional(readOnly = true)
public Specialty findSpecialtyById(int specialtyId) {
return findEntityById(() -> specialtyRepository.findById(specialtyId));
}
@Override
@Transactional(readOnly = true)
public Collection<Specialty> findAllSpecialties() throws DataAccessException {
return specialtyRepository.findAll();
}
@Override
@Transactional
public void saveSpecialty(Specialty specialty) throws DataAccessException {
specialtyRepository.save(specialty);
}
@Override
@Transactional
public void deleteSpecialty(Specialty specialty) throws DataAccessException {
specialtyRepository.delete(specialty);
}
@Override
@Transactional(readOnly = true)
public Collection<PetType> findPetTypes() throws DataAccessException {
return petRepository.findPetTypes();
}
@Override
@Transactional(readOnly = true)
public Owner findOwnerById(int id) throws DataAccessException {
return findEntityById(() -> ownerRepository.findById(id));
}
@Override
@Transactional(readOnly = true)
public Pet findPetById(int id) throws DataAccessException {
return findEntityById(() -> petRepository.findById(id));
}
@Override
@Transactional
public void savePet(Pet pet) throws DataAccessException {
petRepository.save(pet);
}
@Override
@Transactional
public void saveVisit(Visit visit) throws DataAccessException {
visitRepository.save(visit);
}
@Override
@Transactional(readOnly = true)
public Collection<Vet> findVets() throws DataAccessException {
return vetRepository.findAll();
}
@Override
@Transactional
public void saveOwner(Owner owner) throws DataAccessException {
ownerRepository.save(owner);
}
@Override
@Transactional(readOnly = true)
public Collection<Owner> findOwnerByLastName(String lastName) throws DataAccessException {
return ownerRepository.findByLastName(lastName);
}
@Override
@Transactional(readOnly = true)
public Collection<Visit> findVisitsByPetId(int petId) {
return visitRepository.findByPetId(petId);
}
@Override
@Transactional(readOnly = true)
public List<Specialty> findSpecialtiesByNameIn(Set<String> names) {
return findEntityById(() -> specialtyRepository.findSpecialtiesByNameIn(names));
}
private <T> T findEntityById(Supplier<T> supplier) {
try {
return supplier.get();
} catch (ObjectRetrievalFailureException | EmptyResultDataAccessException e) {
// Just ignore not found exceptions for Jdbc/Jpa realization
return null;
}
}
}

View file

@ -0,0 +1,8 @@
package org.springframework.samples.petclinic.service;
import org.springframework.samples.petclinic.model.User;
public interface UserService {
void saveUser(User user) ;
}

View file

@ -0,0 +1,36 @@
package org.springframework.samples.petclinic.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.model.User;
import org.springframework.samples.petclinic.model.Role;
import org.springframework.samples.petclinic.repository.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
@Transactional
public void saveUser(User user) {
if(user.getRoles() == null || user.getRoles().isEmpty()) {
throw new IllegalArgumentException("User must have at least a role set!");
}
for (Role role : user.getRoles()) {
if(!role.getName().startsWith("ROLE_")) {
role.setName("ROLE_" + role.getName());
}
if(role.getUser() == null) {
role.setUser(user);
}
}
userRepository.save(user);
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.util;
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 aspect that monitors call count and call invocation time. It uses JMX annotations and therefore can be
* monitored using any JMX console such as the jConsole
* <p/>
* This is only useful if you use JPA or JDBC. Spring-data-jpa doesn't have any correctly annotated classes to join on
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Michael Isvy
* @since 2.5
*/
@ManagedResource("petclinic:type=CallMonitor")
@Aspect
public class CallMonitoringAspect {
private boolean enabled = true;
private int callCount = 0;
private long accumulatedCallTime = 0;
@ManagedAttribute
public boolean isEnabled() {
return enabled;
}
@ManagedAttribute
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@ManagedOperation
public void reset() {
this.callCount = 0;
this.accumulatedCallTime = 0;
}
@ManagedAttribute
public int getCallCount() {
return callCount;
}
@ManagedAttribute
public long getCallTime() {
if (this.callCount > 0)
return this.accumulatedCallTime / this.callCount;
else
return 0;
}
@Around("within(@org.springframework.stereotype.Repository *)")
public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
if (this.enabled) {
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,54 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.util;
import java.util.Collection;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.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
* @see org.springframework.samples.petclinic.model.BaseEntity
* @since 29.10.2003
*/
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() == entityId && entityClass.isInstance(entity)) {
return entity;
}
}
throw new ObjectRetrievalFailureException(entityClass, entityId);
}
}

View file

@ -0,0 +1,8 @@
# HSQLDB config start
#----------------------------------------------------------------
spring.datasource.url=jdbc:hsqldb:mem:petclinic
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=none
#----------------------------------------------------------------
# HSQLDB config end

View file

@ -0,0 +1,7 @@
# database init, supports mysql too
database=mysql
spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic}
spring.datasource.username=${MYSQL_USER:petclinic}
spring.datasource.password=${MYSQL_PASS:petclinic}
# SQL is written to be idempotent so this is safe
spring.sql.init.mode=always

View file

@ -0,0 +1,6 @@
database=postgres
spring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhost/petclinic}
spring.datasource.username=${POSTGRES_USER:petclinic}
spring.datasource.password=${POSTGRES_PASS:petclinic}
# SQL is written to be idempotent so this is safe
spring.sql.init.mode=always

View file

@ -0,0 +1,44 @@
# active profiles config
#
# application use two active profiles
#
# one - for select database
# ------------------------------------------------
# When using HSQL, use: hsqldb
# When using MySQL, use: mysql
# When using PostgeSQL, use: postgres
# ------------------------------------------------
#
# one for select repository layer
# ------------------------------------------------
# When using Spring jpa, use: jpa
# When using Spring JDBC, use: jdbc
# When using Spring Data JPA, use: spring-data-jpa
# ------------------------------------------------
spring.profiles.active=hsqldb,spring-data-jpa
# ------------------------------------------------
server.port=9966
server.servlet.context-path=/petclinic/
# database init, supports mysql and postgres too
database=hsqldb
spring.sql.init.schema-locations=classpath*:db/${database}/schema.sql
spring.sql.init.data-locations=classpath*:db/${database}/data.sql
spring.messages.basename=messages/messages
spring.jpa.open-in-view=false
logging.level.org.springframework=INFO
#logging.level.org.springframework=DEBUG
#logging.level.org.hibernate.SQL=DEBUG
#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
# enable the desired authentication type
# by default the authentication is disabled
petclinic.security.enable=false

View file

@ -0,0 +1,59 @@
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', '2010-09-07', 1, 1);
INSERT INTO pets VALUES (2, 'Basil', '2012-08-06', 6, 2);
INSERT INTO pets VALUES (3, 'Rosy', '2011-04-17', 2, 3);
INSERT INTO pets VALUES (4, 'Jewel', '2010-03-07', 2, 3);
INSERT INTO pets VALUES (5, 'Iggy', '2010-11-30', 3, 4);
INSERT INTO pets VALUES (6, 'George', '2010-01-20', 4, 5);
INSERT INTO pets VALUES (7, 'Samantha', '2012-09-04', 1, 6);
INSERT INTO pets VALUES (8, 'Max', '2012-09-04', 1, 6);
INSERT INTO pets VALUES (9, 'Lucky', '2011-08-06', 5, 7);
INSERT INTO pets VALUES (10, 'Mulligan', '2007-02-24', 2, 8);
INSERT INTO pets VALUES (11, 'Freddy', '2010-03-09', 5, 9);
INSERT INTO pets VALUES (12, 'Lucky', '2010-06-24', 2, 10);
INSERT INTO pets VALUES (13, 'Sly', '2012-06-08', 1, 10);
INSERT INTO visits VALUES (1, 7, '2013-01-01', 'rabies shot');
INSERT INTO visits VALUES (2, 8, '2013-01-02', 'rabies shot');
INSERT INTO visits VALUES (3, 8, '2013-01-03', 'neutered');
INSERT INTO visits VALUES (4, 7, '2013-01-04', 'spayed');
INSERT INTO users(username,password,enabled) VALUES ('admin','{noop}admin', true);
INSERT INTO roles (username, role) VALUES ('admin', 'ROLE_OWNER_ADMIN');
INSERT INTO roles (username, role) VALUES ('admin', 'ROLE_VET_ADMIN');
INSERT INTO roles (username, role) VALUES ('admin', 'ROLE_ADMIN');

View file

@ -0,0 +1,82 @@
DROP TABLE vet_specialties IF EXISTS;
DROP TABLE vets IF EXISTS;
DROP TABLE specialties IF EXISTS;
DROP TABLE visits IF EXISTS;
DROP TABLE pets IF EXISTS;
DROP TABLE types IF EXISTS;
DROP TABLE owners IF EXISTS;
DROP TABLE roles IF EXISTS;
DROP TABLE users IF EXISTS;
CREATE TABLE vets (
id INTEGER 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 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 IDENTITY PRIMARY KEY,
name VARCHAR(80)
);
CREATE INDEX types_name ON types (name);
CREATE TABLE owners (
id INTEGER IDENTITY PRIMARY KEY,
first_name VARCHAR(30),
last_name VARCHAR_IGNORECASE(30),
address VARCHAR(255),
city VARCHAR(80),
telephone VARCHAR(20)
);
CREATE INDEX owners_last_name ON owners (last_name);
CREATE TABLE pets (
id INTEGER 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 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);
CREATE TABLE users (
username VARCHAR(20) NOT NULL ,
password VARCHAR(20) NOT NULL ,
enabled BOOLEAN DEFAULT TRUE NOT NULL ,
PRIMARY KEY (username)
);
CREATE TABLE roles (
id INTEGER IDENTITY PRIMARY KEY,
username VARCHAR(20) NOT NULL,
role VARCHAR(20) NOT NULL
);
ALTER TABLE roles ADD CONSTRAINT fk_username FOREIGN KEY (username) REFERENCES users (username);
CREATE INDEX fk_username_idx ON roles (username);

View file

@ -0,0 +1,59 @@
INSERT IGNORE INTO vets VALUES (1, 'James', 'Carter');
INSERT IGNORE INTO vets VALUES (2, 'Helen', 'Leary');
INSERT IGNORE INTO vets VALUES (3, 'Linda', 'Douglas');
INSERT IGNORE INTO vets VALUES (4, 'Rafael', 'Ortega');
INSERT IGNORE INTO vets VALUES (5, 'Henry', 'Stevens');
INSERT IGNORE INTO vets VALUES (6, 'Sharon', 'Jenkins');
INSERT IGNORE INTO specialties VALUES (1, 'radiology');
INSERT IGNORE INTO specialties VALUES (2, 'surgery');
INSERT IGNORE INTO specialties VALUES (3, 'dentistry');
INSERT IGNORE INTO vet_specialties VALUES (2, 1);
INSERT IGNORE INTO vet_specialties VALUES (3, 2);
INSERT IGNORE INTO vet_specialties VALUES (3, 3);
INSERT IGNORE INTO vet_specialties VALUES (4, 2);
INSERT IGNORE INTO vet_specialties VALUES (5, 1);
INSERT IGNORE INTO types VALUES (1, 'cat');
INSERT IGNORE INTO types VALUES (2, 'dog');
INSERT IGNORE INTO types VALUES (3, 'lizard');
INSERT IGNORE INTO types VALUES (4, 'snake');
INSERT IGNORE INTO types VALUES (5, 'bird');
INSERT IGNORE INTO types VALUES (6, 'hamster');
INSERT IGNORE INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
INSERT IGNORE INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');
INSERT IGNORE INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');
INSERT IGNORE INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');
INSERT IGNORE INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');
INSERT IGNORE INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');
INSERT IGNORE INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');
INSERT IGNORE INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');
INSERT IGNORE INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');
INSERT IGNORE INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');
INSERT IGNORE INTO pets VALUES (1, 'Leo', '2000-09-07', 1, 1);
INSERT IGNORE INTO pets VALUES (2, 'Basil', '2002-08-06', 6, 2);
INSERT IGNORE INTO pets VALUES (3, 'Rosy', '2001-04-17', 2, 3);
INSERT IGNORE INTO pets VALUES (4, 'Jewel', '2000-03-07', 2, 3);
INSERT IGNORE INTO pets VALUES (5, 'Iggy', '2000-11-30', 3, 4);
INSERT IGNORE INTO pets VALUES (6, 'George', '2000-01-20', 4, 5);
INSERT IGNORE INTO pets VALUES (7, 'Samantha', '1995-09-04', 1, 6);
INSERT IGNORE INTO pets VALUES (8, 'Max', '1995-09-04', 1, 6);
INSERT IGNORE INTO pets VALUES (9, 'Lucky', '1999-08-06', 5, 7);
INSERT IGNORE INTO pets VALUES (10, 'Mulligan', '1997-02-24', 2, 8);
INSERT IGNORE INTO pets VALUES (11, 'Freddy', '2000-03-09', 5, 9);
INSERT IGNORE INTO pets VALUES (12, 'Lucky', '2000-06-24', 2, 10);
INSERT IGNORE INTO pets VALUES (13, 'Sly', '2002-06-08', 1, 10);
INSERT IGNORE INTO visits VALUES (1, 7, '2010-03-04', 'rabies shot');
INSERT IGNORE INTO visits VALUES (2, 8, '2011-03-04', 'rabies shot');
INSERT IGNORE INTO visits VALUES (3, 8, '2009-06-04', 'neutered');
INSERT IGNORE INTO visits VALUES (4, 7, '2008-09-04', 'spayed');
INSERT IGNORE INTO users(username,password,enabled) VALUES ('admin','{noop}admin', true);
INSERT IGNORE INTO roles (username, role) VALUES ('admin', 'ROLE_OWNER_ADMIN');
INSERT IGNORE INTO roles (username, role) VALUES ('admin', 'ROLE_VET_ADMIN');
INSERT IGNORE INTO roles (username, role) VALUES ('admin', 'ROLE_ADMIN');

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