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)
}
}
