Deep @Embedded Hibernate entity hierarchy with Oracle

If you want to have a deep @Embeddable Hibernate entity hierarchy with Oracle – you are not welcome. Oracle has a genious unconfigurable (at least after a lot of search) limit for column names – 30 characters.
Hibernate (JPA’s actually) @Embedded and @Embeddable annotations provide the possibility to have a deep hierarcy of objects that will be represented in one table in the database. (The reasons to use such hierarchy is beyond the scope of this post.). Hibernate generates the names for the columns using all property names down the hierarcy. For instance: person_address_street_number But these names often get long and oracle says “identifier too long”. You can set @Column(name=”something”) at the bottom of the hierarchy, but it is not a rare case that one class, or one column name is found twice in the same entity (ergo table). So a duplicate column name problem appears.

The resolution, although not very beautiful, is to write a custom NamingStrategy which abbreviates the columns. A sample would look like this:

public class PrefixNamingStrategy extends DefaultComponentSafeNamingStrategy {

    private static final int MAX_ORACLE_ALLOWED_CHARS = 30;
    private static final int MAX_PART_LENGTH = 4;
    private static final int MAX_PART_SHORT_LENGTH = 3;

    @Override
    public String propertyToColumnName(String propertyName) {
        // Take the last column name before calling propertyToColumnName
        // in order to preserve casing
        String lastPropertyName = propertyName;
        if (lastPropertyName.indexOf(".") != -1) {
            lastPropertyName = lastPropertyName.substring(lastPropertyName.lastIndexOf(".") + 1);
        }

        // Getting the fully qualified, component-safe name of the column
        String columnName = super.propertyToColumnName(propertyName);

        if (columnName.length() <= MAX_ORACLE_ALLOWED_CHARS) {
            return columnName;
        }

        String result = shortenColumnName(columnName, MAX_PART_LENGTH, lastPropertyName,
                false);

        if (result.length() > MAX_ORACLE_ALLOWED_CHARS) {
            result = shortenColumnName(columnName, MAX_PART_SHORT_LENGTH,
                    lastPropertyName, true);
        }

        return result;
    }

    private String shortenColumnName(String columnName, int maxPartLength,
            String lastPropertyName, boolean shortenLast) {
        String[] parts = columnName.split("_");
        for (int i = 0; i < parts.length - 1; i++) {
            parts[i] = parts[i].substring(0, parts[i].length() < maxPartLength ? parts[i]
                    .length() : maxPartLength);
        }

        if (shortenLast) {
            parts[parts.length - 1] = getAbbreviation(lastPropertyName);
        }

        String result = "";
        for (int i = 0; i < parts.length; i++) {
            if (i != 0) {
                result += "_";
            }
            result += parts[i];
        }

        return result;
    }

    private String getAbbreviation(String string) {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < string.length() - 1; i++) {
            if (Character.isUpperCase(string.charAt(i)) || i == 0) {
                buf.append(string.substring(i, i + MAX_PART_SHORT_LENGTH));
            }
        }

        return buf.toString();
    }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: