Java Spring Boot (Part 3)

Neil Haddley โ€ข October 28, 2023

Using Spring Data JPA with an in-memory H2 database to store and list todo items, and deploying the application to Azure

JavaSpring Bootspring-bootspring-data-jpadatabase

I created a Spring Boot application using Spring Data JPA, which provides high-level, repository-style interfaces for data access and reduces boilerplate code.

I created a new GitHub repository haddley-spring-data-jpa

I created a new GitHub repository haddley-spring-data-jpa

I opened the repository using GitHub Desktop

I opened the repository using GitHub Desktop

I cloned the GitHub repository

I cloned the GitHub repository

GitHub repository folder

GitHub repository folder

I created a new spring boot application springdatajpa

I created a new spring boot application springdatajpa

I copied the generated files to the GitHub repository folder

I copied the generated files to the GitHub repository folder

I updated SpringdatajpaApplication.java so that it would respond to http://localhost/hello requests

I updated SpringdatajpaApplication.java so that it would respond to http://localhost/hello requests

I accessed http://localhost/hello using my web browser

I accessed http://localhost/hello using my web browser

I created a TodoItem Java class

I created a TodoItem Java class

I created a Spring Data JPA repository interface (based on the TodoItem class)

I created a Spring Data JPA repository interface (based on the TodoItem class)

I created a TodoItemDataLoader to ensure that if the repository was empty two new TodoItems would be added as the application started

I created a TodoItemDataLoader to ensure that if the repository was empty two new TodoItems would be added as the application started

I updated SpringdatajpaApplication.java to return all of the TodoItems in response to http://localhost/hello requests

I updated SpringdatajpaApplication.java to return all of the TodoItems in response to http://localhost/hello requests

Under the hood: How the seed data gets loaded

TodoItemDataLoader is a component that implements CommandLineRunner, so Spring calls its run method once, as the application starts. The method asks the repository for a count, and only when the count is zero does it save the two items, get the milk and rake the leaves. It then logs how many items there are. That is why the todos list is never empty when I open /todos.

I accessed http://localhost/todos using my web browser

I accessed http://localhost/todos using my web browser

I added text to applications.properties

I added text to applications.properties

I accessed the h2 console (using values jdbc:h2:mem:testdb, sa and password)

I accessed the h2 console (using values jdbc:h2:mem:testdb, sa and password)

I clicked on the TODO_ITEM table

I clicked on the TODO_ITEM table

I clicked Run to execute the select query

I clicked Run to execute the select query

I deployed the haddley-spring-data-jpa application to Azure

I deployed the haddley-spring-data-jpa application to Azure

I accessed the haddley-spring-data-jpa application hosted in Azure

I accessed the haddley-spring-data-jpa application hosted in Azure

JAVA
1package com.haddley.springdatajpa;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6import org.springframework.web.bind.annotation.GetMapping;
7import org.springframework.web.bind.annotation.RequestParam;
8import org.springframework.web.bind.annotation.RestController;
9
10@SpringBootApplication
11@RestController
12public class SpringdatajpaApplication {
13    public static void main(String[] args) {
14        SpringApplication.run(SpringdatajpaApplication.class, args);
15    }
16
17    @GetMapping("/hello")
18    public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
19        return String.format("Hello %s!", name);
20    }
21}

TodoItem.java

JAVA
1package com.haddley.springdatajpa.models;
2
3import java.time.Instant;
4
5import jakarta.persistence.Entity;
6import jakarta.persistence.GeneratedValue;
7import jakarta.persistence.GenerationType;
8import jakarta.persistence.Id;
9import jakarta.persistence.Table;
10import jakarta.validation.constraints.NotBlank;
11
12import lombok.Getter;
13import lombok.Setter;
14
15@Entity
16@Table(name = "todo_item")
17public class TodoItem {
18
19    @Id
20    @GeneratedValue(strategy = GenerationType.AUTO)
21    @Getter
22    @Setter
23    private Long id;
24
25    @Getter
26    @Setter
27    @NotBlank(message = "Description is required")
28    private String description;
29
30    @Getter
31    @Setter
32    private boolean complete;
33
34    @Getter
35    @Setter
36    private Instant createdDate;
37
38    @Getter
39    @Setter
40    private Instant modifiedDate;
41
42    public TodoItem() {}
43
44    public TodoItem(String description) {
45        this.description = description;
46        this.complete = false;
47        this.createdDate = Instant.now();
48        this.modifiedDate = Instant.now();
49    }
50
51    @Override
52    public String toString() {
53        return String.format("TodoItem{id=%d, description='%s', complete='%s', createdDate='%s', modifiedDate='%s'}", id, description, complete, createdDate, modifiedDate);
54    }
55
56
57}

TodoItemRepository.java

JAVA
1package com.haddley.springdatajpa.models;
2
3import com.haddley.springdatajpa.models.TodoItem;
4import org.springframework.data.repository.CrudRepository;
5
6public interface TodoItemRepository extends CrudRepository {
7}

TodoItemDataLoader.java

JAVA
1package com.haddley.springdatajpa.config;
2
3import com.haddley.springdatajpa.models.TodoItem;
4import com.haddley.springdatajpa.repositories.TodoItemRepository;
5
6import org.slf4j.Logger;
7import org.slf4j.LoggerFactory;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.boot.CommandLineRunner;
10import org.springframework.stereotype.Component;
11
12@Component
13public class TodoItemDataLoader implements CommandLineRunner {
14
15    private final Logger logger = LoggerFactory.getLogger(TodoItemDataLoader.class);
16
17    @Autowired
18    TodoItemRepository todoItemRepository;
19
20    @Override
21    public void run(String... args) throws Exception {
22        loadSeedData();
23    }
24
25    private void loadSeedData() {
26        if (todoItemRepository.count() == 0) {
27            TodoItem todoItem1 = new TodoItem("get the milk");
28            TodoItem todoItem2 = new TodoItem("rake the leaves");
29
30            todoItemRepository.save(todoItem1);
31            todoItemRepository.save(todoItem2); 
32        }
33
34        logger.info("Number of TodoItems: {}", todoItemRepository.count());
35    }
36
37}

SpringdatajpaApplication.java updated

JAVA
1package com.haddley.springdatajpa;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6import org.springframework.web.bind.annotation.GetMapping;
7import org.springframework.web.bind.annotation.RequestParam;
8import org.springframework.web.bind.annotation.RestController;
9
10import com.haddley.springdatajpa.models.TodoItem;
11import com.haddley.springdatajpa.repositories.TodoItemRepository;
12import org.springframework.beans.factory.annotation.Autowired;
13
14import java.util.stream.StreamSupport;
15import java.util.stream.Collectors;
16
17@SpringBootApplication
18@RestController
19public class SpringdatajpaApplication {
20    public static void main(String[] args) {
21        SpringApplication.run(SpringdatajpaApplication.class, args);
22    }
23
24    @GetMapping("/hello")
25    public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
26        return String.format("Hello %s!", name);
27    }
28
29    @Autowired
30    private TodoItemRepository todoItemRepository;
31
32    @GetMapping("/todos")
33    public String index() {
34
35        Iterable todos = todoItemRepository.findAll();
36
37        return StreamSupport.stream(todos.spliterator(), false)
38            .map(TodoItem::toString) 
39            .collect(Collectors.joining(", ")); 
40
41    }
42
43}

application.properties

TEXT
1spring.datasource.url=jdbc:h2:mem:testdb
2spring.datasource.driverClassName=org.h2.Driver
3spring.datasource.username=sa
4spring.datasource.password=password
5spring.jpa.database-platform=org.hibernate.dialect.H2Dialect