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.
MySQL
MyBatis
[^ mybatis]H2
[^ h2]DBUnit
[^ dbunit]** ・ ・ ・ 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.
OS : macOS Catalina IDE : IntelliJ Ultimate Java : 11 Gradle : 6.6.1 SpringBoot : 2.3.4 MySQL : 8.0.17
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
.
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.
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'
}
É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
.
Créez un exemple de table simple.
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
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.
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
.
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>
--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.
<insert>
Si vous souhaitez définir la valeur numérotée automatiquement sur la propriété du modèle d'origine, spécifiez une combinaison deuseGeneratedKeys = "true"
et keyProperty =" id "
.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
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.
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.
Les données saisies en toute sécurité s'affichent!
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