Descriptive names for column constraints in Hibernate

Hibernate (re-)generates names for database constraints such as foreign keys or unique columns when creating a schema. However, the names for indexes and unique keys are not very descriptive, since they are created from a hash string of the table and column name.

Unreadable technical names may seem just to be an insignificant ugliness, but can become quite cumbersome when you are searching error logs for database problems…

Hibernate by default generates names like uk_dw7cde94np3e3gt2991uoamtp for unique columns as annotated in the entity class:

@NaturalId
@Column(name = "CORRELATION_ID", nullable = false, unique = true, updatable = false, length = 100)
var correlationId = createUniqueId()

When an error occurs while saving some object, the message

2023-11-27 03:48:38,534 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (Thread-5 (ActiveMQ-client-global-threads)) ERROR: duplicate key value violates unique constraint "uk_ironb5nnnpm7agy59dt5jxxxp"

is not really helpful. Whereas the name “uk_taskheader_correlationid” would tell you directly which table and column caused the problem here.

Modifying constraint names

For tables and other named entities, Hibernate allows to specify strategies in order to determine the generated names, e.g. ImplicitNamingStrategy. Alas, there is no such configurable strategy for auto-generated constraints. Even worse, the name generation algorithm is hard-coded.

As a remedy, you could for example modify Hibernates meta model of the schema. Yet what we found most feasible is to to adjust the constraint names just before they are created in the database. We will use an enhanced dialect in the persistence.xml file to accomplish that:

...
<property name="hibernate.dialect" value="de.akquinet.MY_H2Dialect"/>

Where the dialect just overrides a single method returning a new exporter implementation that handles the enhanced constraint names.

class MY_H2Dialect : H2Dialect() {
  override fun getUniqueKeyExporter() = MY_UniqueKeyExporter(this)
}
class MY_UniqueKeyExporter(dialect: Dialect) : StandardUniqueKeyExporter(dialect) {
  override fun getSqlCreateStrings(constraint: Constraint,
      metadata: Metadata): Array<String> {
    val table = constraint.table
    val newConstraint = if (constraint.columns.size == 1) {
      val column = constraint.columnIterator.next()
      val betterKeyName = "UK_" + table.name + "_" + column.name

      UniqueKey().apply {
        this.table = table
        name = betterKeyName
        addColumn(column)
      }
    } else {
      constraint
    }

    return super.getSqlCreateStrings(newConstraint, metadata)
  }
}

Discover more from Spree-Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading

Cookie Consent Banner by Real Cookie Banner