Il y a quelque temps que je n'avais pas publié d'article. Aujourd'hui, voyons comment utiilser un TypeHandler dans MyBatis, intégré à Spring Boot, pour convertir des types de données spéciaux, notamment lors de l'utilisation de PostgreSQL.
Dans mes projets, j'ai rencontré deux types particuliers dans PostgreSQL : UUID et jsonb. Pour la couche d'accès aux données, j'utilise la dépendance suivante (avec l'ORM tk.mybatis) :
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
Ce framework simplifie la création de code CRUD basique grâce au générateur de code. Cependant, les types UUID et jsonb posent problème. Le type UUID peut être traité relativement facilement en mappant les colonnes vers le type Java UUID, mais le type jsonb est plus délicat.
Pour résoudre ce problème, il est nécessaire de créer un TypeHandler personnalisé. Voici un exemple d'implémentation (les noms de classes et méthodes ont été modifiés par rapport à l'original) :
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 org.apache.ibatis.type.MappedTypes;
import org.postgresql.util.PGobject;
import com.example.common.utils.JsonUtils;
@MappedTypes({Object.class})
public class JsonbTypeConverter extends BaseTypeHandler<Object> {
private static final PGobject pgObject = new PGobject();
@Override
public void setNonNullParameter(PreparedStatement ps, int index, Object obj, JdbcType jdbcType) throws SQLException {
pgObject.setType("jsonb");
pgObject.setValue(JsonUtils.toJson(obj));
ps.setObject(index, pgObject);
}
@Override
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
return value != null ? JsonUtils.fromJson(value, Object.class) : null;
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
return value != null ? JsonUtils.fromJson(value, Object.class) : null;
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
return value != null ? JsonUtils.fromJson(value, Object.class) : null;
}
}
Ce TypeHandler peut être intégré de deux manières :
Méthode 1 : Configuraton globale dans application.yml
Ajoutez la configuration type-handlers-package dans votre fichier YAML :
mybatis:
mapper-locations: classpath:/mapper/**/*.xml
type-aliases-package: com.example.**.pojo
type-handlers-package: com.example.common.config # chemin du package contenant JsonbTypeConverter
Attention : cette méthode convertit toutes les listes et objets de paramètres en type jsonb, ce qui peut causer des effets indésirables. Je recommande plutôt la deuxième approche.
Méthode 2 : Application ciblée via le fichier XML
Déclarez le TypeHandler uniquement sur les colonnes concernées dans le resultMap :
<resultMap id="BaseResultMap" type="com.example.pojo.Demo">
<id column="guid" property="guid" jdbcType="VARCHAR" />
<result column="dtag" property="dtag" jdbcType="OTHER"
typeHandler="com.example.common.config.JsonbTypeConverter" />
</resultMap>
Pour les insertions ou mises à jour, utilisez la même syntaxe dans les paramètres :
#{dtag, jdbcType=VARCHAR, typeHandler=com.example.common.config.JsonbTypeConverter}
Avec cette approche, les opérations de lecture et d'écriture des colonnes de type jsonb fonctionnent correctement, sans perturber les autres types.