Skip to content

Commit f3a867d

Browse files
HHH-14584 Allow PhysicalNamingStrategy implementations to detect when a name is implicit or explicit
1 parent 6bbdf83 commit f3a867d

File tree

10 files changed

+171
-12
lines changed

10 files changed

+171
-12
lines changed

hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -917,15 +917,20 @@ public Table addTable(
917917
String name,
918918
String subselectFragment,
919919
boolean isAbstract,
920-
MetadataBuildingContext buildingContext) {
920+
MetadataBuildingContext buildingContext,
921+
boolean isExplicit) {
921922
final Namespace namespace = getDatabase().locateNamespace(
922923
getDatabase().toIdentifier( catalogName ),
923924
getDatabase().toIdentifier( schemaName )
924925
);
925926

926927
// annotation binding depends on the "table name" for @Subselect bindings
927928
// being set into the generated table (mainly to avoid later NPE), but for now we need to keep that :(
928-
final Identifier logicalName = name != null ? getDatabase().toIdentifier( name ) : null;
929+
Identifier logicalName = null;
930+
if ( name != null ) {
931+
logicalName = getDatabase().toIdentifier( name );
932+
logicalName.setExplicit( isExplicit );
933+
}
929934

930935
if ( subselectFragment != null ) {
931936
return new Table( buildingContext.getCurrentContributorName(),

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,10 @@ public boolean isNameDeferred() {
358358
* @return {@code true} if a name could be inferred
359359
*/
360360
boolean inferColumnNameIfPossible(String columnName, String propertyName, boolean applyNamingStrategy) {
361-
if ( !isEmpty( columnName ) || !isEmpty( propertyName ) ) {
361+
if ( isNotEmpty( columnName ) || isNotEmpty( propertyName ) ) {
362362
final String logicalColumnName = resolveLogicalColumnName( columnName, propertyName );
363-
mappingColumn.setName( processColumnName( logicalColumnName, applyNamingStrategy ) );
363+
mappingColumn.setName(
364+
processColumnName( logicalColumnName, applyNamingStrategy, isNotEmpty( columnName ) ) );
364365
return true;
365366
}
366367
else {
@@ -411,11 +412,13 @@ private String applyEmbeddedColumnNaming(String inferredColumnName, ComponentPro
411412
return result;
412413
}
413414

414-
protected String processColumnName(String columnName, boolean applyNamingStrategy) {
415+
protected String processColumnName(String columnName, boolean applyNamingStrategy, boolean isExplicit) {
415416
if ( applyNamingStrategy ) {
416417
final Database database = getDatabase();
418+
Identifier identifier = database.toIdentifier( columnName );
419+
identifier.setExplicit( isExplicit );
417420
return getPhysicalNamingStrategy()
418-
.toPhysicalColumnName( database.toIdentifier( columnName ), database.getJdbcEnvironment() )
421+
.toPhysicalColumnName( identifier, database.getJdbcEnvironment() )
419422
.render( database.getDialect() );
420423
}
421424
else {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,8 @@ public void overrideFromReferencedColumnIfNecessary(Column column) {
454454
@Override
455455
boolean inferColumnNameIfPossible(String columnName, String propertyName, boolean applyNamingStrategy) {
456456
if ( isNotEmpty( columnName ) ) {
457-
getMappingColumn().setName( processColumnName( columnName, applyNamingStrategy ) );
457+
getMappingColumn().setName(
458+
processColumnName( columnName, applyNamingStrategy, isNotEmpty( columnName ) ) );
458459
return true;
459460
}
460461
else {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1773,7 +1773,10 @@ public MetadataBuildingContext getBuildingContext() {
17731773

17741774
@Override
17751775
public Identifier handleExplicitName(String explicitName, MetadataBuildingContext buildingContext) {
1776-
return jdbcEnvironment( buildingContext ).getIdentifierHelper().toIdentifier( explicitName );
1776+
Identifier identifier = jdbcEnvironment( buildingContext ).getIdentifierHelper()
1777+
.toIdentifier( explicitName );
1778+
identifier.setExplicit( true );
1779+
return identifier;
17771780
}
17781781

17791782
@Override

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,8 @@ private static Table addTable(
541541
logicalName.render(),
542542
subselect,
543543
isAbstract,
544-
buildingContext
544+
buildingContext,
545+
logicalName.isExplicit()
545546
);
546547
}
547548
}

hibernate-core/src/main/java/org/hibernate/boot/model/naming/Identifier.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
public class Identifier implements Comparable<Identifier> {
2424
private final String text;
2525
private final boolean isQuoted;
26+
private boolean isExplicit;
2627

2728
/**
2829
* Means to generate an {@link Identifier} instance from its simple text form.
@@ -286,4 +287,12 @@ public static Identifier quote(Identifier identifier) {
286287
public int compareTo(Identifier identifier) {
287288
return getCanonicalName().compareTo( identifier.getCanonicalName() );
288289
}
290+
291+
public boolean isExplicit() {
292+
return isExplicit;
293+
}
294+
295+
public void setExplicit(boolean isExplicit) {
296+
this.isExplicit = isExplicit;
297+
}
289298
}

hibernate-core/src/main/java/org/hibernate/boot/spi/InFlightMetadataCollector.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ Table addTable(
150150
String name,
151151
String subselect,
152152
boolean isAbstract,
153-
MetadataBuildingContext buildingContext);
153+
MetadataBuildingContext buildingContext,
154+
boolean isExplicit);
154155

155156
/**
156157
* Adds a 'denormalized table' to this repository.

hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,10 +352,12 @@ private static QualifiedName sequenceName(
352352
IdentifierHelper identifierHelper) {
353353
if ( isNotEmpty( explicitSequenceName ) ) {
354354
// we have an explicit name, use it
355-
return explicitSequenceName.contains(".")
355+
QualifiedNameParser.NameParts nameParts = explicitSequenceName.contains( "." )
356356
? QualifiedNameParser.INSTANCE.parse( explicitSequenceName )
357357
: new QualifiedNameParser.NameParts( catalog, schema,
358358
identifierHelper.toIdentifier( explicitSequenceName ) );
359+
nameParts.getObjectName().setExplicit( true );
360+
return nameParts;
359361
}
360362
else {
361363
// otherwise, determine an implicit name to use
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.associations;
6+
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.GeneratedValue;
10+
import jakarta.persistence.Id;
11+
import jakarta.persistence.SequenceGenerator;
12+
import jakarta.persistence.Table;
13+
import org.hibernate.boot.model.naming.Identifier;
14+
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
15+
import org.hibernate.cfg.AvailableSettings;
16+
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
17+
import org.hibernate.testing.orm.junit.DomainModel;
18+
import org.hibernate.testing.orm.junit.JiraKey;
19+
import org.hibernate.testing.orm.junit.ServiceRegistry;
20+
import org.hibernate.testing.orm.junit.SessionFactory;
21+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
22+
import org.hibernate.testing.orm.junit.Setting;
23+
import org.junit.jupiter.api.AfterAll;
24+
import org.junit.jupiter.api.Test;
25+
26+
import static jakarta.persistence.GenerationType.SEQUENCE;
27+
import static org.junit.jupiter.api.Assertions.assertEquals;
28+
29+
@SessionFactory
30+
@DomainModel(annotatedClasses = {
31+
ExplicitIdentifierTest.PersonExplicit.class,
32+
ExplicitIdentifierTest.PersonImplicit.class
33+
})
34+
@ServiceRegistry(
35+
settings = {
36+
@Setting(name = AvailableSettings.PHYSICAL_NAMING_STRATEGY,
37+
value = "org.hibernate.orm.test.associations.ExplicitIdentifierTest$CustomPhysicalNamingStrategy")
38+
}
39+
)
40+
@JiraKey("HHH-14584")
41+
public class ExplicitIdentifierTest {
42+
43+
@Test
44+
void testExplicitIdentifier(SessionFactoryScope scope) {
45+
46+
scope.inTransaction( session -> {
47+
PersonImplicit personImplicit = new PersonImplicit();
48+
personImplicit.name = "implicit";
49+
session.persist( personImplicit );
50+
51+
PersonExplicit personExplicit = new PersonExplicit();
52+
personExplicit.name = "explicit";
53+
session.persist( personExplicit );
54+
} );
55+
56+
scope.inTransaction( session -> {
57+
58+
// implicit named objects have a prefix
59+
assertEquals( "implicit", session.createNativeQuery(
60+
"select COLUMN_name from TABLE_ExplicitIdentifierTest$PersonImplicit",
61+
String.class )
62+
.getSingleResult() );
63+
assertEquals( 51, session.createNativeQuery(
64+
"select nextval('SEQUENCE_TABLE_ExplicitIdentifierTest$PersonImplicit_SEQ')",
65+
Integer.class ).getSingleResult() );
66+
67+
// explicit named object don't have a prefix
68+
assertEquals( "explicit", session.createNativeQuery(
69+
"select name from PersonExplicit",
70+
String.class )
71+
.getSingleResult() );
72+
assertEquals( 51, session.createNativeQuery(
73+
"select nextval('person_explicit_seq')",
74+
Integer.class ).getSingleResult() );
75+
} );
76+
}
77+
78+
@AfterAll
79+
public void dropTestData(SessionFactoryScope scope) {
80+
scope.dropData();
81+
}
82+
83+
@Entity
84+
public static class PersonImplicit {
85+
86+
@Id
87+
@GeneratedValue
88+
long id;
89+
90+
@Column
91+
String name;
92+
}
93+
94+
@Entity
95+
@Table(name = "PersonExplicit")
96+
public static class PersonExplicit {
97+
98+
@Id
99+
@GeneratedValue(
100+
strategy = SEQUENCE,
101+
generator = "person_sequence"
102+
)
103+
@SequenceGenerator(name = "person_sequence", sequenceName = "person_explicit_seq")
104+
long id;
105+
106+
@Column(name = "name")
107+
String name;
108+
}
109+
110+
public static class CustomPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {
111+
112+
@Override
113+
public Identifier toPhysicalTableName(Identifier logicalName, JdbcEnvironment jdbcEnvironment) {
114+
return logicalName.isExplicit() ? logicalName
115+
: jdbcEnvironment.getIdentifierHelper().toIdentifier( "TABLE_" + logicalName,
116+
logicalName.isQuoted() );
117+
}
118+
119+
@Override
120+
public Identifier toPhysicalSequenceName(Identifier logicalName, JdbcEnvironment jdbcEnvironment) {
121+
return logicalName.isExplicit() ? logicalName
122+
: jdbcEnvironment.getIdentifierHelper().toIdentifier( "SEQUENCE_" + logicalName,
123+
logicalName.isQuoted() );
124+
}
125+
126+
@Override
127+
public Identifier toPhysicalColumnName(Identifier logicalName, JdbcEnvironment jdbcEnvironment) {
128+
return logicalName.isExplicit() ? logicalName
129+
: jdbcEnvironment.getIdentifierHelper().toIdentifier( "COLUMN_" + logicalName,
130+
logicalName.isQuoted() );
131+
}
132+
}
133+
}

hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1247,7 +1247,8 @@ public Table addTable(
12471247
String name,
12481248
String subselect,
12491249
boolean isAbstract,
1250-
MetadataBuildingContext buildingContext) {
1250+
MetadataBuildingContext buildingContext,
1251+
boolean isExplicit) {
12511252
return null;
12521253
}
12531254

0 commit comments

Comments
 (0)