Difference between revisions of "Java: Spring Boot"

From RHS Wiki
Jump to navigation Jump to search
Tag: visualeditor
m
Tag: visualeditor-switched
 
(17 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 85: Line 86:
  
 
You can enable more endpoints of actuator at application.properties:<syntaxhighlight lang="java">
 
You can enable more endpoints of actuator at application.properties:<syntaxhighlight lang="java">
management.endpoints.web.exposure.include=*
+
# anagement.endpoints.web.exposure.include=*
 +
# anagement.endpoints.web.exposure.include=health,metrics
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
==Dev Tools==
 +
Add at pom.xml<syntaxhighlight lang="xml">
 +
....
 +
    <dependencies>
 +
        ....
 +
        <dependency>
 +
            <groupId>org.springframework.boot</groupId>
 +
            <artifactId>spring-boot-starter-devtools</artifactId>
 +
            <scope>runtime</scope>
 +
        </dependency>
 +
        ....
 +
    </dependencies>
 +
....
 +
</syntaxhighlight><scope>runtime</scope> --> runs only at development
 +
 +
Provides:
 +
 +
*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)