[JAVA] J'ai écrit un test CRUD avec SpringBoot + MyBatis + DBUnit (Partie 1)

introduction

Dans Article précédent, nous avons implémenté une classe de test pour la couche Controller avec SpringBoot + JUnit5. Cette fois, je voudrais résumer l'implémentation de la couche Service et de la classe de test qui effectuent des opérations DB.

Cependant, comme il y a un peu de volume dans la préparation de l'application à tester, j'ai décidé de diviser l'article en première partie et deuxième partie. Je vais le résumer avec la configuration suivante.

** ・ ・ ・ La première partie ne va pas à la création de la classe de test essentielle (sueur **

[^ mybatis]: soi-disant mappeur O / R. Le mappage entre Java et SQL est simple et facile à comprendre, et du SQL dynamique peut être écrit. Il est très pratique de pouvoir créer doucement Mapper, Model, etc. à partir de TBL avec MyBatisGenerator. (À introduire dans un autre article)

[^ h2]: logiciel de base de données Java. Léger, disponible en JDBC et compatible avec les principales bases de données, il est depuis longtemps populaire pour les applications légères, les démos et les tests. Je vais également l'utiliser à des fins de test cette fois-ci.

[^ dbunit]: Un outil de test unitaire pour les classes qui effectuent des opérations sur DB telles que DAO et Repository. Il possède toutes les fonctions nécessaires pour les tests impliquant DB, telles que la saisie de données de pré-test, la post-vérification et l'annulation de post-test.

Environnement de développement

OS : macOS Catalina IDE : IntelliJ Ultimate Java : 11 Gradle : 6.6.1 SpringBoot : 2.3.4 MySQL : 8.0.17

1. Préparation de la base de données

Tout d'abord, préparez la base de données à exploiter par l'application. En supposant que le principal utilise MySQL, nous utiliserons H2 pour les tests JUnit.

En séparant la base de données, vous n'avez pas à vous soucier d'effacer ou de détruire accidentellement des données pendant le test, et le DB H2 intégré est pratique car vous n'avez pas à vous soucier des connexions externes et il démarre rapidement. (Particulièrement parfait pour les tests dans un environnement déguisé CI / CD)

Dans la première partie, nous préparerons un environnement local en utilisant MySQL.

1-1. Préparation de MySQL

Utiliser MySQL avec Docker est simple et facile. (Lancer rond)

Après vous être connecté à MySQL en tant qu'utilisateur root ci-dessus, créez une base de données et un utilisateur pour cette application.

--Démo de base de données_Créer une base de données
CREATE DATABASE demo_db;
--Nom d'utilisateur: démo, mot de passe:démo, autorité:demo_Toutes les autorisations sur db
CREATE USER 'demo'@'%' IDENTIFIED BY 'demo';
GRANT ALL PRIVILEGES ON demo_db.* TO 'demo'@'%';
FLUSH PRIVILEGES;

MySQL est maintenant prêt.

1-2. Créer un projet Spring Boot

Le projet Spring Boot est basé sur la dernière fois (https://qiita.com/kilvis/items/d75461b3596bfb0f6759#1-%E3%83%86%E3%82%B9%E3%83%88%E5%AF%BE % E8% B1% A1% E3% 81% AE% E3% 82% A2% E3% 83% 97% E3% 83% AA% E3% 82% B1% E3% 83% BC% E3% 82% B7% E3 Ajoutez les bibliothèques MyBatis et JDBC (MySQL) à% 83% A7% E3% 83% B3% E3% 81% AE% E6% BA% 96% E5% 82% 99).

build.gradle


dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3' //ajouter à
    compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.22' //ajouter à
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'
}

1-3. Définition de la source de données pour l'application

Écrivez les paramètres de connexion dans MySQL dans main / resources / application.yml pour l'environnement local de l'application. Les paramètres de connexion pour user: demo et Database: demo_db créés précédemment sont les suivants.

src/main/resources/application.yml


spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo_db
    username: demo
    password: demo
mybatis:
  configuration:
    map-underscore-to-camel-case: true

map-underscore-to-camel-case: true mappera le nom du cas du serpent au cas du chameau lors du mappage de l'objet DB et de l'objet Java.

Ceci termine les paramètres de connexion pour MySQL.

1-4. Création d'un tableau

Créez un exemple de table simple.

spécifications de la table client
Nom de colonne Moule Contraintes, etc.
id int Clé primaire pour la numérotation automatique
name varchar(100) contrainte non nulle
age int contrainte non nulle
address varchar(200)

DDL

schema.sql


create table customer
(
    id int auto_increment,
    name varchar(100) not null,
    age int not null,
    address varchar(200) null,
    constraint customer_pk primary key (id)
);

DML De plus, je ferai également les données d'entrée initiales.

data.sql


insert into
    customer (name, age, address)
VALUES
    ('Luke Skywalker', 19, 'Tatween'),
    ('Leia Organa', 19, 'Orderan'),
    ('Han Solo', 32, 'Correlia'),
    ('Dark Vador', 41, 'Tatween');

Exécutez et validez le DDL et le DML ci-dessus avec MySQL pour configurer la table et les données initiales.

En outre, enregistrez le DDL créé sous «schema.sql» et le DML sous «data.sql» dans «test / resources». Ces fichiers seront utilisés dans la deuxième partie du test JUnit.

├── build.gradle
└── src
    ├── main
    └── test
        ├── java
        └── resources
            ├── application.yml
            ├── schema.sql   // DDL
            └── data.sql     // DML

2. Création d'un référentiel à l'aide de MyBatis

Créez une classe Repository pour effectuer des opérations DB. Cette fois, MyBatis est utilisé pour le mappeur O / R, alors suivez les règles

--Création d'une classe Model (Entity) qui correspond au résultat de la clause Select --Création d'un mappeur qui mappe Model et CRUD SQL --Création de la classe Repository à l'aide de Mapper

Je vais le faire dans l'ordre de.

2-1. Création d'une classe Model (Entity)

Créez une classe Model qui contient les données pour une ligne de la table customer créée précédemment.

Customer.java


package com.example.dbunitdemo.domain.model;

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class Customer {
    private Long id;
    private String name;
    private Integer age;
    private String address;
}

C'est simple car l'accesseur et toString () sont créés automatiquement grâce à @ lombok.Data.

2-2. Création du mappeur

Créez un mappeur qui connecte la classe Customer créée précédemment avec le SQL de CRUD. Le mappeur est créé comme un ensemble de deux, «interface Java» et «fichier XML qui décrit SQL.

CustomerMapper.java

java:com.example.dbunitdemo.domain.mapper.CustomerMapper.java


package com.example.dbunitdemo.domain.mapper;

import com.example.dbunitdemo.domain.model.Customer;
import org.apache.ibatis.annotations.*;
import java.util.List;

@Mapper
public interface CustomerMapper {
    List<Customer> findAll();
    Customer get(@Param("id") Long id);
    int insert(@Param("customer") Customer customer);
    int update(@Param("customer") Customer customer);
    int delete(@Param("id") Long id);
}

@ Param spécifie le nom pour faire référence à l'argument dans l'instruction XML SQL décrite ci-dessous.

CustomerMappler.xml

main/resources/com/example/dbunitdemo/domain/mapper/CustomerMapper.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dbunitdemo.domain.mapper.CustomerMapper">
    <select id="findAll" resultType="com.example.dbunitdemo.domain.model.Customer">
        SELECT id, name, age, address FROM customer
    </select>
    <select id="get" resultType="com.example.dbunitdemo.domain.model.Customer">
        SELECT id, name, age, address FROM customer WHERE id = #{id}
    </select>
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO customer (name, age, address) VALUES (#{customer.name}, #{customer.age}, #{customer.address})
    </insert>
    <update id="update">
        UPDATE customer SET name = #{customer.name}, age = #{customer.age}, address = #{customer.address} WHERE id = #{customer.id}
    </update>
    <delete id="delete">
        DELETE FROM customer WHERE id = #{id}
    </delete>
</mapper>
Supplément: Remarques sur la création de XML pour Mapper

--Créez la même hiérarchie de répertoires que la hiérarchie de packages Java sous main / resources et créez un fichier (nom du mappeur) .xml. --Spécifiez le FQCN de l'interface du mappeur pour <mapper namespace =" ... ">. --Spécifiez le nom de la méthode de l'interface Mapper dans l'attribut id de <insert>, <select>, <update>, <delete>, et décrivez SQL dans la partie contenu ** Mapper Associez SQL à la méthode de. ** ** --Pour <select resultType =" ... ">, spécifiez le FQCN de la classe Model qui mappe les résultats de la recherche.

AppConfig.java

Enfin, spécifiez le package auquel appartient l'interface Mapper avec l'annotation de classe de configuration.

AppConfig.java


package com.example.dbunitdemo.config;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.example.dbunitdemo.domain.mapper") //Spécifiez le package auquel appartient Mapper
public class AppConfig {
   // ...réduction
}

Ceci termine la création de CustomerMapper.

La structure des répertoires après la création de Mapper est la suivante.

├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── dbunitdemo
│   │               ├── DbunitDemoApplication.java
│   │               ├── config
│   │               │   └── AppConfig.java
│   │               └── domain
│   │                   ├── mapper
│   │                   │   └── CustomerMapper.java
│   │                   └── model
│   │                       └── Customer.java
│   └── resources
│       ├── application.yml
│       ├── com
│       │   └── example
│       │       └── dbunitdemo
│       │           └── domain
│       │               └── mapper
│       │                   └── CustomerMapper.xml

2-3. Création d'une classe Repository à l'aide de Mapper

Utilisez le simple DI Mapper de Repository.

CustomerRepository.java


package com.example.dbunitdemo.domain.repository;

import com.example.dbunitdemo.domain.mapper.CustomerMapper;
import com.example.dbunitdemo.domain.model.Customer;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@RequiredArgsConstructor
public class CustomerRepository {

  private final CustomerMapper customerMapper;

  public List<Customer> findAll() {
    return customerMapper.findAll();
  }

  public Customer get(Long id) {
    return customerMapper.get(id);
  }

  public int create(Customer customer) {
    return customerMapper.insert(customer);
  }

  public int update(Customer customer) {
    return customerMapper.update(customer);
  }

  public int delete(Long id) {
    return customerMapper.delete(id);
  }
}

Cela peut sembler une classe insignifiante juste pour envelopper Mapper, Ceci est nécessaire pour rendre le Service ** indépendant de l'infrastructure et du middleware **.

Par exemple, si vous utilisez Mapper directement depuis Service sans passer par Repository, vous pouvez utiliser le traitement spécifique à MyBatis (par exemple, la construction de requêtes à l'aide de l'exemple) dans la logique de Service, ou si vous n'êtes pas bon en DB, utilisez MySQL. Il est possible qu'un traitement conscient de quelque chose soit mis en œuvre.

«Service» doit se concentrer sur la gestion des transactions du «référentiel» et ne doit pas implémenter de traitement dépendant de l'infrastructure ou du middleware. Créez un Repository pour masquer ces parties.

3. Vérifiez le fonctionnement

Après l'avoir fait jusqu'à présent, je veux vérifier l'opération, donc je vais le connecter rapidement à Repository-> Service-> Controller. Pour l'instant, implémentons-le uniquement dans la méthode findAll pour vérifier l'affichage des données initiales de la base de données.

CustomerService.java


@Service
@RequiredArgsConstructor
public class CustomerService {
  private final CustomerRepository customerRepository;

  @Transactional(readOnly = true)
  public List<Customer> findAll() {
    return customerRepository.findAll();
  }
}

CustomerController.java



@RequiredArgsConstructor
@RestController
public class CustomerController {
    private final CustomerService customerService;

    @GetMapping("customers")
    public List<Customer> findAll() {
        return customerService.findAll();
    }
}

Après avoir créé jusqu'à présent, démarrez l'application avec bootRun et essayez d'accéder à http: // localhost: 8080 / customers depuis le navigateur. image.png

Les données saisies en toute sécurité s'affichent!

Suite à la deuxième partie ...

Dans la première partie, j'ai présenté le paramétrage de MySQL, l'implémentation de Mapper de MyBatis et de Repository qui l'utilise. Dans la deuxième partie, nous présenterons l'implémentation du test unitaire à l'aide de DBUnit (et JUnit5).

Recommended Posts

J'ai écrit un test CRUD avec SpringBoot + MyBatis + DBUnit (Partie 1)
J'ai écrit un test avec Spring Boot + JUnit 5 maintenant
Créer un formulaire de relation parent-enfant avec form_object (j'ai également écrit un test)
04. J'ai fait un frontal avec SpringBoot + Thymeleaf
J'ai écrit un programme de jugement des nombres premiers en Java
Créez un CRUD simple avec SpringBoot + JPA + Thymeleaf ③ ~ Ajouter une validation ~
Créez un CRUD simple avec SpringBoot + JPA + Thymeleaf ① ~ Hello World ~
Créez un CRUD simple avec SpringBoot + JPA + Thymeleaf ⑤ ~ Modèle commun ~
J'ai essayé OCR de traiter un fichier PDF avec Java part2
Ai-je besoin d'un test si je fais DDD dans une langue avec un type?
Créez un CRUD simple avec SpringBoot + JPA + Thymeleaf ④ ~ Personnaliser le message d'erreur ~
J'ai essayé d'utiliser Spring + Mybatis + DbUnit
Test de l'API REST à l'aide de REST Assured Part 2
J'ai créé une interface graphique avec Swing
Créez un CRUD simple avec SpringBoot + JPA + Thymeleaf ② ~ Création d'écran et de fonctions ~
01. J'ai essayé de créer un environnement avec SpringBoot + IntelliJ + MySQL (MyBatis) (Windows 10)
J'ai écrit une fonction Lambda en Java et l'ai déployée avec SAM
Je veux écrire un test unitaire!
J'ai essayé d'utiliser OnlineConverter avec SpringBoot + JODConverter
J'ai essayé de jouer un peu avec BottomNavigationView ①
J'ai fait une mort risquée avec Ruby
Extraire une partie d'une chaîne en Ruby
J'ai créé une application Janken avec kotlin
Transaction distribuée avec SpringBoot + PostgreSql + mybatis + NarayanaJTA
Désérialiser XML dans une collection avec spring-boot
J'ai trouvé MyBatis utile, alors je l'ai écrit.
J'ai créé une application Janken avec Android
[SpringBoot] Comment écrire un test de contrôleur
J'ai créé une application d'apprentissage automatique avec Dash (+ Docker) part3 ~ Practice ~
J'ai écrit un fichier Jenkins avec Declarative Pipeline (Checkstyle, Findbugs, PMD, CPD, etc.)
Comment tester une classe qui gère application.properties avec SpringBoot (requête: signalée)