1. Implementación del TypeHandler para jsonb
A diferencia del manejo genérico de json, este adaptador está optimizado para jsonb, utiliza Jackson (en lugar de FastJSON) y soporta listas y objetos anidados sin pérdida de tipado:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.postgresql.util.PGobject;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JsonbTypeHandler<T> extends BaseTypeHandler<T> {
private final Class<T> targetType;
private final ObjectMapper mapper = new ObjectMapper();
public JsonbTypeHandler(Class<T> targetType) {
this.targetType = targetType;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T value, JdbcType jdbcType) throws SQLException {
PGobject pgObj = new PGobject();
pgObj.setType("jsonb");
try {
pgObj.setValue(mapper.writeValueAsString(value));
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to serialize object to JSONB", e);
}
ps.setObject(i, pgObj);
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parseJsonb(rs.getString(columnName));
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parseJsonb(rs.getString(columnIndex));
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parseJsonb(cs.getString(columnIndex));
}
private T parseJsonb(String json) {
if (json == null || json.trim().isEmpty()) {
return null;
}
try {
return mapper.readValue(json, targetType);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to deserialize JSONB to " + targetType.getSimpleName(), e);
}
}
}
2. Uso en entidades MyBatis-Plus
Se aplica directamente en el campo anotado con @TableField, especificando el manejador y el nombre de columna:
@TableField(value = "metadata", typeHandler = JsonbTypeHandler.class)
private Map<String, Object> metadata;
3. Configuración de mapeo automático
Para habilitar la rseolución automática de tipos complejos sin XML explícito, se debe activar autoResultMap = true en la anotación @TableName. MyBatis-Plus generará internamente el ResultMap necesario si el tipo handler está correctamente registrado:
@TableName(value = "app_config", autoResultMap = true)
public class AppConfigEntity {
private Long id;
@TableField(value = "settings", typeHandler = JsonbTypeHandler.class)
private Settings settings;
}
Adicionalmente, asegúrese de registrar el TypeHandler globalmente en la configuración de MyBatis-Plus (por ejemplo, en MybatisPlusConfig.java):
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.getTypeHandlerRegistry()
.register(Map.class, new JsonbTypeHandler<>(Map.class))
.register(List.class, new JsonbTypeHandler<>(List.class));
}