Skip to content

Type Converters

Volt uses type converters to translate between Java types and database types. Converters handle both writing values to prepared statements and reading values from result sets.

Built-in Converters

Volt includes converters for common types out of the box:

Java TypeSQL Type
StringVARCHAR
Long / longBIGINT
Integer / intINTEGER
Double / doubleDOUBLE
BigDecimalDECIMAL
Boolean / booleanBOOLEAN
UUIDUUID
InstantTIMESTAMP
LocalDateDATE
LocalDateTimeTIMESTAMP

Custom Converters

For types not covered by built-in converters, implement BidirectionalTypeConverter<T>:

java
import me.oskarscot.volt.converter.BidirectionalTypeConverter;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

public class MoneyConverter implements BidirectionalTypeConverter<Money> {

    @Override
    public void write(PreparedStatement stmt, int index, Money value) throws SQLException {
        if (value == null) {
            stmt.setNull(index, Types.BIGINT);
        } else {
            // Store as cents
            stmt.setLong(index, value.toCents());
        }
    }

    @Override
    public Money read(ResultSet rs, String column) throws SQLException {
        long cents = rs.getLong(column);
        if (rs.wasNull()) {
            return null;
        }
        return Money.fromCents(cents);
    }
}

Registering Custom Converters

Register your converter with the Volt instance:

java
volt.registerConverter(Money.class, new MoneyConverter());

Register converters before registering entities that use them.

Example: Enum Converter

Store enums as strings in the database:

java
public class OrderStatusConverter implements BidirectionalTypeConverter<OrderStatus> {

    @Override
    public void write(PreparedStatement stmt, int index, OrderStatus value) throws SQLException {
        if (value == null) {
            stmt.setNull(index, Types.VARCHAR);
        } else {
            stmt.setString(index, value.name());
        }
    }

    @Override
    public OrderStatus read(ResultSet rs, String column) throws SQLException {
        String value = rs.getString(column);
        return value != null ? OrderStatus.valueOf(value) : null;
    }
}

Usage:

java
volt.registerConverter(OrderStatus.class, new OrderStatusConverter());
volt.registerEntity(Order.class);

Example: JSON Converter

Store objects as JSON using a library like Gson:

java
public class JsonConverter<T> implements BidirectionalTypeConverter<T> {

    private final Gson gson = new Gson();
    private final Class<T> type;

    public JsonConverter(Class<T> type) {
        this.type = type;
    }

    @Override
    public void write(PreparedStatement stmt, int index, T value) throws SQLException {
        if (value == null) {
            stmt.setNull(index, Types.VARCHAR);
        } else {
            stmt.setString(index, gson.toJson(value));
        }
    }

    @Override
    public T read(ResultSet rs, String column) throws SQLException {
        String json = rs.getString(column);
        return json != null ? gson.fromJson(json, type) : null;
    }
}

Handling Nulls

Always handle null values in your converters:

  1. Writing nulls: Use stmt.setNull(index, sqlType) with the appropriate SQL type
  2. Reading nulls: Check rs.wasNull() after reading primitive types, or check for null directly with object types
java
@Override
public Double read(ResultSet rs, String column) throws SQLException {
    double value = rs.getDouble(column);
    return rs.wasNull() ? null : value;  // getDouble returns 0 for NULL
}

Made with ❤️