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!
Advertisement
Answer
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(VarcharTypeDescriptor.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.