Dans Mybatis de l'O / R mapping flake work, le mécanisme de conversion automatique de type fourni dans Mybatis: Handles Enum en utilisant TypeHandler. Nous présenterons également comment écrire du SQL dynamique et son application à l'aide de Spring MVC.
Il existe une colonne [^ 1] qui gère les «indicateurs» et les «valeurs partitionnées» susceptibles d'exister dans la base de données utilisée par les systèmes et applications nouvellement créés qui ont déjà été créés, et la plage de ces valeurs est pratiquement inchangée [^ 2 ]est. Étant donné que ces valeurs sont souvent gérées en tant que type chaîne de caractères (CHAR ou VARCHAR) ou type numérique, au moins dans le monde du code Java, la chaîne et la valeur numérique ne sont pas traitées telles quelles et la plage de valeurs est S'il a été décidé, peut-il être géré par enum ...? C'est le début.
Dans cet article, nous avons créé et confirmé l'opération avec les versions suivantes.
Il est publié sur GitHub.
https://github.com/A-pZ/mybatis-spring-enum-sample
Cette fois, une fonction qui renvoie le résultat de la recherche dans une table qui gère un certain indicateur ou une valeur de division est introduite.
Pour expliquer brièvement ce qu'il faut mettre en œuvre
est.
C'est la définition de la table à référencer cette fois [^ 3].
Tableau des produits (nom du tableau: article)
Nom de colonne | Moule | Le rôle de la colonne | Contraintes, etc. |
---|---|---|---|
id | Valeur numérique | Clé primaire qui définit de manière unique le produit | Valeur à numéroter automatiquement |
name | Chaîne | Nom du produit | Nom du produitの表示に使う |
status | Chaîne | Classification d'affichage des produits | 0:Pas de statut 1:Ouvert au public 2:Membres seulement |
display | Chaîne | Afficher / masquer les produits | 0:Ne pas afficher 1:indiquer |
Cette fois, nous nous concentrerons sur la valeur de division à trois valeurs définie par le statut et le drapeau géré par l'affichage.
Créez une énumération côté Java pour l'état et l'affichage des deux colonnes qui apparaissent cette fois. Étant donné que le statut est «celui qui détermine la portée de la divulgation du produit», renommez-le ItemPublish. Puisque l'affichage est une valeur binaire qui détermine s'il faut afficher ou non, il est généralement défini sur TrueOrFalse afin qu'il puisse être utilisé dans d'autres colonnes.
ItemPublish.java
import java.util.Arrays;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*Catégorie d'affichage du produit.
*/
@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 est défini [^ 4] dans le cas où une valeur en dehors de la plage que la valeur de division peut prendre est spécifiée.
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);
}
}
Une énumération qui définit une valeur booléenne (1 pour vrai, 0 pour faux). Les valeurs autres que 0 et 1 sont inattendues, mais les valeurs inattendues sont définies comme NONE.
Une implémentation qui interroge la base de données. Référentiel et service Spring MVC. Les conditions de recherche sont définies dans la classe ItemCondition et un enregistrement des résultats de recherche est défini dans la classe Item.
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;
/**
*Référentiel de recherche de produits.
*/
@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;
/**
*Service de recherche de produits.
*/
@Service
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository repository;
public List<Item> selectItems(ItemCondition condition) {
return repository.selectItems(condition);
}
}
Item Condition de la condition de recherche. L'énumération définie précédemment est spécifiée telle quelle en tant que condition.
ItemCondition.java
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
/**
*Conditions de recherche de produits.
*/
@Builder
@ToString
@Getter
public class ItemCondition {
private int id;
private ItemPublish status;
private TrueOrFalse display;
}
Élément qui représente un enregistrement du résultat de la recherche. Les valeurs de statut et d'affichage des colonnes sont gérées par l'énumération définie.
Item.java
import lombok.Getter;
import lombok.Setter;
/**
*Tableau des produits(Item)1 enregistrement de.
*/
@Getter @Setter
public class Item {
private Integer id;
private String name;
private ItemPublish status;
private TrueOrFalse display;
}
Le SQL à exécuter cette fois est le suivant.
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>
La classe d'implémentation est maintenant prête. Cependant, lorsqu'il est exécuté tel quel, Mybatis se convertit automatiquement en enum, mais une exception d'exécution est levée car la méthode [^ 5] à exécuter par défaut ne peut pas être obtenue.
Par conséquent, Mybatis utilise TypeHandler qui effectue la conversion de type pour une énumération spécifique.
TypeHandler est ce que Mybatis effectue la conversion entre la classe Java et la base de données contenue dans la définition SQLMapping.
Le TypeHandler implémenté est fourni dans le package org.apache.ibatis.type de Mybatis. Ici, nous définissons la conversion pour les types Java et le TypeHandler pour les types utilisés dans les colonnes de la base de données. Par exemple, la conversion de type Big Decimal de Java et la conversion de type BLOB de base de données sont également fournies, qui exécutent les fonctions trouvées dans l'instruction Pretared de JDBC.
TypeHandler de Mybatis est fait par BaseTypeHandler <classe à convertir>
. Implémentez quatre méthodes: setNonNullParameter, qui définit la valeur à transmettre à SQL, et getNullableResult (trois méthodes au total), qui reçoit la valeur.
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;
/**
*Classe de conversion de type pour Enum du statut de publication du produit.
*/
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;
/**
*Une classe de conversion de type pour Enum qui gère les indicateurs.
*/
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));
}
}
Décrivez le TypeHandler créé dans le fichier de configuration Mybatis. Décrivez le TypeHandler créé par l'élément «
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>
En plus des résultats de recherche de la base de données, TypeHandler peut être appliqué aux arguments (paramètres).
La condition de recherche utilise TrueOrFalse de ItemCondition mentionné ci-dessus. Pour l'utilisation la plus concise, il existe un moyen d'obtenir la valeur avec getValue () d'énumération.
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>
Cependant, cette méthode semble un peu déraisonnable car le contenu décrit uniquement avec enum est comparé aux chaînes de caractères à l'intérieur du SQL dynamique. Cependant, Mybatis peut écrire des instructions conditionnelles SQL dynamiques dans OGNL.
Utilisons-le.
Pour faire référence à enum dans OGNL, utilisez @ enum's full class name @ enumerator
comme suit.
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>
Vous pouvez maintenant appliquer enum au branchement conditionnel de SQL dynamique.
Exécutez le contenu introduit cette fois depuis RestController de Spring et créez-le pour qu'il renvoie une réponse au format JSON.
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;
/**
*Recherche de produits 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);
}
}
En supposant que la condition de recherche n'est pas définie, si le paramètre n'est pas défini, ce sera la valeur lorsqu'il n'est pas défini dans chaque valeur d'énumération.
http://127.0.0.1:8080/items
[
{
"id": 1,
"name": "Chaise en bois",
"status": "NONE",
"display": "TRUE"
},
{
"id": 2,
"name": "Table en verre",
"status": "PUBLISH",
"display": "TRUE"
},
{
"id": 3,
"name": "Table en bois",
"status": "MEMBER_ONLY",
"display": "FALSE"
}
]
http://127.0.0.1:8080/items?display=0
[
{
"id": 3,
"name": "Table en bois",
"status": "MEMBER_ONLY",
"display": "FALSE"
}
]
http://127.0.0.1:8080/items?display=1
[
{
"id": 1,
"name": "Chaise en bois",
"status": "NONE",
"display": "TRUE"
},
{
"id": 2,
"name": "Table en verre",
"status": "PUBLISH",
"display": "TRUE"
}
]
Pour cette valeur de paramètre, la valeur de la base de données est utilisée telle quelle comme valeur de condition, mais d'autres valeurs peuvent être utilisées dans le contrôleur. En outre, si vous souhaitez convertir davantage la valeur de réponse JSON, implémentez l'interface Json Serialize de jackson dans enum et elle sera convertie automatiquement.
Nous espérons que vous pourrez utiliser la conversion automatique de type et la conversion de valeur pour aider à créer une application plus robuste et maintenable (・ ω ・).
[^ 1]: Il existe également un "site de développement ou un dialecte de fournisseur" dans le contenu indiqué par le "drapeau" et la "valeur de classification". Dans cet article, «Indicateur» est une colonne qui n'a que deux valeurs, c'est-à-dire une colonne qui affiche uniquement vrai / faux de soi-disant booléen, et «Valeur de division» est une colonne qui a une plage fixe de valeurs possibles à des fins autres que booléenne. [^ 2]: Par exemple, il y a des cas où l'indicateur de suppression et l'indicateur de mise à jour, qui ne sont pas si souhaitables même à partir de 2018, existent, que le système soit ancien ou nouveau. 0: enregistrement valide, 1: supprimé, etc. [^ 3]: Le nom de la colonne est intentionnellement négligé, ce qui est difficile à deviner à partir du nom, qui a tendance à être un système hérité. Que sont le statut et l'affichage! ?? Qu'il sera? [^ 4]: Cette fois, je renvoie toujours une valeur, mais si une valeur impossible est spécifiée, cela peut lancer une exception d'application. [^ 5]: La méthode java.lang.Enum.valueOf est exécutée.
Recommended Posts