diff --git a/pom.xml b/pom.xml index 680a21923d59389a2cae130ddac7e4296d98435a..644166de79851ca163b10e9815cf13c3bd989b66 100644 --- a/pom.xml +++ b/pom.xml @@ -88,6 +88,11 @@ <!-- Data stores and supporting libs --> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <scope>runtime</scope> + </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> diff --git a/readme.md b/readme.md index c52b4bb787ac1f7dfe8014bb459f0d884c815bf4..a58b1729168b63fc059dacef044a0701e9e4189c 100644 --- a/readme.md +++ b/readme.md @@ -101,48 +101,90 @@ See its repository here: https://github.com/spring-petclinic/spring-petclinic-an ## 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. +By default, Petclinic uses an **in-memory H2 database**, which is automatically populated with sample data at startup. -A similar setup is provided for MySQL and PostgreSQL if a persistent database configuration is needed. +### **Supported Databases** -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.properties` file: -```properties -spring.profiles.active=hsqldb,spring-data-jpa -``` +Petclinic supports the following databases: + +- **H2 (Default, In-Memory)** +- **HSQLDB (Alternative In-Memory Option)** +- **MySQL (Persistent)** +- **PostgreSQL (Persistent)** + +### **Switching Databases** + +You can change the database by updating the `spring.profiles.active` property in `application.properties`: + +| Database | Profile Configuration | +|-----------|----------------------| +| **H2** (Default) | `spring.profiles.active=h2,spring-data-jpa` | +| **HSQLDB** (Alternative In-Memory) | `spring.profiles.active=hsqldb,spring-data-jpa` | +| **MySQL** (Persistent) | `spring.profiles.active=mysql,spring-data-jpa` | +| **PostgreSQL** (Persistent) | `spring.profiles.active=postgres,spring-data-jpa` | + +For more details, 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). -You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker: +### **Using H2 (Default)** +- No additional setup is required. +- The database schema and sample data are loaded automatically from `src/main/resources/db/h2/`. +- You can access the **H2 Console** to inspect the database. +### **Accessing the H2 Console** +1. **Run the application:** + ```sh + mvn spring-boot:run + ``` +2. **Open H2 Console in your browser:** + - **URL**: http://localhost:9966/h2-console + - **JDBC URL**: `jdbc:h2:mem:petclinic` + - **Username**: `sa` + - **Password**: _(leave blank)_ + +### **Using HSQLDB** +- HSQLDB works similarly to H2 as an **in-memory database**. +- No additional setup is required—schema and sample data are loaded automatically from `src/main/resources/db/hsqldb/`. +- Swtich to **HSQLDB** by modifying `application.properties`: + + ```properties + spring.profiles.active=hsqldb,spring-data-jpa + ``` + +### **Using MySQL** +Modify `application.properties`: + +```properties +spring.profiles.active=mysql,spring-data-jpa +``` +Start a MySQL database using 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 +### **Using PostgreSQL** +Modify application.properties: +```properties +spring.profiles.active=postgres,spring-data-jpa +``` +Start a PostgreSQL database using Docker: ```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: +Instead of manually running containers, you can also use `docker-compose.yml`: -```bash +```sh docker-compose --profile mysql up -``` - -or - -```bash docker-compose --profile postgres up ``` +### **Further Documentation** +- [HSQLDB](http://hsqldb.org/doc/2.0/guide/index.html) +- [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt) +- [PostgreSQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/postgres/petclinic_db_setup_postgres.txt) ## API First Approach @@ -231,7 +273,6 @@ git clone https://github.com/spring-petclinic/spring-petclinic-rest.git File -> Import -> Maven -> Existing Maven project ``` - ## Looking for something in particular? | Layer | Source | @@ -243,7 +284,6 @@ File -> Import -> Maven -> Existing Maven project | 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. @@ -264,12 +304,8 @@ hosted in a special GitHub org: [spring-petclinic](https://github.com/spring-pet 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>. - - - +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>. \ No newline at end of file diff --git a/src/main/resources/application-h2.properties b/src/main/resources/application-h2.properties new file mode 100644 index 0000000000000000000000000000000000000000..3eed058cfb8d296f2f522d4ed6d0edcd7a10fae4 --- /dev/null +++ b/src/main/resources/application-h2.properties @@ -0,0 +1,16 @@ +# H2 Database Configuration +spring.datasource.url=jdbc:h2:mem:petclinic;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= +spring.datasource.platform=h2 + +# Hibernate Configuration +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +# Change to 'validate' in production +spring.jpa.hibernate.ddl-auto=update +spring.jpa.show-sql=true + +# H2 Console Configuration +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a9bd37f77f5c837c8cb5e56d0a0df19bcba39f58..bad0e593366498e8aafb8f620053034974dcbc55 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -7,6 +7,7 @@ # When using HSQL, use: hsqldb # When using MySQL, use: mysql # When using PostgeSQL, use: postgres +# When using H2, use: h2 # ------------------------------------------------ # # one for select repository layer @@ -16,19 +17,20 @@ # When using Spring Data JPA, use: spring-data-jpa # ------------------------------------------------ -spring.profiles.active=hsqldb,spring-data-jpa +spring.profiles.active=h2,spring-data-jpa # ------------------------------------------------ server.port=9966 server.servlet.context-path=/petclinic/ -# database init, supports mysql and postgres too -database=hsqldb +# database init, supports hsqldb, mysql and postgres too +database=h2 +# Ensures schema & data reload on every restart (good for local dev) +spring.sql.init.mode=always 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 diff --git a/src/main/resources/db/h2/data.sql b/src/main/resources/db/h2/data.sql new file mode 100644 index 0000000000000000000000000000000000000000..741994529299c04af42125292c6f11830637325e --- /dev/null +++ b/src/main/resources/db/h2/data.sql @@ -0,0 +1,77 @@ +-- Insert Vets +INSERT INTO vets (first_name, last_name) VALUES +('James', 'Carter'), +('Helen', 'Leary'), +('Linda', 'Douglas'), +('Rafael', 'Ortega'), +('Henry', 'Stevens'), +('Sharon', 'Jenkins'); + +-- Insert Specialties +INSERT INTO specialties (name) VALUES +('radiology'), +('surgery'), +('dentistry'); + +-- Link Vets to Specialties +INSERT INTO vet_specialties (vet_id, specialty_id) VALUES +(2, 1), +(3, 2), +(3, 3), +(4, 2), +(5, 1); + +-- Insert Pet Types +INSERT INTO types (name) VALUES +('cat'), +('dog'), +('lizard'), +('snake'), +('bird'), +('hamster'); + +-- Insert Owners +INSERT INTO owners (first_name, last_name, address, city, telephone) VALUES +('George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023'), +('Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749'), +('Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763'), +('Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198'), +('Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765'), +('Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654'), +('Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387'), +('Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683'), +('David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435'), +('Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487'); + +-- Insert Pets +INSERT INTO pets (name, birth_date, type_id, owner_id) VALUES +('Leo', '2010-09-07', 1, 1), +('Basil', '2012-08-06', 6, 2), +('Rosy', '2011-04-17', 2, 3), +('Jewel', '2010-03-07', 2, 3), +('Iggy', '2010-11-30', 3, 4), +('George', '2010-01-20', 4, 5), +('Samantha', '2012-09-04', 1, 6), +('Max', '2012-09-04', 1, 6), +('Lucky', '2011-08-06', 5, 7), +('Mulligan', '2007-02-24', 2, 8), +('Freddy', '2010-03-09', 5, 9), +('Lucky', '2010-06-24', 2, 10), +('Sly', '2012-06-08', 1, 10); + +-- Insert Visits +INSERT INTO visits (pet_id, visit_date, description) VALUES +(7, '2013-01-01', 'rabies shot'), +(8, '2013-01-02', 'rabies shot'), +(8, '2013-01-03', 'neutered'), +(7, '2013-01-04', 'spayed'); + +-- Insert Admin User +INSERT INTO users (username, password, enabled) VALUES +('admin', 'admin', TRUE); + +-- Assign Roles to Admin +INSERT INTO roles (username, role) VALUES +('admin', 'ROLE_OWNER_ADMIN'), +('admin', 'ROLE_VET_ADMIN'), +('admin', 'ROLE_ADMIN'); \ No newline at end of file diff --git a/src/main/resources/db/h2/schema.sql b/src/main/resources/db/h2/schema.sql new file mode 100644 index 0000000000000000000000000000000000000000..db1adf51acbeea6630e1695a8b67ffcd427bf4c3 --- /dev/null +++ b/src/main/resources/db/h2/schema.sql @@ -0,0 +1,74 @@ +CREATE TABLE IF NOT EXISTS vets ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + first_name VARCHAR(30) NOT NULL, + last_name VARCHAR(30) NOT NULL +); + +CREATE INDEX idx_vets_last_name ON vets(last_name); + +CREATE TABLE IF NOT EXISTS specialties ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name VARCHAR(80) NOT NULL +); + +CREATE INDEX idx_specialties_name ON specialties(name); + +CREATE TABLE IF NOT EXISTS vet_specialties ( + vet_id INTEGER NOT NULL, + specialty_id INTEGER NOT NULL, + FOREIGN KEY (vet_id) REFERENCES vets(id) ON DELETE CASCADE, + FOREIGN KEY (specialty_id) REFERENCES specialties(id) ON DELETE CASCADE, + UNIQUE (vet_id, specialty_id) +); + +CREATE TABLE IF NOT EXISTS types ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name VARCHAR(80) NOT NULL +); + +CREATE INDEX idx_types_name ON types(name); + +CREATE TABLE IF NOT EXISTS owners ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + first_name VARCHAR(30) NOT NULL, + last_name VARCHAR(30) NOT NULL, + address VARCHAR(255) NOT NULL, + city VARCHAR(80) NOT NULL, + telephone VARCHAR(20) NOT NULL +); + +CREATE INDEX idx_owners_last_name ON owners(last_name); + +CREATE TABLE IF NOT EXISTS pets ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name VARCHAR(30) NOT NULL, + birth_date DATE NOT NULL, + type_id INTEGER NOT NULL, + owner_id INTEGER NOT NULL, + FOREIGN KEY (owner_id) REFERENCES owners(id) ON DELETE CASCADE, + FOREIGN KEY (type_id) REFERENCES types(id) ON DELETE CASCADE +); + +CREATE INDEX idx_pets_name ON pets(name); + +CREATE TABLE IF NOT EXISTS visits ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + pet_id INTEGER NOT NULL, + visit_date DATE NOT NULL, + description VARCHAR(255) NOT NULL, + FOREIGN KEY (pet_id) REFERENCES pets(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS users ( + username VARCHAR(20) NOT NULL PRIMARY KEY, + password VARCHAR(255) NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT TRUE +); + +CREATE TABLE IF NOT EXISTS roles ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + username VARCHAR(20) NOT NULL, + role VARCHAR(20) NOT NULL, + UNIQUE (role, username), + FOREIGN KEY (username) REFERENCES users(username) ON DELETE CASCADE +); \ No newline at end of file