Tim Reddy
Tim Reddy

Reputation: 4440

Can Hibernate automatically uppercase a column on read/insert via configuration?

We have some columns with data that must always be in uppercase to ensure uniqueness. I was wondering if hibernate can force all such columns to uppercase via some configuration file change?

We actually use a custom UserType for encrypting/decrypting column data for some other table, but I figured that would be overkill just to uppercase everything...

Alternatively, I was thinking about modifying the models such that all getters/setters will uppercase any string coming and going.

The worst(?) case scenario is to modify the Oracle column constraint to ignore case while checking uniqueness.

Any thoughts?

Upvotes: 8

Views: 17475

Answers (5)

StasKolodyuk
StasKolodyuk

Reputation: 4524

Much cleaner approach:

@Column(name = "DUMMY")
@ColumnTransformer(read = "UPPER(DUMMY)")
private String dummy

Upvotes: 14

Tim Reddy
Tim Reddy

Reputation: 4440

I decided to implement a UserType...it is as close to a hibernate configuration as I can get...here's the code...

package model;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;

public class UpperCaseUserType implements UserType {
    private static final int[] TYPES = {Types.VARCHAR};

public int[] sqlTypes() {
        return TYPES;
}

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

public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y) {
            return true;
        }
        if (null == x || null == y) {
            return false;
        }
        return new EqualsBuilder().append(x, y).isEquals();
}

public int hashCode(Object o) throws HibernateException {
        return new HashCodeBuilder().append(o).toHashCode();
}

public Object nullSafeGet(ResultSet resultSet, String[] strings, Object object) throws HibernateException, SQLException {
        return ((String) Hibernate.STRING.nullSafeGet(resultSet, strings[0])).toUpperCase();
}

public void nullSafeSet(PreparedStatement preparedStatement, Object object, int i) throws HibernateException, SQLException {
        String string = ((String) object).toUpperCase();
        Hibernate.STRING.nullSafeSet(preparedStatement, string, i);
}

public Object deepCopy(Object o) throws HibernateException {
        if (null == o) {
            return null;
        }
        return new String(o.toString());
}

public boolean isMutable() {
        return false;
}

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

public Object assemble(Serializable serializable, Object o) throws HibernateException {
        return serializable;
}

public Object replace(Object o, Object arg1, Object arg2) throws HibernateException {
        return o;
}
}

Consider this property element

<property name="serialNumber" type="model.UpperCaseUserType">
    <column name="SERIAL_NUMBER" length="20" not-null="true" unique="true" />
</property>

So the reasoning...As hibernate inserts the data, this type will convert the string to uppercase. As hibernate selects data, the same thing happens. The advantage this class has over just changing the bean's get/set to uppercase everything is when I use a Criteria to select on serialNumber. Hibernate will also uppercase my parameter as it will cast/apply the same type as defined in the table configuration.

Therefore, I don't need to remember to manually uppercase all of my search criteria for serial numbers...hibernate takes care of that for me...that's exactly what I'm trying to achieve here!

I have a JUnit that demonstrates all of this stuff, but I think my answer is way too big as it is...

Upvotes: 1

Pascal Thivent
Pascal Thivent

Reputation: 570385

Several solutions:

  1. Use a database trigger (not via configuration though).
  2. Modify the getter/setters (not via configuration though).
  3. Use a UserType to convert the attribute to upper case.
  4. Use an interceptor or an event listener.

Solution #1 is transparent for the code but I'm not a big fan of "behind your back" triggers and I don't like to spread business rules everywhere. Solution #3 and #4 are Hibernate dependent (but this might not be an issue though). Solution #2 is the easiest and most portable solution if you can change the code. That would be my choice if this is an option.

Upvotes: 4

markthegrea
markthegrea

Reputation: 3851

I suggest checking out this page: http://forum.springsource.org/archive/index.php/t-18214.html

It has 3 different ways to do this. I believe the least intrusive way is this:

<property name="upperCaseName" formula="upper(name)" lazy="true"/>

Upvotes: 0

P&#233;ter T&#246;r&#246;k
P&#233;ter T&#246;r&#246;k

Reputation: 116266

I am not aware of any configuration settings to make this possible. However, you could try using an interceptor to fix the data upon insert / update, like:

package interceptor;

import java.io.Serializable;

import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;

public class CustomSaveInterceptor extends EmptyInterceptor {
    public boolean onSave(Object entity,
        Serializable id,
        Object[] state,
        String[] propertyNames,
        Type[] types)
    {
        if (entity instanceof MyClass) {
            MyClass myInstance = (MyClass)entity;
            myInstance.setName(myInstance.getName().toUpperCase());
        }
        return super.onSave(entity, id, state, propertyNames, types);
    }
}

Upvotes: 1

Related Questions