Integración de PostgreSQL jsonb con MyBatis-Plus mediante TypeHandler personalizado

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&gt extends BaseTypeHandler<T&gt {
    private final Class<T&gt targetType;
    private final ObjectMapper mapper = new ObjectMapper();

    public JsonbTypeHandler(Class<T&gt 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));
}

Etiquetas: MyBatis-Plus PostgreSQL jsonb jackson typehandler

Publicado el 8-20 07:37