Creating Spring Boot MVC application with AWS DynamoDB in 10 mins

Building a Spring Boot MVC Web App with DynamoDB for a Game Leaderboard

In this article, we’ll walk through the process of building a Spring Boot MVC web application that interacts with an Amazon DynamoDB table to display a game leaderboard. The application will allow users to view top scores for specific games, retrieve scores for individual users, and display all scores for a particular user. We’ll also explore how to use the AWS SDK for Java to interact with DynamoDB.

Prerequisites

Before diving into the Spring Boot project, ensure the following prerequisites are met:

  1. DynamoDB Table Setup: Create a DynamoDB table named leaderboard with the following schema:
    • Partition Key: user_id
    • Sort Key: sk
    • Global Secondary Index (GSI): sk-top_score_index with sk as the partition key and top_score as the sort key.
  2. AWS CLI Configuration: Configure the AWS CLI to connect to your AWS account. This setup is necessary for the application to interact with DynamoDB.
  3. Spring Boot Project Setup: Use Spring Initializr to generate a Spring Boot project with the following dependencies:
    • Spring Web
    • Thymeleaf
    • AWS SDK for DynamoDB (Enhanced Client)

Project Structure

The project follows the MVC (Model-View-Controller) architecture:

  • Model: Leaderboard.java (Plain Java class representing the DynamoDB table structure)
  • View: main.html (Thymeleaf template for displaying the leaderboard)
  • Controller: MyController.java (Handles HTTP requests and interacts with DynamoDB)

Code Walkthrough

1. pom.xml

The pom.xml file includes the necessary dependencies for the project:

<dependencies>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>dynamodb-enhanced</artifactId>
</dependency>
</dependencies>

2. LeaderboardWebApplication.java

This is the entry point of the Spring Boot application. Here, we initialize the DynamoDB client:

@SpringBootApplication
public class LeaderboardWebApplication {

public static void main(String[] args) {
    SpringApplication.run(LeaderboardWebApplication.class, args);
}

@Bean
public DynamoDbClient dynamoDbClient() {
    return DynamoDbClient.builder()
            .region(Region.US_EAST_1) // Specify your AWS region
            .build();
}
}

3. MyController.java

The controller handles three main endpoints:

  1. / (Root Path): Displays the leaderboard for a specific game by querying the GSI.
  2. /getone: Retrieves the top score for a specific user and game.
  3. /playerscore: Retrieves all scores for a specific user.
@Controller
public class MyController {

@Autowired
private DynamoDbClient dynamoDbClient;

@GetMapping("/")
public String getLeaderboard(@RequestParam String game, Model model) {
    Map<String, AttributeValue> attributeValues = new HashMap<>();
    attributeValues.put(":v1", AttributeValue.builder().s(game).build());

    QueryRequest queryRequest = QueryRequest.builder()
            .tableName("leaderboard")
            .indexName("sk-top_score_index")
            .keyConditionExpression("sk = :v1")
            .expressionAttributeValues(attributeValues)
            .scanIndexForward(false) // Descending order
            .build();

    QueryResponse response = dynamoDbClient.query(queryRequest);
    List<Leaderboard> leaderboardList = response.items().stream()
            .map(this::convertToLeaderboard)
            .collect(Collectors.toList());

    model.addAttribute("leaderboardList", leaderboardList);
    return "main";
}

@GetMapping("/getone")
public String getOne(@RequestParam String user_id, @RequestParam String game, Model model) {
    Map<String, AttributeValue> keyMap = new HashMap<>();
    keyMap.put("user_id", AttributeValue.builder().s(user_id).build());
    keyMap.put("sk", AttributeValue.builder().s(game).build());

    GetItemRequest getItemRequest = GetItemRequest.builder()
            .tableName("leaderboard")
            .key(keyMap)
            .build();

    GetItemResponse response = dynamoDbClient.getItem(getItemRequest);
    Leaderboard leaderboard = convertToLeaderboard(response.item());

    model.addAttribute("leaderboard", leaderboard);
    return "main";
}

@GetMapping("/playerscore")
public String getPlayerScore(@RequestParam String user_id, Model model) {
    Map<String, AttributeValue> attributeValues = new HashMap<>();
    attributeValues.put(":v1", AttributeValue.builder().s(user_id).build());

    QueryRequest queryRequest = QueryRequest.builder()
            .tableName("leaderboard")
            .keyConditionExpression("user_id = :v1")
            .expressionAttributeValues(attributeValues)
            .build();

    QueryResponse response = dynamoDbClient.query(queryRequest);
    List<Leaderboard> leaderboardList = response.items().stream()
            .map(this::convertToLeaderboard)
            .collect(Collectors.toList());

    model.addAttribute("leaderboardList", leaderboardList);
    return "main";
}

private Leaderboard convertToLeaderboard(Map<String, AttributeValue> item) {
    Leaderboard leaderboard = new Leaderboard();
    leaderboard.setUser_id(item.get("user_id").s());
    leaderboard.setSk(item.get("sk").s());
    leaderboard.setTop_score(Integer.parseInt(item.get("top_score").n()));
    return leaderboard;
}
}

4. main.html

The Thymeleaf template displays the leaderboard data:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Leaderboard</title>
</head>
<body>
<table th:if="${leaderboardList}">
    <tr th:each="leaderboard : ${leaderboardList}">
        <td th:text="${leaderboard.user_id}"></td>
        <td th:text="${leaderboard.sk}"></td>
        <td th:text="${leaderboard.top_score}"></td>
    </tr>
</table>
<div th:if="${leaderboard}">
    <p>User ID: <span th:text="${leaderboard.user_id}"></span></p>
    <p>Game: <span th:text="${leaderboard.sk}"></span></p>
    <p>Top Score: <span th:text="${leaderboard.top_score}"></span></p>
</div>
</body>
</html>

5. Leaderboard.java

The model class represents the structure of the DynamoDB table:

public class Leaderboard {
private String user_id;
private String sk;
private int top_score;

// Getters and Setters
}

Running the Application

  1. Start the Spring Boot application using the command:
    mvn spring-boot:run

Conclusion

In this article, we’ve built a Spring Boot MVC web application that interacts with DynamoDB to display a game leaderboard. We’ve covered how to set up the DynamoDB table, configure the Spring Boot project, and implement the MVC components. The application demonstrates how to query DynamoDB using the AWS SDK for Java and display the results using Thymeleaf.

You can find the complete source code on GitHub. Happy coding!



Video explain the table design:
https://youtu.be/V0GtrBfY7XM

Prerequisite: Install the AWS CLI:
https://youtu.be/pE-Q_4YXlR0

Video explain the how to create the table:
https://youtu.be/sBZIVLlmpxY

Popular posts from this blog

Sample Apps: Spring data MongoDB and JSF Integration tutorial (PART 1)

Customizing Spring Data JPA Repository

Adding Hibernate Entity Level Filtering feature to Spring Data JPA Repository