At first glance, I would try using the following @Column definition for your field:
@Column(columnDefinition="bit")
private Boolean attribute;
If it doesn't work, although I am aware that you mentioned you tested it in your question, try defining a custom @Type. The idea is based on this other SO question, the following test case, and this article from Vlad Mihalcea.
Your custom type definition could be as follows:
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
public class PostgresBit1Type
extends ImmutableType<Boolean> {
public PostgresBit1Type() {
super(Boolean.class);
}
@Override
public int[] sqlTypes() {
return new int[]{Types.BIT};
}
@Override
public Boolean get(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner)
throws SQLException {
// The paradox is that the driver itself treats BIT(1) as boolean
Boolean result = rs.getBoolean(names[0]);
return result;
}
@Override
public void set(
PreparedStatement st,
Point value,
int index,
SharedSessionContractImplementor session)
throws SQLException {
if (value == null) {
st.setNull(index, Types.BIT);
} else {
Object holder = ReflectionUtils.newInstance(
"org.postgresql.util.PGobject"
);
ReflectionUtils.invokeSetter(
holder,
"type",
"bit"
);
ReflectionUtils.invokeSetter(
holder,
"value",
value ? "1" : "0"
);
st.setObject(index, holder);
}
}
}
As mentioned in the companion SO answer, the Inmutable class is taken from this excellent article from Vlad Mihalcea:
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
public abstract class ImmutableType<T> implements UserType {
private final Class<T> clazz;
protected ImmutableType(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public Object nullSafeGet(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner)
throws SQLException {
return get(rs, names, session, owner);
}
@Override
public void nullSafeSet(
PreparedStatement st,
Object value,
int index,
SharedSessionContractImplementor session)
throws SQLException {
set(st, clazz.cast(value), index, session);
}
protected abstract T get(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner) throws SQLException;
protected abstract void set(
PreparedStatement st,
T value,
int index,
SharedSessionContractImplementor session)
throws SQLException;
@Override
public Class<T> returnedClass() {
return clazz;
}
@Override
public boolean equals(Object x, Object y) {
return Objects.equals(x, y);
}
@Override
public int hashCode(Object x) {
return x.hashCode();
}
@Override
public Object deepCopy(Object value) {
return value;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Object o) {
return (Serializable) o;
}
@Override
public Object assemble(
Serializable cached,
Object owner) {
return cached;
}
@Override
public Object replace(
Object o,
Object target,
Object owner) {
return o;
}
}
Finally, define your Boolean field similar to:
@Type(type = "your.packg.PostgresBit1Type")
@Column(columnDefinition = "bit")
public Boolean attribute;
I recommend to use Boolean and not boolean to appropriately handle null. values.
Although I think it should work I haven't tested the solution myself. I hope at least it gives you some clues to advance in the problem resolution.