[JAVA] Behandeln Sie Enum mit Mybatis TypeHandler

Überblick

In Mybatis der O / R-Mapping-Flockenarbeit wird der in Mybatis bereitgestellte automatische Typkonvertierungsmechanismus: Behandelt Enum mit TypeHandler. Wir werden auch vorstellen, wie dynamisches SQL und seine Anwendung mit Spring MVC geschrieben werden.

Gründe für die Zusammenfassung

Es gibt eine Spalte [^ 1], die "Flags" und "partitionierte Werte" behandelt, die wahrscheinlich in der Datenbank vorhanden sind, die von neu erstellten Systemen und Anwendungen verwendet wird, die bereits erstellt wurden, und der Bereich dieser Werte ist nahezu unverändert [^ 2]. ] ist. Da diese Werte häufig als Zeichenfolgentyp (CHAR oder VARCHAR) oder numerischer Typ behandelt werden, werden die Zeichenfolge und der numerische Wert zumindest in der Welt des Java-Codes nicht so behandelt, wie sie sind, und der Wertebereich ist Wenn es entschieden wurde, kann es per Aufzählung behandelt werden ...? Ist der Anfang.

Artikel Prämisse

In diesem Artikel haben wir den Vorgang mit den folgenden Versionen erstellt und bestätigt.

Satz von Quelldateien

Es wird auf GitHub veröffentlicht.

https://github.com/A-pZ/mybatis-spring-enum-sample

Implementierung der Suchfunktion

Diesmal wurde eine Funktion eingeführt, die das Ergebnis der Suche in einer Tabelle zurückgibt, die ein bestimmtes Flag oder einen bestimmten Teilungswert verarbeitet.

Was zu implementieren

Um kurz zu erklären, was implementiert werden soll

ist.

Tabelle zur Referenz

Dies ist die Definition der Tabelle, auf die dieses Mal verwiesen werden soll [^ 3].

Produkttabelle (Tabellenname: Artikel)

Spaltenname Schimmel Die Rolle der Spalte Einschränkungen usw.
id Numerischer Wert Primärschlüssel, der das Produkt eindeutig definiert Wert, der automatisch nummeriert werden soll
name String Produktname Produktnameの表示に使う
status String Klassifizierung der Produktanzeige 0:Kein Status 1:Für die Öffentlichkeit zugänglich 2:Nur für Mitglieder
display String Produkte ein- / ausblenden 0:Nicht anzeigen 1:zeigen

Dieses Mal konzentrieren wir uns auf den dreistelligen Teilungswert, der durch den Status definiert wird, und auf das Flag, das von der Anzeige behandelt wird.

Aufzählung, die Flags und Partitionswerte definiert

Erstellen Sie auf der Java-Seite eine Aufzählung für den Status und die Anzeige der beiden Spalten, die dieses Mal angezeigt werden. Da der Status "derjenige ist, der den Umfang der Produktoffenlegung bestimmt", benennen Sie ihn in ItemPublish um. Da display ein Binärwert ist, der bestimmt, ob angezeigt werden soll oder nicht, wird er im Allgemeinen auf TrueOrFalse gesetzt, damit er in anderen Spalten verwendet werden kann.

ItemPublish.java


import java.util.Arrays;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 *Produktanzeigekategorie.
 */
@Getter
@AllArgsConstructor
public enum ItemPublish {
	NONE("0"), PUBLISH("1"), MEMBER_ONLY("2"), NO_VALUE("");

	private String value;

	public static ItemPublish getDisplayStatus(String value) {
		return Arrays.stream(values())
				.filter(v -> v.getValue().equals(value))
				.findFirst()
				.orElse(NO_VALUE);
	}
}

NO_VALUE ist definiert [^ 4], wenn ein Wert außerhalb des Bereichs angegeben wird, den der Divisionswert annehmen kann.

TrueOrFalse.java


@Getter
@AllArgsConstructor
public enum TrueOrFalse {
  TRUE("1"), FALSE("0"), NONE("");

  private String value;

  TrueOrFalse(String value) {
    this.value = value;
  }

  public static TrueOrFalse getTrueOrFalse(String input) {
    return Arrays.stream(values())
        .filter(v -> v.getValue().equals(input))
          .findFirst().orElse(NONE);
  }
}

Eine Aufzählung, die einen booleschen Wert definiert (1 für wahr, 0 für falsch). Andere Werte als 0 und 1 sind unerwartet, aber unerwartete Werte werden als NONE definiert.

Spring und Mybatis Implementierung

Eine Implementierung, die die Datenbank abfragt. Spring MVC Repository und Service. Suchbedingungen werden in der ItemCondition-Klasse definiert, und ein Datensatz mit Suchergebnissen wird in der Item-Klasse definiert.

ItemRepository.java


mport java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;
import serendip.spring.sample.mybatis.model.Item;
import serendip.spring.sample.mybatis.model.ItemCondition;

/**
 *Produktsuch-Repository.
 */
@Repository
@RequiredArgsConstructor
public class ItemRepository {
	private final SqlSession sqlSession;

	public List<Item> selectItems(ItemCondition condition) {
		return sqlSession.selectList("selectItems", condition);
	}
}

ItemService.java


import java.util.List;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import serendip.spring.sample.mybatis.model.Item;
import serendip.spring.sample.mybatis.model.ItemCondition;
import serendip.spring.sample.mybatis.repository.ItemRepository;

/**
 *Produktsuche Service.
 */
@Service
@RequiredArgsConstructor
public class ItemService {
	private final ItemRepository repository;

	public List<Item> selectItems(ItemCondition condition) {
		return repository.selectItems(condition);
	}
}

Artikelbedingung der Suchbedingung. Die zuvor definierte Aufzählung wird als Bedingung angegeben.

ItemCondition.java


import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

/**
 *Bedingungen für die Produktsuche.
 */
@Builder
@ToString
@Getter
public class ItemCondition {
	private int id;
	private ItemPublish status;
	private TrueOrFalse display;
}

Element, das einen Datensatz des Suchergebnisses darstellt. Die Werte für Spaltenstatus und Anzeige werden von der definierten Aufzählung behandelt.

Item.java


import lombok.Getter;
import lombok.Setter;

/**
 *Produkttabelle(Item)1 Aufzeichnung von.
 */
@Getter @Setter
public class Item {
	private Integer id;
	private String name;
	private ItemPublish status;
	private TrueOrFalse display;
}

Definieren Sie die SQL-Zuordnung

Die diesmal auszuführende SQL lautet wie folgt.

sql-mappings.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="serendip.spring.sample.mybatis.repository.MemberRepository">
	<select id="selectItems"
		parameterType="serendip.spring.sample.mybatis.model.ItemCondition"
		resultType="serendip.spring.sample.mybatis.model.Item">
		SELECT
			id,
			name,
			status,
			display
		FROM
			item
	</select>
</mapper>

Jetzt ist die Implementierungsklasse fertig. Wenn Mybatis so ausgeführt wird, wie es ist, wird es automatisch in enum konvertiert. Es wird jedoch eine Laufzeitausnahme ausgelöst, da die standardmäßig auszuführende Methode [^ 5] nicht abgerufen werden kann.

Daher verwendet Mybatis TypeHandler, der die Typkonvertierung für eine bestimmte Aufzählung durchführt.

Erstellen Sie TypeHandler für enum

Mit TypeHandler konvertiert Mybatis zwischen der Java-Klasse und der in der SQLMapping-Definition enthaltenen Datenbank.

Implementierter TypeHandler

Der implementierte TypeHandler ist im Paket org.apache.ibatis.type von Mybatis enthalten. Hier definieren wir die Konvertierung für Java-Typen und den TypeHandler für die in Datenbankspalten verwendeten Typen. Beispielsweise werden auch die Big Decimal-Typkonvertierung und die Datenbank-BLOB-Typkonvertierung von Java bereitgestellt, die die in der Pretared-Anweisung von JDBC enthaltenen Funktionen ausführen.

Erweiterung von TypeHandler

TypeHandler von Mybatis wird von BaseTypeHandler ausgeführt. Implementieren Sie vier Methoden: setNonNullParameter, mit dem der an SQL zu übergebende Wert festgelegt wird, und getNullableResult (insgesamt drei Methoden), mit dem der Wert empfangen wird.

ItemPublishTypeHandler.java


import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import serendip.spring.sample.mybatis.model.ItemPublish;

/**
 *Geben Sie die Konvertierungsklasse für die Aufzählung des Status der Produktveröffentlichung ein.
 */
public class ItemPublishTypeHandler extends BaseTypeHandler<ItemPublish> {

	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, ItemPublish parameter, JdbcType jdbcType)
			throws SQLException {
		ps.setString(i, parameter.getValue());
	}

	@Override
	public ItemPublish getNullableResult(ResultSet rs, String columnName) throws SQLException {
		return ItemPublish.getDisplayStatus(rs.getString(columnName));
	}

	@Override
	public ItemPublish getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
		return ItemPublish.getDisplayStatus(rs.getString(columnIndex));
	}

	@Override
	public ItemPublish getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
		return ItemPublish.getDisplayStatus(cs.getString(columnIndex));
	}
}

TrueOrFalseTypeHandler.java


import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import serendip.spring.sample.mybatis.model.TrueOrFalse;

/**
 *Eine Typkonvertierungsklasse für Enum, die Flags verarbeitet.
 */
public class TrueOrFalseTypeHandler extends BaseTypeHandler<TrueOrFalse> {

	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, TrueOrFalse parameter, JdbcType jdbcType)
			throws SQLException {
		ps.setString(i, parameter.getValue());
	}

	@Override
	public TrueOrFalse getNullableResult(ResultSet rs, String columnName) throws SQLException {
		return TrueOrFalse.getTrueOrFalse(rs.getString(columnName));
	}

	@Override
	public TrueOrFalse getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
		return TrueOrFalse.getTrueOrFalse(rs.getString(columnIndex));
	}

	@Override
	public TrueOrFalse getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
		return TrueOrFalse.getTrueOrFalse(cs.getString(columnIndex));
	}
}

Registrierung von TypeHandler

Beschreiben des erstellten TypeHandlers in der Mybatis-Konfigurationsdatei. Beschreiben Sie den TypeHandler, der vom Element nacheinander im Element erstellt wurde.

mybatis-config.xml


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<typeHandlers>
		<typeHandler handler="serendip.spring.sample.mybatis.typehandler.ItemPublishTypeHandler"/>
		<typeHandler handler="serendip.spring.sample.mybatis.typehandler.TrueOrFalseTypeHandler"/>
	</typeHandlers>
...
</configuration>

Gilt für Suchbedingungen

Zusätzlich zu den Suchergebnissen aus der Datenbank kann TypeHandler auf Argumente (Parameter) angewendet werden.

Beschreibung der Behandlung von Enum in SQL Mapping

Die Suchbedingung verwendet TrueOrFalse von ItemCondition, wie oben erwähnt. Für die präziseste Verwendung gibt es eine Möglichkeit, den Wert mit getValue () von enum abzurufen.

sql-mappings.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="serendip.spring.sample.mybatis.repository.MemberRepository">
	<select id="selectItems"
		parameterType="serendip.spring.sample.mybatis.model.ItemCondition"
		resultType="serendip.spring.sample.mybatis.model.Item">
		SELECT
			id,
			name,
			status,
			display
		FROM
			item
		<where>
			<if test="display.getValue() != ''">
			     display = #{display.value}
			</if>
		</where>
	</select>
</mapper>

Diese Methode ist jedoch etwas unvernünftig, da sie den beschriebenen Inhalt nur mit enum mit Zeichenfolgen in dynamischem SQL vergleicht. Mybatis kann jedoch dynamische SQL-bedingte Anweisungen in OGNL schreiben.

Lass es uns benutzen.

Vergleichen Sie enum direkt mit SQL Mapping

Um in OGNL auf enum zu verweisen, verwenden Sie den vollständigen Klassennamen @ enumerator von @ enum wie folgt.

sql-mappings.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="serendip.spring.sample.mybatis.repository.MemberRepository">
	<select id="selectItems"
		parameterType="serendip.spring.sample.mybatis.model.ItemCondition"
		resultType="serendip.spring.sample.mybatis.model.Item">
		SELECT
			id,
			name,
			status,
			display
		FROM
			item
		<where>
			<if test="display != @serendip.spring.sample.mybatis.model.TrueOrFalse@NONE">
			    display = #{display.value}
			</if>
		</where>
	</select>
</mapper>

Jetzt können Sie enum auf die bedingte Verzweigung von dynamischem SQL anwenden.

Über RestController von Spring MVC

Führen Sie den diesmal eingeführten Inhalt von RestController of Spring aus und erstellen Sie ihn so, dass eine Antwort im JSON-Format zurückgegeben wird.

Controller-Implementierung

import java.util.List;
import java.util.Optional;

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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import serendip.spring.sample.mybatis.model.Item;
import serendip.spring.sample.mybatis.model.ItemCondition;
import serendip.spring.sample.mybatis.model.ItemPublish;
import serendip.spring.sample.mybatis.model.TrueOrFalse;
import serendip.spring.sample.mybatis.service.ItemService;

/**
 *Produktsuche RestController.
 */
@RestController
@RequestMapping("/items")
@RequiredArgsConstructor
public class ItemController {

	private final ItemService service;

	@GetMapping("")
	public List<Item> searchItems(@RequestParam Optional<String> publish, @RequestParam Optional<String>  display) {
		ItemCondition condition = ItemCondition.builder()
				.status(ItemPublish.getDisplayStatus(publish.orElse("")))
				.display(TrueOrFalse.getTrueOrFalse(display.orElse("")))
				.build();

		return service.selectItems(condition);
	}
}

Unter der Annahme, dass die Suchbedingung nicht festgelegt ist, ist der Parameter, wenn er undefiniert ist, der Wert, wenn er in jedem Aufzählungswert undefiniert ist.

Ausführungsergebnis

http://127.0.0.1:8080/items

[
    {
        "id": 1,
        "name": "Holzstuhl",
        "status": "NONE",
        "display": "TRUE"
    },
    {
        "id": 2,
        "name": "Glas tisch",
        "status": "PUBLISH",
        "display": "TRUE"
    },
    {
        "id": 3,
        "name": "Holztisch",
        "status": "MEMBER_ONLY",
        "display": "FALSE"
    }
]

http://127.0.0.1:8080/items?display=0

[
    {
        "id": 3,
        "name": "Holztisch",
        "status": "MEMBER_ONLY",
        "display": "FALSE"
    }
]

http://127.0.0.1:8080/items?display=1

[
    {
        "id": 1,
        "name": "Holzstuhl",
        "status": "NONE",
        "display": "TRUE"
    },
    {
        "id": 2,
        "name": "Glas tisch",
        "status": "PUBLISH",
        "display": "TRUE"
    }
]

Für diesen Parameterwert wird der Datenbankwert so wie er ist als Bedingungswert verwendet, es können jedoch auch andere Werte im Controller verwendet werden. Wenn Sie den JSON-Antwortwert weiter konvertieren möchten, implementieren Sie die JsonSerialize-Schnittstelle von jackson in enum und sie wird automatisch konvertiert.

abschließend

Wir hoffen, dass Sie die automatische Typkonvertierung und Wertekonvertierung verwenden können, um eine robustere und wartbarere Anwendung (・ ω ・) zu erstellen.


[^ 1]: Der Inhalt enthält auch eine "Entwicklungssite oder einen Herstellerdialekt", der durch das "Flag" und den "Klassifizierungswert" gekennzeichnet ist. In diesem Artikel ist "Flag" eine Spalte mit nur zwei Werten, dh eine Spalte, die nur wahr / falsch des sogenannten Booleschen Werts anzeigt, und "Teilungswert" ist eine Spalte mit einem festen Bereich möglicher Werte für andere Zwecke als den Booleschen Wert. [^ 2]: Es gibt beispielsweise Fälle, in denen das Lösch- und das Aktualisierungsflag, die selbst ab 2018 nicht mehr so vorzuziehen sind, unabhängig davon existieren, ob das System alt oder neu ist. 0: gültiger Datensatz, 1: gelöscht usw. [^ 3]: Der Spaltenname wird absichtlich schlampig gemacht, was aus dem Namen, der eher ein Altsystem ist, schwer zu erraten ist. Was sind Status und Anzeige! ?? Wird es sein? [^ 4]: Dieses Mal gebe ich immer einen Wert zurück, aber wenn ein unmöglicher Wert angegeben wird, kann dies eine Anwendungsausnahme auslösen. [^ 5]: Die Methode java.lang.Enum.valueOf wird ausgeführt.

Recommended Posts

Behandeln Sie Enum mit Mybatis TypeHandler
Siehe Aufzählung in Thymeleaf
Verwendung des Java-Aufzählungstyps (Enum) in Mapper XML von MyBatis
Enum Strategiemuster in Java
Reproduzieren Sie die Java-Enumeration in C #
Behandeln Sie Ihre eigenen Anmerkungen in Java
Behandle C char ** gut mit Swift