Spring Boot, MyBatis One-to-Many and One-to-One relationship - Annotations Example

In this section, we will learn how to handle One-to-Many and One-to-One relationship in Spring Boot, MyBatis application (Annotations example).


Technologies used:

  • Spring Boot 3.0.2
  • MyBatis 3
  • Java 17
  • Maven 
  • H2 Database

Creating a simple spring boot web application:

First, open the Spring initializr https://start.spring.io/ 

Then, Provide the Group and Artifact name. We have provided Group name com.knf.dev.demo and Artifact spring-boot-mybatis-one-to-many-one-to-one. Here I selected the Maven project - language Java 17 - Spring Boot 3.0.2 and add Spring web dependencyMyBatis Framework and H2 Database.


Then, click on the Generate button. When we click on the Generate button, it starts packing the project in a .zip(spring-boot-mybatis-one-to-many-one-to-one) file and downloads the project. Then, Extract the Zip file. 
Then, import the project on your favourite IDE.

Final Project directory:


pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>spring-boot-mybatis-one-to-many-one-to-one</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-mybatis-one-to-many-one-to-one</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

Create tables and load initial data.

schema.sql

CREATE TABLE IF NOT EXISTS users
(
email CHARACTER VARYING(30) NOT NULL,
name CHARACTER VARYING(30),
PRIMARY KEY (email)
);
CREATE TABLE IF NOT EXISTS cards
(

PRIMARY KEY (id),
id INTEGER NOT NULL,
cardnumber CHARACTER VARYING(30),
cardtype CHARACTER VARYING(50),
email CHARACTER VARYING(50),
CONSTRAINT fk_group
FOREIGN KEY(email)
REFERENCES users(email)
);

data.sql

insert into users (email, name) values ('alpha@gmail.com', 'Alpha');
insert into users (email, name) values ('beta@gmail.com', 'Beta');
insert into users (email, name) values ('gama@gmail.com', 'Gama');

insert into cards (id, cardnumber, cardtype, email)
values (1, '11111111', 'Mastercard', 'gama@gmail.com');
insert into cards (id, cardnumber, cardtype, email)
values (2, '22222222', 'Visa', 'gama@gmail.com');
insert into cards (id, cardnumber, cardtype, email)
values (3, '33333333', 'PayPal', 'gama@gmail.com');
insert into cards (id, cardnumber, cardtype, email)
values (4, '4444444', 'Visa', 'beta@gmail.com');
insert into cards (id, cardnumber, cardtype, email)
values (5, '5555555', 'PayPal', 'beta@gmail.com');
insert into cards (id, cardnumber, cardtype, email)
values (6, '6666666', 'Mastercard', 'alpha@gmail.com');
insert into cards (id, cardnumber, cardtype, email)
values (7, '7777777', 'Visa', 'alpha@gmail.com');

application.properties

logging.level.com.knf.dev.demo=TRACE

Create User Bean

package com.knf.dev.demo.model;

import java.util.List;

public class User {

private String email;
private String name;
private List<Card> cards;

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Card> getCards() {
return cards;
}

public void setCards(List<Card> cards) {
this.cards = cards;
}
}

Create Card Bean

package com.knf.dev.demo.model;

public class Card {

private Integer id;
private String cardNumber;
private String cardType;
private String email;
private User user;

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getCardNumber() {
return cardNumber;
}

public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}

public String getCardType() {
return cardType;
}

public void setCardType(String cardType) {
this.cardType = cardType;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}
}

Create User Repository

@Mapper: indicates that this is a MyBatis mapper class.
@Select: We need to provide select query as value.
@Results: A list of Result mappings that contain details of how a particular result column is mapped to a property or field.
@Result: A single result mapping between a column and a property or field.
@Many: The annotation that specify the nested statement for retrieving collections.
package com.knf.dev.demo.repository;

import com.knf.dev.demo.model.Card;
import com.knf.dev.demo.model.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface UserRepository {

/*
* one to many Select.
*/
@Select("SELECT users.name, users.email " +
"FROM users WHERE users.email = #{email}")
@Results(value = {
@Result(property = "email", column = "email"),
@Result(property="cards", javaType=List.class, column="email",
many=@Many(select="getCards"))
})
User selectUserById(String email);

@Select("SELECT cards.id, cards.cardnumber, cards.cardtype, " +
"cards.email FROM cards WHERE cards.email = #{email}")
@Results(value = {
@Result(property = "cardNumber", column = "cardnumber"),
@Result(property = "cardType", column = "cardtype")
})
List<Card> getCards(String email);
}

Create Card Repository

@One: The annotation that specify the nested statement for retrieving single object.
package com.knf.dev.demo.repository;

import com.knf.dev.demo.model.Card;
import com.knf.dev.demo.model.User;
import org.apache.ibatis.annotations.*;

@Mapper
public interface CardRepository {

/*
* one to one Select.
*/
@Select("SELECT cards.id, cards.email, " +
"cards.cardnumber, cards.cardtype" +
" FROM cards WHERE cards.id = #{id}")
@Results(value = {
@Result(property = "email", column = "email"),
@Result(property = "cardNumber", column = "cardnumber"),
@Result(property = "cardType", column = "cardtype"),
@Result(property = "user", column = "email",
one=@One(select = "getUser"))
})
Card selectCardById(Integer id);

@Select("SELECT users.name, users.email FROM " +
"users WHERE users.email = #{email}")
User getUser(String email);
}

Create User Controller

package com.knf.dev.demo.controller;

import com.knf.dev.demo.model.Card;
import com.knf.dev.demo.model.User;
import com.knf.dev.demo.repository.CardRepository;
import com.knf.dev.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/")
public class UserController {

@Autowired
UserRepository userRepository;
@Autowired
CardRepository cardRepository;

@GetMapping("users/{email}")
public User getUserById(@PathVariable String email)
{
return userRepository.selectUserById(email);
}

@GetMapping("cards/{id}")
public Card getCardById(@PathVariable Integer id)
{
return cardRepository.selectCardById(id);
}
}

Run the application - Application.java

package com.knf.dev.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

}
Application is the entry point that sets up the Spring Boot application. The @SpringBootApplication annotation enables auto-configuration and component scanning. 

Step1: Download or clone the source code from GitHub to a local machine - Click here!


Step 2: mvn clean install


Step 3: Run the Spring Boot application - mvn spring-boot:run


OR 


Run this Spring boot application from 

  • IntelliJ IDEA IDE by right click - Run 'Application.main()' 
  • Eclipse/STS - You can right click the project or the Application.java file and run as java application or Spring boot application.


Verify APIs using Postman:

1. Get User by ID (email): localhost:8080/api/v1/users/beta@gmail.com


2. Get Card by ID: localhost:8080/api/v1/cards/4

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete