2

I have a table with a column which type is a bit (24). I tried to use String and when I try to get this object it maps well, but when I try to save it, Hibernate throws Exception: org.postgresql.util.PSQLException: ERROR: column is of type bit but expression is of type character varying

I know that if it's a bit(1) I can use Boolean, but I don't know what should I do in my case.

Thanks in advance!

2 Answers 2

6

Unfortunately, JPA/hibernate does not support BIT having a parametrized length.

Fortunately, you can define your own custom type with hibernate.

First, you need to create BitStringType, BitStringJavaDescriptor, and BitStringSqlDescriptor:

public class BitStringType extends AbstractSingleColumnStandardBasicType<String> {

    public static final BitStringType INSTANCE = new BitStringType();

    public BitStringType() {
        super(BitStringSqlDescriptor.INSTANCE, BitStringJavaDescriptor.INSTANCE);
    }

    @Override
    public String getName() {
        return "BitString";
    }

}
public class BitStringJavaDescriptor extends AbstractTypeDescriptor<String> {

    public static final BitStringJavaDescriptor INSTANCE = new BitStringJavaDescriptor();

    public BitStringJavaDescriptor() {
        super(String.class, ImmutableMutabilityPlan.INSTANCE);
    }

    @Override
    public String fromString(String string) {
        return string;
    }

    @Override
    public <X> X unwrap(String value, Class<X> type, WrapperOptions options) {
        if (value == null)
            return null;
        if (String.class.isAssignableFrom(type))
            return (X) value;
        throw unknownUnwrap(type);
    }

    @Override
    public <X> String wrap(X value, WrapperOptions options) {
        if (value == null)
            return null;
        if (String.class.isInstance(value))
            return (String) value;
        throw unknownWrap(value.getClass());
    }

}
public class BitStringSqlDescriptor implements SqlTypeDescriptor {

    public static BitStringSqlDescriptor INSTANCE = new BitStringSqlDescriptor();

    @Override
    public int getSqlType() {
        return Types.OTHER;
    }

    @Override
    public boolean canBeRemapped() {
        return true;
    }

    @Override
    public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicBinder<X>(javaTypeDescriptor, this) {
            @Override
            protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
                st.setObject(index, javaTypeDescriptor.unwrap(value, String.class, options), Types.OTHER);
            }
            @Override
            protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
                st.setObject(name, javaTypeDescriptor.unwrap(value, String.class, options), Types.OTHER);
            }
        };
    }

    @Override
    public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicExtractor<X>(javaTypeDescriptor, this) {
            @Override
            protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
                return javaTypeDescriptor.wrap(rs.getString(name), options);
            }
            @Override
            protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
                return javaTypeDescriptor.wrap(statement.getString(index), options);
            }
            @Override
            protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
                return javaTypeDescriptor.wrap(statement.getString(name), options);
            }
        };
    }

}

Having those classes, you can define a type for your field. Please, use the correct package (in my case I've used the one from my demo com.yonlabs.jpa):

    @Column
    @Type(type = "com.yonlabs.jpa.BitStringType")
    private String bits;

You can also register this type with hibernate to use a registered name instead of a fully qualified Java class.

Sign up to request clarification or add additional context in comments.

Comments

0

works with @JdbcType annotation and custom type, tested on hibernate 6.4.4

@Column
@JdbcType(BitStringJdbcType.class)
private String bits;

jdbc type class

import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
import org.hibernate.type.descriptor.jdbc.JdbcType;

import java.sql.*;

public class BitStringJdbcType implements JdbcType {

    @Override
    public int getJdbcTypeCode() {
        return SqlTypes.OTHER;
    }

    @Override
    public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
        return new BasicBinder<X>(javaType, this) {
            @Override
            protected void doBind(PreparedStatement preparedStatement, X value, int index, WrapperOptions wrapperOptions) throws SQLException {
                preparedStatement.setObject(index, javaType.unwrap(value, String.class, wrapperOptions), Types.OTHER);
            }

            @Override
            protected void doBind(CallableStatement callableStatement, X value, String name, WrapperOptions wrapperOptions) throws SQLException {
                callableStatement.setObject(name, javaType.unwrap(value, String.class, wrapperOptions), Types.OTHER);
            }
        };
    }

    @Override
    public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
        return new BasicExtractor<X>(javaType, this) {
            @Override
            protected X doExtract(ResultSet resultSet, int index, WrapperOptions wrapperOptions) throws SQLException {
                return javaType.wrap(resultSet.getString(index), wrapperOptions);
            }

            @Override
            protected X doExtract(CallableStatement callableStatement, int index, WrapperOptions wrapperOptions) throws SQLException {
                return javaType.wrap(callableStatement.getString(index), wrapperOptions);
            }

            @Override
            protected X doExtract(CallableStatement callableStatement, String name, WrapperOptions wrapperOptions) throws SQLException {
                return javaType.wrap(callableStatement.getString(name), wrapperOptions);
            }
        };
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.