...
Below are a couple of examples from our integration tests. Essentially, the new org.jooq.Binding type will complement the existing org.jooq.Converter type. It can be associated with all TableFields by the code generator to allow for a custom JDBC interaction override on a per-column level. I.e., not only will you define how to convert between <T> and <U> types (T=database type, U=user type), but you will also be able to define how such types are:
import static org.jooq.tools.Convert.convert;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import org.jooq.BindingGetResultSetContext;
import org.jooq.BindingGetSQLInputContext;
import org.jooq.BindingGetStatementContext;
import org.jooq.BindingRegisterContext;
import org.jooq.BindingSetSQLOutputContext;
import org.jooq.BindingSetStatementContext;
@SuppressWarnings("serial")
public abstract class AbstractPostgresVarcharBinding<U> implements Binding<Object, U> {
public void register(BindingRegisterContext<U> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.VARCHAR);
public void set(BindingSetStatementContext<U> ctx) throws SQLException {
ctx.statement().setString(ctx.index(), convert(ctx.convert(converter()).value(), String.class));
public void get(BindingGetResultSetContext<U> ctx) throws SQLException {
ctx.convert(converter()).value(ctx.resultSet().getString(ctx.index()));
public void get(BindingGetStatementContext<U> ctx) throws SQLException {
ctx.convert(converter()).value(ctx.statement().getString(ctx.index()));
public void set(BindingSetSQLOutputContext<U> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
public void get(BindingGetSQLInputContext<U> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
import static org.jooq.tools.Convert.convert;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import org.jooq.BindingGetResultSetContext;
import org.jooq.BindingGetSQLInputContext;
import org.jooq.BindingGetStatementContext;
import org.jooq.BindingRegisterContext;
import org.jooq.BindingSQLContext;
import org.jooq.BindingSetSQLOutputContext;
import org.jooq.BindingSetStatementContext;
import org.jooq.Converter;
import org.jooq.impl.DSL;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
@SuppressWarnings("serial")
public class PostgresJSONGsonBinding extends AbstractPostgresVarcharBinding<JsonElement> {
public Converter<Object, JsonElement> converter() {
return new Converter<Object, JsonElement>() {
public JsonElement from(Object t) {
return t == null ? JsonNull.INSTANCE : new Gson().fromJson("" + t, JsonElement.class);
public Object to(JsonElement u) {
return u == null || u == JsonNull.INSTANCE ? null : new Gson().toJson(u);
public Class<Object> fromType() {
public Class<JsonElement> toType() {
return JsonElement.class;
public void sql(BindingSQLContext<JsonElement> ctx) throws SQLException {
ctx.render().visit(DSL.val(ctx.convert(converter()).value())).sql("::json"); // Custom SQL behaviour here
import java.io.IOException;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.stream.Collectors;
import org.jooq.BindingSQLContext;
import org.jooq.Converter;
import org.jooq.impl.DSL;
import org.jooq.lambda.Seq;
import org.jooq.tools.csv.CSVParser;
@SuppressWarnings("serial")
public class PostgresHstoreMapBinding extends AbstractPostgresVarcharBinding<Map<String, String>> {
public Converter<Object, Map<String, String>> converter() {
return new Converter<Object, Map<String, String>>() {
public Map<String, String> from(Object t) {
String[] kvs = new CSVParser(',').parseLine(t + "");
Map<String, String> result = new LinkedHashMap<>();
String[] split = kv.split("=>");
result.put(split[0].replaceAll("^\"?(.*?)\"?$", "$1"), split[1].replaceAll("^\"?(.*?)\"?$", "$1"));
throw new RuntimeException(e);
public Object to(Map<String, String> u) {
return u == null ? null : Seq.seq(u).map(t -> t.v1 + "=>" + t.v2).collect(Collectors.joining(","));
public Class<Object> fromType() {
@SuppressWarnings({ "unchecked", "rawtypes" })
public Class<Map<String, String>> toType() {
return (Class) Map.class;
public void sql(BindingSQLContext<Map<String, String>> ctx) throws SQLException {
ctx.render().visit(DSL.val(ctx.convert(converter()).value())).sql("::hstore"); // Custom SQL behaviour here