In Vorheriger Artikel haben wir mit SpringBoot + JUnit5 eine Testklasse für die Controller-Ebene implementiert. Dieses Mal möchte ich die Implementierung der Service-Schicht und der Testklasse zusammenfassen, die DB-Operationen ausführen.
Da die Vorbereitung der zu testenden App jedoch etwas umfangreich ist, habe ich beschlossen, den Artikel in den ersten und den zweiten Teil zu unterteilen. Ich werde es mit der folgenden Konfiguration zusammenfassen.
MyBatis
[^ mybatis]H2
[^ h2]DBUnit
[^ dbunit]** ・ ・ ・ Der erste Teil befasst sich nicht mit der Erstellung der wesentlichen Testklasse (Schweiß **)
[^ mybatis]: Sogenannter O / R-Mapper. Die Zuordnung zwischen Java und SQL ist einfach und leicht zu verstehen, und dynamisches SQL kann geschrieben werden. Es ist sehr praktisch, mit MyBatisGenerator Mapper, Model usw. sanft aus TBL erstellen zu können. (Wird in einem anderen Artikel vorgestellt)
[^ h2]: Java-Datenbanksoftware. Lightweight, JDBC verfügbar und kompatibel mit wichtigen DBs. Es ist seit langem beliebt für Lightweight-Apps, Demos und Tests. Ich werde es auch dieses Mal zu Testzwecken verwenden.
[^ dbunit]: Ein Unit-Test-Tool für Klassen, die Operationen an DBs ausführen, z. B. DAO und Repository. Es verfügt über alle Funktionen, die für Tests mit DB erforderlich sind, z. B. die Eingabe von Daten vor dem Test, die Überprüfung nach der Überprüfung und das Rollback nach dem Test.
OS : macOS Catalina IDE : IntelliJ Ultimate Java : 11 Gradle : 6.6.1 SpringBoot : 2.3.4 MySQL : 8.0.17
Bereiten Sie zunächst die DB vor, die von der Anwendung bedient werden soll. Unter der Annahme, dass der Hauptteil "MySQL" verwendet, verwenden wir "H2" für JUnit-Tests.
Durch das Trennen der Datenbank müssen Sie sich keine Sorgen machen, dass während des Tests versehentlich Daten gelöscht oder zerstört werden. Die integrierte Datenbank H2 ist praktisch, da Sie sich nicht um externe Verbindungen kümmern müssen und sie schnell startet. (Besonders perfekt zum Testen in einer CI / CD-Kostümumgebung)
Im ersten Teil werden wir eine lokale Umgebung mit MySQL vorbereiten.
Die Verwendung von MySQL mit Docker ist einfach und unkompliziert. (Runder Wurf)
Nachdem Sie oben eine Verbindung zu MySQL als "Root" -Benutzer hergestellt haben, erstellen Sie "Datenbank" und "Benutzer" für diese Anwendung.
--Datenbank-Demo_Erstellen Sie db
CREATE DATABASE demo_db;
--Benutzername: Demo, Passwort:Demo, Autorität:demo_Alle Berechtigungen für db
CREATE USER 'demo'@'%' IDENTIFIED BY 'demo';
GRANT ALL PRIVILEGES ON demo_db.* TO 'demo'@'%';
FLUSH PRIVILEGES;
MySQL ist jetzt fertig.
Das Spring Boot-Projekt basiert auf dem vorherigen (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 Fügen Sie MyBatis- und JDBC-Bibliotheken (MySQL) zu% 83% A7% E3% 83% B3% E3% 81% AE% E6% BA% 96% E5% 82% 99 hinzu.
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' //hinzufügen
compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.22' //hinzufügen
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'
}
Schreiben Sie die Verbindungseinstellungen in "MySQL" in "main / resources / application.yml" für die lokale Umgebung der App.
Die Verbindungseinstellungen für den zuvor erstellten user: demo
und Database: demo_db
lauten wie folgt.
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
ordnet den Namen des Schlangenfalls dem Kamelfall zu, wenn das DB-Objekt und das Java-Objekt zugeordnet werden.
Damit sind die Verbindungseinstellungen für "MySQL" abgeschlossen.
Erstellen Sie eine einfache Beispieltabelle.
Spaltenname | Schimmel | Einschränkungen usw. |
---|---|---|
id | int | Primärschlüssel für die automatische Nummerierung |
name | varchar(100) | keine Nullbedingung |
age | int | keine Nullbedingung |
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 Außerdem werde ich auch die ersten Eingabedaten machen.
data.sql
insert into
customer (name, age, address)
VALUES
('Luke Skywalker', 19, 'Tatween'),
('Leia Organa', 19, 'Orderan'),
('Han Solo', 32, 'Correlia'),
('Darth Vader', 41, 'Tatween');
Führen Sie die obige DDL und DML mit MySQL
aus und schreiben Sie sie fest, um die Tabelle und die Anfangsdaten einzurichten.
Speichern Sie außerdem die erstellte DDL als "schema.sql" und die DML als "data.sql" in "test / resources". Diese Dateien werden im zweiten Teil des JUnit-Tests verwendet.
├── build.gradle
└── src
├── main
└── test
├── java
└── resources
├── application.yml
├── schema.sql // DDL
└── data.sql // DML
Erstellen Sie eine Repository-Klasse, um DB-Vorgänge auszuführen. Dieses Mal wird MyBatis für den O / R-Mapper verwendet. Befolgen Sie daher die Regeln
--Erstellen einer Modellklasse (Entität), die dem Ergebnis der Select-Klausel zugeordnet ist --Erstellen eines Mappers, der Model und CRUD SQL abbildet --Erstellung der Repository-Klasse mit Mapper
Ich werde es in der Reihenfolge von machen.
Erstellen Sie eine Modellklasse, die die Daten für eine Zeile der zuvor erstellten "Kunden" -Tabelle enthält.
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;
}
Es ist einfach, da der Accessor und toString () dank @ lombok.Data
automatisch erstellt werden.
Erstellen Sie einen Mapper, der die zuvor erstellte Klasse "Customer" mit dem SQL von CRUD verbindet. Mapper wird aus zwei Java-Schnittstellen und einer XML-Datei erstellt, die SQL beschreiben.
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
gibt den Namen an, der auf das Argument in der unten beschriebenen XML-SQL-Anweisung verweist.
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>
--Erstellen Sie dieselbe Verzeichnishierarchie wie die Java-Pakethierarchie unter "main / resources" und erstellen Sie eine "(Mapper-Name) .xml" -Datei.
<mapper namespace =" ... ">
an.<select resultType =" ... ">
den FQCN der Modellklasse an, die die Suchergebnisse abbildet.
--Wenn Sie sich bei <Einfügen>
registrieren Wenn Sie den automatisch nummerierten Wert auf die Eigenschaft des ursprünglichen Modells setzen möchten, geben Sie eine Kombination aususeGeneratedKeys = "true"
und keyProperty =" id "
an.AppConfig.java
Geben Sie abschließend das Paket an, zu dem die Mapper-Schnittstelle mit der Annotation der Konfigurationsklasse gehört.
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") //Geben Sie das Paket an, zu dem Mapper gehört
public class AppConfig {
// ...Kürzung
}
Damit ist die Erstellung von "CustomerMapper" abgeschlossen.
Die Verzeichnisstruktur nach dem Erstellen von Mapper ist wie folgt.
├── 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
Verwenden Sie den einfachen DI Mapper
aus 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);
}
}
Es mag wie eine bedeutungslose Klasse erscheinen, nur um "Mapper" zu verpacken. Dies ist notwendig, um "Service" ** unabhängig von Infrastruktur und Middleware ** zu machen.
Wenn Sie beispielsweise "Mapper" direkt von "Service" aus verwenden, ohne "Repository" zu durchlaufen, können Sie die MyBatis-spezifische Verarbeitung (z. B. Abfragekonstruktion mit Beispiel) in der Logik von "Service" verwenden, oder wenn Sie nicht gut in DB sind, verwenden Sie MySQL. Es besteht die Möglichkeit, dass eine Verarbeitung implementiert wird, die sich etwas bewusst ist.
"Service" sollte sich auf die Verwaltung von "Repository" -Transaktionen konzentrieren und keine infrastruktur- oder Middleware-abhängige Verarbeitung implementieren. Erstellen Sie ein Repository, um solche Teile auszublenden.
Nachdem ich es bisher gemacht habe, möchte ich den Vorgang überprüfen, damit ich es schnell mit Repository-> Service-> Controller
verbinden kann.
Lassen Sie es uns zunächst nur in der Methode "findAll" implementieren, um die Anzeige der Anfangsdaten der Datenbank zu überprüfen.
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();
}
}
Starten Sie die Anwendung nach dem bisherigen Erstellen mit bootRun
und versuchen Sie, über den Browser auf http: // localhost: 8080 / customers
zuzugreifen.
Die sicher eingegebenen Daten werden angezeigt!
Im ersten Teil habe ich die Einstellung von "MySQL" eingeführt, die Implementierung von "Mapper" von MyBatis und "Repository", das es verwendet. Im zweiten Teil werden wir die Implementierung des Unit-Tests mit DBUnit (und JUnit5) vorstellen.
Recommended Posts