7

I'm running into trouble with hibernate. I recently set my hbm2ddl to validate, and it has been complaining a lot about wrong datatypes. I have fixed every problem except for booleans.

I have a field opener in my class, which is mapped as:

<property column="opener" name="opener" type="boolean"/>

The column opener is a tinyint (4) and has a value of 1 or 0. So far I've tried changing the types, but to no avail. I have also tried using the following setting in my hibernate.cfg:

<property name="hibernate.query.substitutions">true 1, false 0</property>

But I am still getting the same error. What am I doing wrong?

org.hibernate.HibernateException: Wrong column type: opener, expected: bit
    at org.hibernate.mapping.Table.validateColumns(Table.java:261)
    at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1083)
    at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:116)
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:317)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1294)
    at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:859)

note: I have no access to the database.

n00begon
  • 3,503
  • 3
  • 29
  • 42
Maarten Blokker
  • 604
  • 1
  • 5
  • 23

6 Answers6

4

If you can't change your SQL type in your table, i recommend you to do this :

<property name="opener" column="opener" type="path.to.your.package.YourClassUserType"/>

and create your class :

import org.hibernate.usertype.UserType;

public class YourClassUserType implements UserType{
 ...
}

you have to implement methods from the interface UserType. The implementation will transform byte to boolean (because a TINYINT is mapped in byte in Java)

see examples here

good luck :)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
EricParis16
  • 809
  • 5
  • 9
4

For anyone who ran into the same trouble as me, I used a combination of two answers posted here.

I implemented a custom usertype to handle my tinyint field:

public class TinyIntegerToBoolean implements UserType {

    public int[] sqlTypes() {
        return new int[]{Types.TINYINT};
    }

    public Class returnedClass() {
        return Boolean.class;
    }

    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor si, Object owner) throws HibernateException, SQLException {
        return (rs.getByte(names[0]) != 0);
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor si) throws HibernateException, SQLException {
        st.setByte(index, Boolean.TRUE.equals(value) ? (byte) 1 : (byte) 0);
    }

    /* boilerplate... */
    public boolean isMutable() {
        return false;
    }

    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == null || y == null) {
            return false;
        } else {
            return x.equals(y);
        }
    }

    public int hashCode(Object x) throws HibernateException {
        assert (x != null);
        return x.hashCode();
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    public Object replace(Object original, Object target, Object owner)
            throws HibernateException {
        return original;
    }

    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    public Object assemble(Serializable cached, Object owner)
            throws HibernateException {
        return cached;
    }
}

Then I added the following to my mappings:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <typedef class="com.test.model.TinyIntegerToBoolean" name="tinyint_boolean"/>
</hibernate-mapping>

Then in my opener field I use type=tinyint_boolean and it works like a charm :)

n00begon
  • 3,503
  • 3
  • 29
  • 42
Maarten Blokker
  • 604
  • 1
  • 5
  • 23
  • 2
    If this is the default way you want to map booleans in your app and if you happen to be running Hibernate 4 a nicer alternative IMHO is to implement org.hibernate.type.BasicType instead of UserType and regsiter this implementation with the org.hibernate.type.BasicTypeRegistry. Specifically note the BasicType#getRegistrationKeys. Your type would become the default type mapping for Whatever keys you register your type under. Here you would register it under ["boolean", "java.lang.Boolean"]. See http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/#types-registry – Steve Ebersole May 31 '12 at 04:34
  • I am running hibernate 4.1, it's not the default way to map a boolean, but its used quite a lot. The solution above is really ugly imho, so il look into this as soon as i can, thanks! – Maarten Blokker May 31 '12 at 08:47
  • So using BasicType with getRegistrationKeys == ["tinyint_boolean"] would be equivalent to the typedef mapping. – Steve Ebersole May 31 '12 at 11:25
  • so that would save me some boilerplate code and an xml file :)! thanks for mentioning this! – Maarten Blokker May 31 '12 at 13:11
  • We've been using Jadira Usertype to map Jodatime DateTime fields to hibernate. That Jadira Usertype framework also has an `AbstractUserType` which takes care of all your boilerplate code. – drvdijk May 13 '14 at 19:45
  • I dont really like that solution, it creates an extra dependency with jodatime, what if you want to switch to java.util.time in java 8? you'd have to rewrite your models, and now also rewrite your usertypes! – Maarten Blokker May 14 '14 at 08:02
3

You can define your DB column as a char(1) and in your Hibernate mapping file define the property as type="yes_no", which is a Java Boolean type. These will appear as Y and N values in the DB.

If you want to use tiny_int then the size will have to be 1, but I'm not 100% sure how this is mapped in the HBM file.

mut1na
  • 795
  • 6
  • 21
1

try this :

<property column="opener" name="opener" access="field" />

assuming that you got a getter

 boolean isOpener() ;

and a setter

void setOpener(boolean b);
EricParis16
  • 809
  • 5
  • 9
1

You can try to use numeric_boolean as a type:

<property column="opener" name="opener" type="numeric_boolean"/>
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • 1
    i think i've seen a jpa annonation somewhere that did something like this. This does not help me thoug, when i map opener type as a numeric_boolean i get: org.hibernate.MappingException: Could not determine type for: numeric_boolean, for columns: [org.hibernate.mapping.Column(opener)] – Maarten Blokker Jun 01 '11 at 07:48
  • Your answer leaded me to this more complete answer which solved my issue: https://stackoverflow.com/a/5567818/1657465 – luiscla27 Sep 07 '20 at 22:45
0

This is a tricky one because you have no access to the database. But it can be done with a bit of work

What you will need to do is create a custom type class. This class will basically retrieve the value from the database (1 or 0) and it will contain logic that returns either a true or false Boolean object.

Here is an example:

http://alenovarini.wikidot.com/mapping-a-custom-type-in-hibernate

your mapping for your new type will look like:

    <typedef class="com.path.to.my.package.CustomBooleanType" name="myBoolType" />

That classes nullSafeGet method will return your Boolean object that will contain true or false.

so your new mapping will contain:

<property column="opener" name="opener" type="myBoolType"/>
bsimic
  • 926
  • 7
  • 15