122 lines
9.6 KiB
Markdown
Executable File
122 lines
9.6 KiB
Markdown
Executable File
# 04 — Annotations vs. XML mappings in Hibernate 7.4.5 — what actually still works
|
|
|
|
[← Previous: 03 — Inserting objects](03-inserting-objects.md) | [Next: 05 — JPA persistence annotations →](05-jpa-persistence-annotations.md)
|
|
|
|
Backs [ankurm.com: Hibernate 7 annotations vs. XML mappings](https://ankurm.com/hibernate-annotations-vs-xml-mappings-making-the-right-choice-in-hibernate-7/).
|
|
|
|
Post 4863 frames this as a two-way choice: annotations vs. `orm.xml`, with `hbm.xml` waved off
|
|
as "deprecated." That framing undersells what actually happens when you boot Hibernate
|
|
7.4.5.Final with each of these on the classpath, and it misses that there are really **three**
|
|
XML dialects in play, not two. Everything below was booted, not read about — see
|
|
[`docs/output/mappingstyle-xml-vs-annotations.txt`](output/mappingstyle-xml-vs-annotations.txt) for the verbatim run, and the test classes
|
|
under [`src/test/java/com/ankurm/hibernatedemo/mappingstyle/`](../src/test/java/com/ankurm/hibernatedemo/mappingstyle/) for the exact setup of each case.
|
|
|
|
## `hbm.xml` is not dead — it is a live code path with a warning label
|
|
|
|
The Hibernate 6 story was: `hbm.xml` is deprecated, and `hibernate.transform_hbm_xml.enabled`
|
|
is a shim that rewrites it into the modern model at boot. That shim setting still exists in
|
|
7.4.5 (`org.hibernate.cfg.MappingSettings.TRANSFORM_HBM_XML`, and the whole
|
|
`org.hibernate.boot.jaxb.hbm.*` package plus an `HbmXmlTransformer` are present in
|
|
`hibernate-core-7.4.5.Final.jar`), but it is **not required** to use `hbm.xml`.
|
|
|
|
[`HbmXmlBootTest`](../src/test/java/com/ankurm/hibernatedemo/mappingstyle/HbmXmlBootTest.java) and [`HbmXmlRuntimeTest`](../src/test/java/com/ankurm/hibernatedemo/mappingstyle/HbmXmlRuntimeTest.java) put a real, annotation-free POJO
|
|
([`HbmEmployee`](../src/main/java/com/ankurm/hibernatedemo/mappingstyle/HbmEmployee.java)) on the classpath with only an [`.hbm.xml` mapping](../src/test/resources/com/ankurm/hibernatedemo/mappingstyle/HbmEmployee.hbm.xml), and boot a plain Hibernate
|
|
`SessionFactory` three ways:
|
|
|
|
- default settings (no `transform_hbm_xml.enabled` at all) — **boots successfully**, logs one
|
|
WARN: `HHH90000028: Support for <hibernate-mappings/> is deprecated ... migrate to orm.xml or
|
|
mapping.xml, or enable hibernate.transform_hbm_xml.enabled for on the fly transformation`
|
|
- `hibernate.transform_hbm_xml.enabled=true` — boots successfully, no observable behavior
|
|
difference for a simple mapping
|
|
- `hibernate.transform_hbm_xml.enabled=false` (explicit) — **also boots successfully**
|
|
|
|
`HbmXmlRuntimeTest` goes further: it actually persists and loads an `HbmEmployee` row through
|
|
the hbm.xml-only mapping, with the transform setting deliberately unset, and it round-trips
|
|
correctly. So the honest 7.4.5 status of `hbm.xml` is: **it still fully works, unmodified,
|
|
with a WARN-level deprecation notice** — not a shim you must opt into, not a hard failure, and
|
|
not silently broken. The "transform" setting appears to matter for hbm.xml features that no
|
|
longer have a native binding path in 7.x and must be rewritten into the modern model to be
|
|
understood at all; a plain `<class>`/`<id>`/`<property>` mapping like this one never needs it.
|
|
Treat any blog claim that `hbm.xml` "requires" the transform flag, or throws without it, as
|
|
wrong for 7.4.5 — it is a live code path. (Chapter 14 hits the same "orm.xml still works with
|
|
zero registration effort" theme from the named-query angle — see
|
|
[`14 — Named queries`](14-named-queries.md#named-queries-in-ormxml).)
|
|
|
|
## `orm.xml` really can define an entire entity, annotation-free
|
|
|
|
Post 4863's `orm.xml` example only *overrides* an already-annotated `Employee`. It never proves
|
|
`orm.xml` can carry a mapping on its own. [`OrmXmlMappingResourcesTest`](../src/test/java/com/ankurm/hibernatedemo/mappingstyle/OrmXmlMappingResourcesTest.java) does: [`OrmXmlOnlyEntity`](../src/main/java/com/ankurm/hibernatedemo/mappingstyle/OrmXmlOnlyEntity.java)
|
|
carries **zero** JPA/Hibernate annotations — not even `@Entity` — and its entire mapping lives
|
|
in [`orm-xml-only-mapping.xml`](../src/test/resources/orm-xml-only-mapping.xml). The Spring Boot wiring that makes this work is
|
|
`spring.jpa.mapping-resources` (confirmed via `javap` on
|
|
`org.springframework.boot.jpa.autoconfigure.JpaProperties` in `spring-boot-jpa-4.1.1.jar` —
|
|
note the package: Boot 4's autoconfigure split moved this off the old
|
|
`org.springframework.boot.autoconfigure.orm.jpa` path entirely). With that one property set,
|
|
Hibernate creates `xml_only_widgets`, and a JPQL query (`select o from OrmXmlOnlyEntity o ...`)
|
|
against it succeeds — the entity name resolves purely from the XML.
|
|
|
|
## XML wins on conflict — confirmed, not just documented
|
|
|
|
Post 4863's Q5 claims "XML always wins over annotations." [`AnnotationXmlOverrideTest`](../src/test/java/com/ankurm/hibernatedemo/mappingstyle/AnnotationXmlOverrideTest.java) proves it
|
|
mechanically: [`OverrideEntity`](../src/main/java/com/ankurm/hibernatedemo/mappingstyle/OverrideEntity.java)`.value` is annotated `@Column(name = "annotation_name")`, and a
|
|
matching [`orm-xml-override-mapping.xml`](../src/test/resources/orm-xml-override-mapping.xml) entry maps the same field to `xml_name`. Reading the runtime metamodel
|
|
(`SessionFactoryImplementor.getMappingMetamodel().getEntityDescriptor(...).getPropertyColumnNames(...)`)
|
|
confirms the live column name is `xml_name`, and a native query against that literal column
|
|
name returns the persisted value. The article's claim is correct.
|
|
|
|
## `<xml-mapping-metadata-complete/>` is not an override switch — it is an annotation kill switch
|
|
|
|
This is the sharpest correction. The natural assumption is that
|
|
`<persistence-unit-metadata><xml-mapping-metadata-complete/></persistence-unit-metadata>` means
|
|
"XML overrides annotations for the attributes XML mentions." [`XmlMappingMetadataCompleteTest`](../src/test/java/com/ankurm/hibernatedemo/mappingstyle/XmlMappingMetadataCompleteTest.java)
|
|
shows it is stronger than that: with [metadata-complete set](../src/test/resources/orm-xml-metadata-complete.xml), `OverrideEntity`'s `@Id
|
|
@GeneratedValue` on the `id` field is **entirely ignored**, even though the matching `orm.xml`
|
|
entry never mentions `id` at all. Boot fails with
|
|
`org.hibernate.AnnotationException: Entity '...OverrideEntity' has no identifier`. Metadata-complete
|
|
does not selectively override — it switches off annotation processing for the whole persistence
|
|
unit, and any attribute XML doesn't repeat is simply gone.
|
|
|
|
## The real "what XML can do that annotations can't" — a third XML dialect
|
|
|
|
Post 4863 never mentions this, and it changes the shape of the whole topic: Hibernate 7 ships a
|
|
**third** XML mapping format, distinct from both legacy `hbm.xml` and JPA-standard `orm.xml`.
|
|
It lives in `org/hibernate/xsd/mapping/mapping-7.0.xsd` inside `hibernate-core-7.4.5.Final.jar`,
|
|
under namespace `http://www.hibernate.org/xsd/orm/mapping`, and its own XSD documentation calls
|
|
it out explicitly: *"XSD which 'extends' the JPA orm.xml XSD adding support for Hibernate
|
|
specific features."* This is exactly the second migration target the `HHH90000028` deprecation
|
|
warning names ("migrate to orm.xml **or mapping.xml**").
|
|
|
|
The difference is not cosmetic. Grepping both schemas for Hibernate-only concepts:
|
|
|
|
| Element | `orm_3_2.xsd` (JPA-standard `orm.xml`) | `mapping-7.0.xsd` (Hibernate's native dialect) |
|
|
|---|---|---|
|
|
| `<natural-id>` | absent | present |
|
|
| `<formula>` / `<discriminator-formula>` / `<join-formula>` | absent | present |
|
|
| `<filter>`-related elements | absent | present (11 occurrences) |
|
|
|
|
[`MappingXmlNaturalIdTest`](../src/test/java/com/ankurm/hibernatedemo/mappingstyle/MappingXmlNaturalIdTest.java) proves this is not just schema noise: [`MappingXmlNaturalIdEntity`](../src/main/java/com/ankurm/hibernatedemo/mappingstyle/MappingXmlNaturalIdEntity.java) has
|
|
**zero** annotations, its `<natural-id>` is declared purely in [`mapping-xml-natural-id.xml`](../src/test/resources/mapping-xml-natural-id.xml)
|
|
(root element `<entity-mappings xmlns="http://www.hibernate.org/xsd/orm/mapping" version="7.0">`),
|
|
and `session.byNaturalId(...).using("sku", ...).load()` resolves it correctly. So the accurate
|
|
answer to "what can XML express that annotations can't" is actually inverted from how the
|
|
article poses it: the JPA-portable `orm.xml` dialect can express **less** than annotations
|
|
(no Hibernate extensions at all), while Hibernate's own `mapping.xml` dialect can express
|
|
**anything annotations can**, including natural ids, formulas, and filters — you trade JPA
|
|
portability for that power, not gain something unavailable to annotations. (Chapter 06 covers
|
|
`@NaturalId` from the annotation side; this is the same feature expressed purely in XML.)
|
|
|
|
## Practical takeaway
|
|
|
|
- Legacy `hbm.xml`: still boots and runs in 7.4.5, WARN-level deprecated, no forced migration.
|
|
- `orm.xml` (JPA-standard): fully capable of defining an entity from scratch; wired into Spring
|
|
Boot via `spring.jpa.mapping-resources`; portable across providers but capped at what
|
|
`orm_3_2.xsd` can express — no Hibernate-only concepts.
|
|
- Hibernate's native `mapping.xml` (`mapping-7.0.xsd`): the actual annotation-equivalent XML
|
|
dialect, including `@NaturalId`, `@Formula`, and filters — not portable to other JPA
|
|
providers, but not missing anything either.
|
|
- XML (either dialect) always wins over a conflicting annotation for the same attribute.
|
|
- `xml-mapping-metadata-complete` disables annotation processing for the *entire* persistence
|
|
unit, not just the attributes the XML repeats — a much bigger blast radius than "override."
|
|
|
|
[← Previous: 03 — Inserting objects](03-inserting-objects.md) | [Next: 05 — JPA persistence annotations →](05-jpa-persistence-annotations.md)
|