ECE366 - Lesson 5

Spring Boot and JPA

Instructor: Professor Hong

Review & Questions

## Spring Boot
## What is the Spring Framework? - Framework for providing comprehensive infrastructural support for developing Java Apps - OOP (Object Oriented Programming) Best practices built in - DRY (Don't Repeat Yourself) Principles
## What is Spring Boot? - A tool that supports rapid development of web APIs - Auto-configuration of Application Context - Automatic Servlet Mappings - Database support - Automatic Controller Mappings
## Spring Initializr - start.spring.io - Project: Maven - Language: Java - Spring Boot: 4.0.3 - Group: com.ece366 - Artifact: rps - Java 25 - Add spring web dependency - Download the zip file and put it in your workspace
## Copy over JDBC libraries - util - DataAccessObject, DataTransferObject - DatabaseConnectionManager - Player - PlayerDAO
## Main ``` package com.ece366.rps; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.sql.Connection; import java.sql.SQLException; @SpringBootApplication @RestController public class RpsApplication { public static void main(String[] args) { System.out.println("Hello Spring Boot"); SpringApplication.run(RpsApplication.class, args); } // Sample hello world API @GetMapping("/helloClass") public String helloClass() { System.out.println("HELLO"); return "Hello Class"; } @GetMapping("/getPlayerById/{id}") public Player create(@PathVariable("id") String id) { System.out.println(id); DatabaseConnectionManager dcm = new DatabaseConnectionManager("localhost", "rps", "postgres", "password"); Player player = new Player(); try { Connection connection = dcm.getConnection(); PlayerDAO playerDAO = new PlayerDAO(connection); player = playerDAO.findById(id); System.out.println(player); } catch(SQLException e) { e.printStackTrace(); } return player; } } ``` - Note we added ```@RestController``` and ```@GetMapping``` - ```GetMapping``` specifies what the API url maps to
## Testing with Postman - Create a post request with the following url: ```http://localhost:8080/getPlayerById/1``` - Add a Body with the message desired: ```issac``` - Send the request - You can also run this on chrome
## PostMapping ``` import org.springframework.web.server.ResponseStatusException; import org.springframework.http.HttpStatus; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @PostMapping("/createNewPlayer") public Player createNewPlayer(@RequestBody String json) { System.out.println(json); ObjectMapper objectMapper = new ObjectMapper(); Player inputPlayer; try { inputPlayer = objectMapper.readValue(json, Player.class); } catch (JsonProcessingException e) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid JSON payload", e); } DatabaseConnectionManager dcm = new DatabaseConnectionManager("localhost", "rps", "postgres", "password"); Player player = new Player(); try { Connection connection = dcm.getConnection(); PlayerDAO playerDAO = new PlayerDAO(connection); player.setUserName(inputPlayer.getUserName()); player.setPassword(inputPlayer.getPassword()); player = playerDAO.create(player); System.out.println(player); } catch(SQLException e) { e.printStackTrace(); } return player; } ``` - We use the jackson library to parse the json
## Docker Compose ``` services: db: image: postgres volumes: - $HOME/srv/postgres:/var/lib/postgresql environment: - POSTGRES_DB=postgres - POSTGRES_PASSWORD=password expose: - "5432" ports: - "5432:5432" restart: always app: build: . environment: - POSTGRES_DB=postgres - POSTGRES_PASSWORD=password expose: - "8080" ports: - "8080:8080" depends_on: - db ```
## Dockerfile for Spring Boot ``` FROM maven:3.9.6-eclipse-temurin-21 AS build ADD . /project WORKDIR /project RUN mvn -e package FROM eclipse-temurin:latest COPY --from=build /project/target/rps-0.0.1-SNAPSHOT.jar /app/rps.jar ENTRYPOINT java -jar /app/rps.jar ```
## Common Traps - Make sure you don't have another spring boot application running on the same port - Rebuild your docker compose if you edited docker-compose.yaml or any Dockerfiles - Make sure you use your docker compose services name for your database
## JPA
## Java Persistence API (JPA) - Standard Java EE (Jakarta EE) specification for ORM (Object–Relational Mapping) - Allows you to map between objects and database tables - Streamlines persistence to standard format - Reduces JDBC code - Focus on OOP
## Spring Initializr - start.spring.io - Project: Maven - Language: Java - Spring Boot: 4.0.3 - Group: com.ece366 - Artifact: rps - Java 25 - Add spring web & spring data jpa dependencies - Download the zip file and put it in your workspace
## resources/application.properties ``` spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=update spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST:db}:5432/${POSTGRES_DB:rps} spring.datasource.username=${POSTGRES_USER:postgres} spring.datasource.password=${POSTGRES_PASSWORD:password} ```
## Hard coded ``` spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=update spring.datasource.url=jdbc:postgresql://localhost:5432/rps spring.datasource.username=postgres spring.datasource.password=password ```
## pom.xml Note that jpa is in the pom file and add postgres dependency ``` org.springframework.boot spring-boot-starter-data-jpa org.postgresql postgresql runtime ```
## Important Annotations - ```@RestController``` - enables API endpoints - ```@Entity``` - a row from the database - ```@Table``` - the table name - ```@Id``` - the ID of a table row - ```@Column``` - a column of the table - ```@GeneratedValue``` - strategies for primary key
## Code Organization for JPA
## Player Class data/Player.java ``` package com.ece366.rpsjpa.data; import jakarta.persistence.*; @Entity @Table(name="PLAYER") public class Player { @Id @Column(name="USER_NAME") private String userName; @Column(name="PASSWORD") private String password; @Column(name="TOTAL_WINS") private int totalWins; @Column(name="TOTAL_LOSSES") private int totalLosses; 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 int getTotalWins() { return totalWins; } public void setTotalWins(int totalWins) { this.totalWins = totalWins; } public int getTotalLosses() { return totalLosses; } public void setTotalLosses(int totalLosses) { this.totalLosses = totalLosses; } @Override public String toString() { return "User{" + "userName='" + userName + '\'' + ", password='" + password + '\'' + ", totalWin=" + totalWins + ", totalLoss=" + totalLosses + '}'; } } ```
## Dockerfile ``` FROM maven:3-eclipse-temurin-25 AS build ADD . /project WORKDIR /project RUN mvn -e -Dmaven.test.skip package FROM eclipse-temurin:25-jre COPY --from=build /project/target/rpsjpa-0.0.1-SNAPSHOT.jar /app/rps.jar ENTRYPOINT java -jar /app/rps.jar ``` - Note, the ```-Dmaven.test.skip``` skips tests during the build
## Docker Compose ``` services: db: image: postgres volumes: - $HOME/srv/postgres:/var/lib/postgresql environment: - POSTGRES_DB=postgres - POSTGRES_PASSWORD=password expose: - "5432" ports: - "5432:5432" restart: always app: build: . environment: - POSTGRES_HOST=db - POSTGRES_DB=rps - POSTGRES_USER=postgres - POSTGRES_PASSWORD=password expose: - "8080" ports: - "8080:8080" depends_on: - db ``` - Don't forget to update the applications.properties file!