Difference between revisions of "Java: Spring Boot"
Rafahsolis (talk | contribs) m (→Build) Tag: visualeditor |
Rafahsolis (talk | contribs) m Tag: visualeditor-switched |
||
| (15 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
| − | Create microservices<syntaxhighlight lang="java"> | + | ==Create microservices== |
| + | <syntaxhighlight lang="java"> | ||
import java.util.Arrays; | import java.util.Arrays; | ||
import java.util.List; | import java.util.List; | ||
| Line 89: | Line 90: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | == Dev Tools == | + | ==Dev Tools== |
Add at pom.xml<syntaxhighlight lang="xml"> | Add at pom.xml<syntaxhighlight lang="xml"> | ||
.... | .... | ||
| Line 106: | Line 107: | ||
Provides: | Provides: | ||
| − | * Develop server reloading on changes (pom.xml changes are not detected) | + | *Develop server reloading on changes (pom.xml changes are not detected) |
| + | |||
| + | <br /> | ||
| + | |||
| + | ==Unit tests== | ||
| + | |||
| + | |||
| + | ==Example: Spring Boot with Data JPA and in memory database H2== | ||
| + | |||
| + | ===/pom.xml=== | ||
| + | <syntaxhighlight lang="xml"> | ||
| + | .... | ||
| + | <dependencies> | ||
| + | .... | ||
| + | <dependency> | ||
| + | <groupId>org.springframework.boot</groupId> | ||
| + | <artifactId>spring-boot-starter-web</artifactId> | ||
| + | </dependency> | ||
| + | <dependency> | ||
| + | <groupId>org.springframework.boot</groupId> | ||
| + | <artifactId>spring-boot-starter-data-jpa</artifactId> | ||
| + | </dependency> | ||
| + | <dependency> | ||
| + | <groupId>com.h2database</groupId> | ||
| + | <artifactId>h2</artifactId> | ||
| + | <scope>runtime</scope> | ||
| + | </dependency> | ||
| + | <dependency> | ||
| + | <groupId>org.springframework.boot</groupId> | ||
| + | <artifactId>spring-boot-starter-actuator</artifactId> | ||
| + | <scope>runtime</scope> | ||
| + | </dependency> | ||
| + | <dependency> | ||
| + | <groupId>org.springframework.boot</groupId> | ||
| + | <artifactId>spring-boot-starter-devtools</artifactId> | ||
| + | <scope>runtime</scope> | ||
| + | </dependency> | ||
| + | .... | ||
| + | </dependencies> | ||
| + | .... | ||
| + | </syntaxhighlight>To run against a MySQL instead of H2 replace the h2 dependency by:<syntaxhighlight lang="xml"> | ||
| + | .... | ||
| + | <dependency> | ||
| + | <groupId>mysql</groupId> | ||
| + | <artifactId>mysql-connector-java</artifactId> | ||
| + | </dependency> | ||
| + | .... | ||
| + | </syntaxhighlight><br /> | ||
| + | ===/src/main/resources/application.properties=== | ||
| + | |||
| + | ====h2 Database==== | ||
| + | <syntaxhighlight lang="java"> | ||
| + | spring.datasource.url=jdbc:h2:mem:testdb | ||
| + | |||
| + | # If using Spring Boot >=2.5.0 the following line is also required | ||
| + | spring.jpa.defer-datasource-initialization=true | ||
| + | </syntaxhighlight>database will be accessible via http://localhost:8080/h2-console | ||
| + | |||
| + | ====Docker MySQL==== | ||
| + | <syntaxhighlight lang="java"> | ||
| + | #spring.datasource.url=jdbc:h2:mem:testdb | ||
| + | |||
| + | spring.jpa.hibernate.ddl-auto=update | ||
| + | spring.datasource.url=jdbc:mysql://localhost:3306/courses | ||
| + | spring.datasource.username=courses-user | ||
| + | spring.datasource.password=dummycourses | ||
| + | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect | ||
| + | |||
| + | #courses-user@localhost:3306 | ||
| + | </syntaxhighlight>Start MySQL Docker container<syntaxhighlight lang="bash"> | ||
| + | docker run --detach --env MYSQL_ROOT_PASSWORD=dummypassword --env MYSQL_USER=courses-user --env MYSQL_PASSWORD=dummycourses --env MYSQL_DATABASE=courses --name mysql --publish 3306:3306 mysql:5.7 | ||
| + | </syntaxhighlight>mysqlsh commands<syntaxhighlight lang="mysql"> | ||
| + | mysqlsh | ||
| + | \connect courses-user@localhost:3306 | ||
| + | \sql | ||
| + | use courses | ||
| + | select * from course; | ||
| + | \quit | ||
| + | </syntaxhighlight><syntaxhighlight lang="bash"> | ||
| + | # Stop MySQL container | ||
| + | docker container ls | ||
| + | docker container stop ID | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ===/src/main/java/com/in28minutes/learnspringboot/courses/bean/Course.java=== | ||
| + | <syntaxhighlight lang="java"> | ||
| + | package com.in28minutes.learnspringboot.courses.bean; | ||
| + | |||
| + | import javax.persistence.Entity; | ||
| + | import javax.persistence.Id; | ||
| + | import javax.persistence.GeneratedValue; | ||
| + | // import javax.persistence.Column; | ||
| + | |||
| + | // @Entity(name="db_table_name") | ||
| + | @Entity | ||
| + | public class Course { | ||
| + | @Id | ||
| + | @GeneratedValue | ||
| + | private long id; | ||
| + | |||
| + | // @Column(name="db_column_name") | ||
| + | private String name; | ||
| + | private String author; | ||
| + | |||
| + | public Course(){} // Entities must have a default constructor | ||
| + | |||
| + | public Course(long id, String name, String author){ | ||
| + | super(); | ||
| + | this.id = id; | ||
| + | this.name = name; | ||
| + | this.author = author; | ||
| + | } | ||
| + | |||
| + | public String toString() { | ||
| + | return "Course [id=" + id + ", name= " + name + ", author=" + author + " ]"; | ||
| + | } | ||
| + | |||
| + | public long getId() { | ||
| + | return id; | ||
| + | } | ||
| + | public String getName() { | ||
| + | return name; | ||
| + | } | ||
| + | public String getAuthor() { | ||
| + | return author; | ||
| + | } | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ===src/main/resources/data.sql=== | ||
| + | querys in this file will be executed at startup automatically<syntaxhighlight lang="sql"> | ||
| + | insert into COURSE(ID, AUTHOR, NAME) | ||
| + | values(100001, 'in28minutes', 'Learn Microservices'); | ||
| + | insert into COURSE(ID, AUTHOR, NAME) | ||
| + | values(100002, 'in28minutes', 'Learn FullStack with React and Angular'); | ||
| + | insert into COURSE(ID, AUTHOR, NAME) | ||
| + | values(100003, 'in28minutes', 'Learn AWS, GCP and Azure'); | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ===/src/main/java/com/in28minutes/learnspringboot/courses/repository/CourseRepository.java=== | ||
| + | <syntaxhighlight lang="java"> | ||
| + | package com.in28minutes.learnspringboot.courses.repository; | ||
| + | |||
| + | import org.springframework.data.jpa.repository.JpaRepository; | ||
| + | import com.in28minutes.learnspringboot.courses.bean.Course; | ||
| + | |||
| + | public interface CourseRepository extends JpaRepository<Course, Long> {} | ||
| + | </syntaxhighlight> | ||
| + | ---- | ||
| + | |||
| + | ===/src/main/java/com/in28minutes/learnspringboot/courses/controller/CourseController.java=== | ||
| + | <syntaxhighlight lang="java"> | ||
| + | package com.in28minutes.learnspringboot.courses.controller; | ||
| + | |||
| + | import java.util.List; | ||
| + | import java.util.Optional; | ||
| + | |||
| + | import org.springframework.beans.factory.annotation.Autowired; | ||
| + | import org.springframework.web.bind.annotation.DeleteMapping; | ||
| + | import org.springframework.web.bind.annotation.GetMapping; | ||
| + | import org.springframework.web.bind.annotation.PathVariable; | ||
| + | import org.springframework.web.bind.annotation.PostMapping; | ||
| + | import org.springframework.web.bind.annotation.PutMapping; | ||
| + | import org.springframework.web.bind.annotation.RequestBody; | ||
| + | import org.springframework.web.bind.annotation.RestController; | ||
| + | |||
| + | import com.in28minutes.learnspringboot.courses.bean.Course; | ||
| + | import com.in28minutes.learnspringboot.courses.repository.CourseRepository; | ||
| + | |||
| + | @RestController | ||
| + | public class CourseController { | ||
| + | |||
| + | @Autowired | ||
| + | private CourseRepository repository; | ||
| + | |||
| + | // http://localhost:8080/courses | ||
| + | @GetMapping("/courses") | ||
| + | public List<Course> getAllCourses() { | ||
| + | return repository.findAll(); | ||
| + | } | ||
| + | |||
| + | //// http://localhost:8080/courses/1 | ||
| + | @GetMapping("/courses/{id}") | ||
| + | public Course getCourseDetails(@PathVariable long id) { | ||
| + | |||
| + | Optional<Course> course = repository.findById(id); | ||
| + | |||
| + | if(course.isEmpty()) { | ||
| + | throw new RuntimeException("Course not found with id " + id); | ||
| + | } | ||
| + | |||
| + | return course.get(); | ||
| + | } | ||
| + | |||
| + | /* | ||
| + | POST http://localhost:8080/courses | ||
| + | { | ||
| + | "name": "Learn DevOps", | ||
| + | "author": "in28minutes" | ||
| + | }*/ | ||
| + | |||
| + | //POST - Create a new resource (/courses) | ||
| + | @PostMapping("/courses") | ||
| + | public void createCourse(@RequestBody Course course){ | ||
| + | repository.save(course); | ||
| + | } | ||
| + | |||
| + | /* | ||
| + | PUT - http://localhost:8080/courses/100001 | ||
| + | { | ||
| + | "id": 100001, | ||
| + | "name": "Learn Microservices 2", | ||
| + | "author": "in28minutes" | ||
| + | } | ||
| + | */ | ||
| + | |||
| + | //PUT - Update/Replace a resource (/courses/1) | ||
| + | @PutMapping("/courses/{id}") | ||
| + | public void updateCourse(@PathVariable long id, @RequestBody Course course){ | ||
| + | repository.save(course); | ||
| + | } | ||
| + | |||
| + | |||
| + | //DELETE - Delete a resource (/courses/1) | ||
| + | @DeleteMapping("/courses/{id}") | ||
| + | public void deleteCourse(@PathVariable long id){ | ||
| + | repository.deleteById(id); | ||
| + | } | ||
| + | |||
| + | |||
| + | // docker run --detach | ||
| + | // --env MYSQL_ROOT_PASSWORD=dummypassword | ||
| + | // --env MYSQL_USER=courses-user | ||
| + | // --env MYSQL_PASSWORD=dummycourses | ||
| + | // --env MYSQL_DATABASE=courses | ||
| + | // --name mysql | ||
| + | // --publish 3306:3306 mysql:5.7 | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ==REST API== | ||
| + | |||
| + | ===GET=== | ||
| + | Retrieve information | ||
| + | |||
| + | /courses, /courses/1 | ||
| + | |||
| + | ===POST=== | ||
| + | Create a new resource | ||
| + | |||
| + | /courses | ||
| + | |||
| + | ===PUT=== | ||
| + | Update/Replace a resource | ||
| + | |||
| + | /courses/1 | ||
| + | |||
| + | ===PATCH=== | ||
| + | Update a part of the resource | ||
| + | |||
| + | /courses/1 | ||
| + | |||
| + | ===DELETE=== | ||
| + | Delete a resource | ||
| + | |||
| + | /courses/1 | ||
==Build== | ==Build== | ||
Latest revision as of 13:40, 23 June 2022
Create microservices[edit]
import java.util.Arrays;
import java.util.List;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
public class Course {
private long id;
private String name;
private String author;
public Course(long id, String name, String author){
super();
this.id = id;
this.name = name;
this.author = author;
}
public String toString() {
return "Course [id=" + id + ", name= " + name + ", author=" + author + " ]";
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getAuthor() {
return author;
}
}
@RestController
public class CourseController {
@GetMapping("/courses")
public List<Course> getAllCourses(){
return Arrays.asList(new Course(1, "Learn microservices", "in28minutes"));
}
}
Spring Boot Starter Projects[edit]
Found at Spring Initializr Under dependencies
Web Application: Spring Boot Starter Web
REST API: Spring Boot Starter Web
Talk to database using JPA: Spring Boot Starter Data JPA
Talk to database using JDBC: Spring Boot Starter Data JDBC
Secure your web application or REST API: Spring Boot Starter Security
Spring Boot Actuator[edit]
Monitor and manage your application
- beans: Complete list of Spring beans in your app
- health: Application health information
- metrics: Application metrics
- mappings: Details around Requests Mappings
To enable Spring Boot actuator at pom.xml:
....
<dependencies>
....
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
....
</dependencies>
....
http://127.0.0.1:8080/actuator
http://127.0.0.1:8080/actuator/health
http://127.0.0.1:8080/actuator/health/{*path}
http://127.0.0.1:8080/actuator/info
You can enable more endpoints of actuator at application.properties:
# anagement.endpoints.web.exposure.include=*
# anagement.endpoints.web.exposure.include=health,metrics
Dev Tools[edit]
Add at pom.xml
....
<dependencies>
....
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-devtools</artifactId>
<scope>runtime</scope>
</dependency>
....
</dependencies>
....
<scope>runtime</scope> --> runs only at development
Provides:
- Develop server reloading on changes (pom.xml changes are not detected)
Unit tests[edit]
Example: Spring Boot with Data JPA and in memory database H2[edit]
/pom.xml[edit]
....
<dependencies>
....
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-devtools</artifactId>
<scope>runtime</scope>
</dependency>
....
</dependencies>
....
To run against a MySQL instead of H2 replace the h2 dependency by:
....
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
....
/src/main/resources/application.properties[edit]
h2 Database[edit]
spring.datasource.url=jdbc:h2:mem:testdb
# If using Spring Boot >=2.5.0 the following line is also required
spring.jpa.defer-datasource-initialization=true
database will be accessible via http://localhost:8080/h2-console
Docker MySQL[edit]
#spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/courses
spring.datasource.username=courses-user
spring.datasource.password=dummycourses
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
#courses-user@localhost:3306
Start MySQL Docker container
docker run --detach --env MYSQL_ROOT_PASSWORD=dummypassword --env MYSQL_USER=courses-user --env MYSQL_PASSWORD=dummycourses --env MYSQL_DATABASE=courses --name mysql --publish 3306:3306 mysql:5.7
mysqlsh commands
mysqlsh
\connect courses-user@localhost:3306
\sql
use courses
select * from course;
\quit
# Stop MySQL container
docker container ls
docker container stop ID
/src/main/java/com/in28minutes/learnspringboot/courses/bean/Course.java[edit]
package com.in28minutes.learnspringboot.courses.bean;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
// import javax.persistence.Column;
// @Entity(name="db_table_name")
@Entity
public class Course {
@Id
@GeneratedValue
private long id;
// @Column(name="db_column_name")
private String name;
private String author;
public Course(){} // Entities must have a default constructor
public Course(long id, String name, String author){
super();
this.id = id;
this.name = name;
this.author = author;
}
public String toString() {
return "Course [id=" + id + ", name= " + name + ", author=" + author + " ]";
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getAuthor() {
return author;
}
}
src/main/resources/data.sql[edit]
querys in this file will be executed at startup automatically
insert into COURSE(ID, AUTHOR, NAME)
values(100001, 'in28minutes', 'Learn Microservices');
insert into COURSE(ID, AUTHOR, NAME)
values(100002, 'in28minutes', 'Learn FullStack with React and Angular');
insert into COURSE(ID, AUTHOR, NAME)
values(100003, 'in28minutes', 'Learn AWS, GCP and Azure');
/src/main/java/com/in28minutes/learnspringboot/courses/repository/CourseRepository.java[edit]
package com.in28minutes.learnspringboot.courses.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.in28minutes.learnspringboot.courses.bean.Course;
public interface CourseRepository extends JpaRepository<Course, Long> {}
/src/main/java/com/in28minutes/learnspringboot/courses/controller/CourseController.java[edit]
package com.in28minutes.learnspringboot.courses.controller;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.in28minutes.learnspringboot.courses.bean.Course;
import com.in28minutes.learnspringboot.courses.repository.CourseRepository;
@RestController
public class CourseController {
@Autowired
private CourseRepository repository;
// http://localhost:8080/courses
@GetMapping("/courses")
public List<Course> getAllCourses() {
return repository.findAll();
}
//// http://localhost:8080/courses/1
@GetMapping("/courses/{id}")
public Course getCourseDetails(@PathVariable long id) {
Optional<Course> course = repository.findById(id);
if(course.isEmpty()) {
throw new RuntimeException("Course not found with id " + id);
}
return course.get();
}
/*
POST http://localhost:8080/courses
{
"name": "Learn DevOps",
"author": "in28minutes"
}*/
//POST - Create a new resource (/courses)
@PostMapping("/courses")
public void createCourse(@RequestBody Course course){
repository.save(course);
}
/*
PUT - http://localhost:8080/courses/100001
{
"id": 100001,
"name": "Learn Microservices 2",
"author": "in28minutes"
}
*/
//PUT - Update/Replace a resource (/courses/1)
@PutMapping("/courses/{id}")
public void updateCourse(@PathVariable long id, @RequestBody Course course){
repository.save(course);
}
//DELETE - Delete a resource (/courses/1)
@DeleteMapping("/courses/{id}")
public void deleteCourse(@PathVariable long id){
repository.deleteById(id);
}
// docker run --detach
// --env MYSQL_ROOT_PASSWORD=dummypassword
// --env MYSQL_USER=courses-user
// --env MYSQL_PASSWORD=dummycourses
// --env MYSQL_DATABASE=courses
// --name mysql
// --publish 3306:3306 mysql:5.7
}
REST API[edit]
GET[edit]
Retrieve information
/courses, /courses/1
POST[edit]
Create a new resource
/courses
PUT[edit]
Update/Replace a resource
/courses/1
PATCH[edit]
Update a part of the resource
/courses/1
DELETE[edit]
Delete a resource
/courses/1
Build[edit]
Make JAR not WAR (JAR contains embeded server)