Skip to content

Commit 69e31d0

Browse files
committed
HHH-19257 - Introduce @EmbeddedTable
1 parent de54ca2 commit 69e31d0

File tree

10 files changed

+380
-6
lines changed

10 files changed

+380
-6
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.annotations;
6+
7+
import org.hibernate.Incubating;
8+
9+
import java.lang.annotation.Target;
10+
import java.lang.annotation.Retention;
11+
12+
import static java.lang.annotation.ElementType.FIELD;
13+
import static java.lang.annotation.ElementType.METHOD;
14+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
15+
16+
/**
17+
* Allows an easier mechanism to declare the table to which an embedded value
18+
* maps compared to the Jakarta Persistence compliant mechanism requiring
19+
* multiple {@link jakarta.persistence.AttributeOverride}
20+
* and {@link jakarta.persistence.AssociationOverride} annotations.
21+
* <pre>
22+
* &#64;Entity
23+
* &#64;Table(name="primary")
24+
* &#64;SecondaryTable(name="secondary")
25+
* class Person {
26+
* ...
27+
* &#64;Embedded
28+
* &#64;EmbeddedTable("secondary")
29+
* Address address;
30+
* }
31+
* </pre>
32+
*
33+
* @see EmbeddedColumnNaming
34+
*
35+
* @since 7.2
36+
* @author Steve Ebersole
37+
*/
38+
@Target({METHOD, FIELD})
39+
@Retention(RUNTIME)
40+
@Incubating
41+
public @interface EmbeddedTable {
42+
/**
43+
* The name of the table in which the embedded value is stored.
44+
*/
45+
String value();
46+
}

hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractPropertyHolder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
5353

5454
private final String path;
5555
protected final AbstractPropertyHolder parent;
56-
private final MetadataBuildingContext context;
56+
protected final MetadataBuildingContext context;
5757

5858
private Boolean isInIdClass;
5959

hibernate-core/src/main/java/org/hibernate/boot/model/internal/ComponentPropertyHolder.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
import org.checkerframework.checker.nullness.qual.Nullable;
1111
import org.hibernate.AnnotationException;
12+
import org.hibernate.annotations.EmbeddedTable;
13+
import org.hibernate.boot.model.naming.Identifier;
14+
import org.hibernate.boot.spi.InFlightMetadataCollector;
1215
import org.hibernate.boot.spi.MetadataBuildingContext;
1316
import org.hibernate.boot.spi.PropertyData;
1417
import org.hibernate.mapping.AggregateColumn;
@@ -83,6 +86,8 @@ public ComponentPropertyHolder(
8386
this.component = component;
8487
this.inheritanceStatePerClass = inheritanceStatePerClass;
8588

89+
applyExplicitTableName( component, inferredData, parent, context );
90+
8691
isOrWithinEmbeddedId = parent.isOrWithinEmbeddedId()
8792
|| embeddedMemberDetails != null && hasIdAnnotation( embeddedMemberDetails );
8893
isWithinElementCollection = parent.isWithinElementCollection()
@@ -98,6 +103,41 @@ public ComponentPropertyHolder(
98103
}
99104
}
100105

106+
/**
107+
* Apply the explicit {@link EmbeddedTable} if there is one and if its
108+
* appropriate for the context (the type of {@code container}).
109+
*
110+
* @param component The (in-flight) component mapping details.
111+
* @param propertyData Details about the property defining this component.
112+
* @param container The container for this component.
113+
*/
114+
public static void applyExplicitTableName(
115+
Component component,
116+
PropertyData propertyData,
117+
PropertyHolder container,
118+
MetadataBuildingContext buildingContext) {
119+
Table tableToUse = container.getTable();
120+
boolean wasExplicit = false;
121+
if ( container instanceof ComponentPropertyHolder componentPropertyHolder ) {
122+
wasExplicit = componentPropertyHolder.getComponent().wasTableExplicitlyDefined();
123+
}
124+
125+
// we only allow this when done for an embedded on an entity or mapped-superclass
126+
if ( propertyData.getAttributeMember() != null && container instanceof ClassPropertyHolder ) {
127+
final EmbeddedTable embeddedTableAnn = propertyData.getAttributeMember().getDirectAnnotationUsage( EmbeddedTable.class );
128+
if ( embeddedTableAnn != null ) {
129+
final Identifier tableNameIdentifier = buildingContext.getObjectNameNormalizer().normalizeIdentifierQuoting( embeddedTableAnn.value() );
130+
final InFlightMetadataCollector.EntityTableXref entityTableXref = buildingContext
131+
.getMetadataCollector()
132+
.getEntityTableXref( container.getEntityName() );
133+
tableToUse = entityTableXref.resolveTable( tableNameIdentifier );
134+
wasExplicit = true;
135+
}
136+
}
137+
138+
component.setTable( tableToUse, wasExplicit );
139+
}
140+
101141
/**
102142
* Access to the underlying component
103143
*/

hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -951,16 +951,16 @@ private static String canonicalize(String typeName) {
951951
static Component createEmbeddable(
952952
PropertyHolder propertyHolder,
953953
PropertyData inferredData,
954-
boolean isComponentEmbedded,
954+
boolean isNonAggregated,
955955
boolean isIdentifierMapper,
956956
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
957957
MetadataBuildingContext context) {
958958
final var embeddable = new Component( context, propertyHolder.getPersistentClass() );
959-
embeddable.setEmbedded( isComponentEmbedded );
960-
//yuk
961-
embeddable.setTable( propertyHolder.getTable() );
959+
embeddable.setEmbedded( isNonAggregated );
960+
ComponentPropertyHolder.applyExplicitTableName( embeddable, inferredData, propertyHolder, context );
961+
962962
if ( isIdentifierMapper
963-
|| isComponentEmbedded && inferredData.getPropertyName() == null ) {
963+
|| isNonAggregated && inferredData.getPropertyName() == null ) {
964964
embeddable.setComponentClassName( embeddable.getOwner().getClassName() );
965965
}
966966
else {

hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,13 @@ private Property makePropertyAndValue() {
269269
basicValueBinder.setReferencedEntityName( referencedEntityName );
270270
basicValueBinder.setAccessType( accessType );
271271

272+
if ( holder instanceof ComponentPropertyHolder embeddableTypedContainer ) {
273+
final Component component = embeddableTypedContainer.getComponent();
274+
if ( component.wasTableExplicitlyDefined() ) {
275+
basicValueBinder.setTable( component.getTable() );
276+
}
277+
}
278+
272279
value = basicValueBinder.make();
273280

274281
return makeProperty();

hibernate-core/src/main/java/org/hibernate/boot/models/HibernateAnnotations.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ public interface HibernateAnnotations {
248248
EmbeddedColumnNaming.class,
249249
EmbeddedColumnNamingAnnotation.class
250250
);
251+
OrmAnnotationDescriptor<EmbeddedTable,EmbeddedTableAnnotation> EMBEDDED_TABLE = new OrmAnnotationDescriptor<>(
252+
EmbeddedTable.class,
253+
EmbeddedTableAnnotation.class
254+
);
251255
OrmAnnotationDescriptor<Fetch,FetchAnnotation> FETCH = new OrmAnnotationDescriptor<>(
252256
Fetch.class,
253257
FetchAnnotation.class
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.boot.models.annotations.internal;
6+
7+
import org.hibernate.annotations.EmbeddedTable;
8+
import org.hibernate.models.spi.ModelsContext;
9+
10+
import java.lang.annotation.Annotation;
11+
import java.util.Map;
12+
13+
/**
14+
* @author Steve Ebersole
15+
*/
16+
@SuppressWarnings({ "ClassExplicitlyAnnotation", "unused" })
17+
public class EmbeddedTableAnnotation implements EmbeddedTable {
18+
private String value;
19+
20+
/**
21+
* Used in creating dynamic annotation instances (e.g. from XML)
22+
*/
23+
public EmbeddedTableAnnotation(ModelsContext modelContext) {
24+
}
25+
26+
/**
27+
* Used in creating annotation instances from JDK variant
28+
*/
29+
public EmbeddedTableAnnotation(
30+
EmbeddedTable annotation,
31+
ModelsContext modelContext) {
32+
this.value = annotation.value();
33+
}
34+
35+
/**
36+
* Used in creating annotation instances from Jandex variant
37+
*/
38+
public EmbeddedTableAnnotation(
39+
Map<String, Object> attributeValues,
40+
ModelsContext modelContext) {
41+
this.value = (String) attributeValues.get( "value" );
42+
}
43+
44+
@Override
45+
public Class<? extends Annotation> annotationType() {
46+
return EmbeddedTable.class;
47+
}
48+
49+
@Override
50+
public String value() {
51+
return value;
52+
}
53+
54+
public void value(String value) {
55+
this.value = value;
56+
}
57+
}

hibernate-core/src/main/java/org/hibernate/mapping/Component.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ public class Component extends SimpleValue implements AttributeContainer, MetaAt
100100
private transient Boolean simpleRecord;
101101
private String columnNamingPattern;
102102

103+
private boolean tableWasExplicit;
104+
103105
public Component(MetadataBuildingContext metadata, PersistentClass owner) throws MappingException {
104106
this( metadata, owner.getTable(), owner );
105107
}
@@ -158,6 +160,23 @@ public List<Property> getProperties() {
158160
return properties;
159161
}
160162

163+
public void setTable(Table table) {
164+
if ( !tableWasExplicit ) {
165+
super.setTable( table );
166+
}
167+
168+
// otherwise, ignore it...
169+
}
170+
171+
public void setTable(Table table, boolean wasExplicit) {
172+
super.setTable( table );
173+
tableWasExplicit = wasExplicit;
174+
}
175+
176+
public boolean wasTableExplicitlyDefined() {
177+
return tableWasExplicit;
178+
}
179+
161180
public void addProperty(Property p, ClassDetails declaringClass) {
162181
properties.add( p );
163182
if ( isPolymorphic() && declaringClass != null ) {

0 commit comments

Comments
 (0)