Available in versions: Dev (3.21) | Latest (3.20) | 3.19 | 3.18 | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11
This documentation is for the unreleased development version of jOOQ. Click on the above version links to get this documentation for a supported version of jOOQ.
Custom data type Binding
Supported by ✅ Open Source Edition ✅ Express Edition ✅ Professional Edition ✅ Enterprise Edition
While converters are very useful for simple use-cases, org.jooq.Binding is useful when you need to customise data type interactions at a JDBC level, e.g. when you want to bind a PostgreSQL JSON data type. Custom bindings implement the following SPI:
public interface Binding<T, U> extends Serializable {
// A converter that does the conversion between the database type T
// and the user type U (see previous examples)
Converter<T, U> converter();
// A callback that generates the SQL string for bind values of this
// binding type. Typically, just ?, but also ?::json, etc.
void sql(BindingSQLContext<U> ctx) throws SQLException;
// Callbacks that implement all interaction with JDBC types, such as
// PreparedStatement, CallableStatement, SQLOutput, SQLinput, ResultSet
void register(BindingRegisterContext<U> ctx) throws SQLException;
void set(BindingSetStatementContext<U> ctx) throws SQLException;
void set(BindingSetSQLOutputContext<U> ctx) throws SQLException;
void get(BindingGetResultSetContext<U> ctx) throws SQLException;
void get(BindingGetStatementContext<U> ctx) throws SQLException;
void get(BindingGetSQLInputContext<U> ctx) throws SQLException;
}
Below is full fledged example implementation that uses Google Gson to model JSON documents in Java
// We're binding <T> = JSON (or JSONB), and <U> = JsonElement (user type)
// Alternatively, extend org.jooq.impl.AbstractBinding to implement fewer methods.
public class PostgresJSONGsonBinding implements Binding<JSON, JsonElement> {
private final Gson gson = new Gson();
// The converter does all the work
@Override
public Converter<JSON, JsonElement> converter() {
return new Converter<JSON, JsonElement>() {
@Override
public JsonElement from(JSON t) {
return t == null ? JsonNull.INSTANCE : gson.fromJson(t.data(), JsonElement.class);
}
@Override
public JSON to(JsonElement u) {
return u == null || u == JsonNull.INSTANCE ? null : JSON.json(gson.toJson(u));
}
@Override
public Class<JSON> fromType() {
return JSON.class;
}
@Override
public Class<JsonElement> toType() {
return JsonElement.class;
}
};
}
// Rending a bind variable for the binding context's value and casting it to the json type
@Override
public void sql(BindingSQLContext<JsonElement> ctx) throws SQLException {
// Depending on how you generate your SQL, you may need to explicitly distinguish
// between jOOQ generating bind variables or inlined literals.
if (ctx.render().paramType() == ParamType.INLINED)
ctx.render().visit(DSL.inline(ctx.convert(converter()).value())).sql("::json");
else
ctx.render().sql(ctx.variable()).sql("::json");
}
// Registering VARCHAR types for JDBC CallableStatement OUT parameters
@Override
public void register(BindingRegisterContext<JsonElement> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.VARCHAR);
}
// Converting the JsonElement to a String value and setting that on a JDBC PreparedStatement
@Override
public void set(BindingSetStatementContext<JsonElement> ctx) throws SQLException {
JSON json = ctx.convert(converter()).value();
ctx.statement().setString(ctx.index(), json == null ? null : json.data());
}
// Getting a String value from a JDBC ResultSet and converting that to a JsonElement
@Override
public void get(BindingGetResultSetContext<JsonElement> ctx) throws SQLException {
ctx.convert(converter()).value(JSON.json(ctx.resultSet().getString(ctx.index())));
}
// Getting a String value from a JDBC CallableStatement and converting that to a JsonElement
@Override
public void get(BindingGetStatementContext<JsonElement> ctx) throws SQLException {
ctx.convert(converter()).value(JSON.json(ctx.statement().getString(ctx.index())));
}
// Setting a value on a JDBC SQLOutput (useful for Oracle OBJECT types)
@Override
public void set(BindingSetSQLOutputContext<JsonElement> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
// Getting a value from a JDBC SQLInput (useful for Oracle OBJECT types)
@Override
public void get(BindingGetSQLInputContext<JsonElement> ctx) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
}
Just like with simple Converters, a org.jooq.Binding can be attached to a column expression either directly in the code generator using forced types configuration, or explictly, e.g. when constructing plain SQL expressions:
// Construct the DataType:
DataType<JsonElement> jsonElementType = JSON.asConvertedDataType(new PostgresJSONGsonBinding());
// Plain SQL template based
Field<JsonElement> f1 = DSL.field("my_table.my_json_column", jsonElementType);
// Name based
Field<JsonElement> f2 = DSL.field(name("my_table", "my_json_column"), jsonElementType);
Feedback
Do you have any feedback about this page? We'd love to hear it!