When externally managing the database schema (e.g. with Liquibase), in addition to specifying the Liquibase changesets, you have to help Hibernate understand your data structure using JPA annotations.
While some annotations concern higher level abstractions of the underlying data like @Embedded
or @OneToMany
, other lower level annotations, such as @Column(length = 255, nullable = false)
, seem only to represent what is already defined in the underlying database schema (varchar(255) not null
). Therefore it feels redundant to specify both, and it raises several questions for which I couldn’t find a clear answer in the docs:
[main question] Which @Column
settings (besides name
, insertable
and updatable
) are only used for DDL creation and could therefore be safely omitted if the database schema is managed externally?
My guess would be: columnDefinition
, length
, precision
and scale
, but I’m unsure if Hibernate makes some other internal use of those. What about nullable
and unique
, does Hibernate take these into account for example to optimize queries, or could those be left out as well?
As a test, I tried to write null to a non-nullable database column, and the only difference in specifying
@Column(nullable = false)
was that the exception would originate from theEntityManager
instead of the database driver, which wouldn’t matter much to the application (edit: assuming it is an exception case).
[side question 1] Does Hibernate assume uniqueness and non-nullability on single-column primary keys so the @Column(unique = true, nullable = false)
settings could be omitted on properties that also have @Id
?
[side question 2] Since Hibernate is capable of inspecting the database schema (e.g. when using hbm2ddl.auto=validate
), can it be configured to extract the type and constraint information it needs from the underlying schema so that settings in @Column
could be omitted?
Advertisement
Answer
Hibernate does make use of some of the annotation members, especially as of version 6. I would suggest you to look into a tool like JPABuddy which can generate Liquibase changesets based on diffs between your entity and liquibase model. In the past, I implemented such a diffing in a JUnit test. The test creates two databases, one through hbm2ddl and one through the liquibase model. Finally, it diffs the two and if there is a difference, fails the test, reporting the XML serialized form of the diff.
This way, both models stay in sync easily and you also don’t need hbm2ddl validate.
Here you can see the runtime model receiving information like length
, precision
, scale
and columnDefinition
: https://github.com/hibernate/hibernate-orm/blob/6.1.3/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java#L262
And here you can see this information being used: https://github.com/hibernate/hibernate-orm/blob/6.1.3/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java#L3629
This particular use is for casting parameters that infer the type of a mapping attribute i.e. in a query like ... where alias.attribute = :param
. Some databases might require that the parameter marker is wrapped by a cast in SQL under certain circumstances.
So this was just a small example of how Hibernate already uses some of this information. You’ll probably find some other uses if you study the Hibernate code.