HHH-14253 support ConstraintMode.PROVIDER_DEFAULT
Provide an option to skip foreign key creation for ConstraintMode.PROVIDER_DEFAULT
This commit is contained in:
parent
8d4de09255
commit
88acc9511b
|
@ -904,6 +904,9 @@ If enabled, allows schema update and validation to support synonyms. Due to the
|
|||
`*hibernate.hbm2ddl.extra_physical_table_types*` (e.g. `BASE TABLE`)::
|
||||
Identifies a comma-separated list of values to specify extra table types, other than the default `TABLE` value, to recognize as defining a physical table by schema update, creation and validation.
|
||||
|
||||
`*hibernate.hbm2ddl.default_constraint_mode*` (`CONSTRAINT` (default value) or `NO_CONSTRAINT`)::
|
||||
Default `javax.persistence.ConstraintMode` for foreign key mapping if `PROVIDER_DEFAULT` strategy used.
|
||||
|
||||
`*hibernate.schema_update.unique_constraint_strategy*` (e.g. `DROP_RECREATE_QUIETLY`, `RECREATE_QUIETLY`, `SKIP`)::
|
||||
Unique columns and unique keys both use unique constraints in most dialects.
|
||||
`SchemaUpdate` needs to create these constraints, but DBs support for finding existing constraints is extremely inconsistent.
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.ConstraintMode;
|
||||
import javax.persistence.SharedCacheMode;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -330,6 +331,11 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
return this;
|
||||
}
|
||||
|
||||
public MetadataBuilder noConstraintByDefault() {
|
||||
this.options.noConstraintByDefault = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataBuilder applySqlFunction(String functionName, SQLFunction function) {
|
||||
this.bootstrapContext.addSqlFunction( functionName, function );
|
||||
|
@ -610,6 +616,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
private boolean implicitlyForceDiscriminatorInSelect;
|
||||
private boolean useNationalizedCharacterData;
|
||||
private boolean specjProprietarySyntaxEnabled;
|
||||
private boolean noConstraintByDefault;
|
||||
private ArrayList<MetadataSourceType> sourceProcessOrdering;
|
||||
|
||||
private IdGeneratorInterpreterImpl idGenerationTypeInterpreter = new IdGeneratorInterpreterImpl();
|
||||
|
@ -702,6 +709,12 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
false
|
||||
);
|
||||
|
||||
this.noConstraintByDefault = ConstraintMode.NO_CONSTRAINT.name().equalsIgnoreCase( configService.getSetting(
|
||||
AvailableSettings.HBM2DDL_DEFAULT_CONSTRAINT_MODE,
|
||||
String.class,
|
||||
null
|
||||
) );
|
||||
|
||||
this.implicitNamingStrategy = strategySelector.resolveDefaultableStrategy(
|
||||
ImplicitNamingStrategy.class,
|
||||
configService.getSettings().get( AvailableSettings.IMPLICIT_NAMING_STRATEGY ),
|
||||
|
@ -882,6 +895,11 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
return specjProprietarySyntaxEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoConstraintByDefault() {
|
||||
return noConstraintByDefault;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetadataSourceType> getSourceProcessOrdering() {
|
||||
return sourceProcessOrdering;
|
||||
|
|
|
@ -158,6 +158,11 @@ public abstract class AbstractDelegatingMetadataBuildingOptions implements Metad
|
|||
return delegate.isSpecjProprietarySyntaxEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoConstraintByDefault() {
|
||||
return delegate.isNoConstraintByDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetadataSourceType> getSourceProcessOrdering() {
|
||||
return delegate.getSourceProcessOrdering();
|
||||
|
|
|
@ -230,6 +230,16 @@ public interface MetadataBuildingOptions {
|
|||
|
||||
boolean isSpecjProprietarySyntaxEnabled();
|
||||
|
||||
/**
|
||||
* Should we create constraint by default?
|
||||
*
|
||||
* @see javax.persistence.ConstraintMode#PROVIDER_DEFAULT
|
||||
* @see org.hibernate.cfg.AvailableSettings#DEFAULT_CONSTRAINT_MODE
|
||||
*
|
||||
* @return {@code true} if not create constraint by default; {@code false} otherwise.
|
||||
*/
|
||||
boolean isNoConstraintByDefault();
|
||||
|
||||
/**
|
||||
* Retrieve the ordering in which sources should be processed.
|
||||
*
|
||||
|
|
|
@ -146,6 +146,8 @@ import org.hibernate.cfg.annotations.QueryBinder;
|
|||
import org.hibernate.cfg.annotations.SimpleValueBinder;
|
||||
import org.hibernate.cfg.annotations.TableBinder;
|
||||
import org.hibernate.engine.OptimisticLockStyle;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.config.spi.StandardConverters;
|
||||
import org.hibernate.engine.spi.FilterDefinition;
|
||||
import org.hibernate.id.PersistentIdentifierGenerator;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
@ -707,15 +709,17 @@ public final class AnnotationBinder {
|
|||
else {
|
||||
final PrimaryKeyJoinColumn pkJoinColumn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class );
|
||||
final PrimaryKeyJoinColumns pkJoinColumns = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class );
|
||||
|
||||
if ( pkJoinColumns != null && pkJoinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault();
|
||||
if ( pkJoinColumns != null && ( pkJoinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| pkJoinColumns.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) {
|
||||
// don't apply a constraint based on ConstraintMode
|
||||
key.setForeignKeyName( "none" );
|
||||
}
|
||||
else if ( pkJoinColumns != null && !StringHelper.isEmpty( pkJoinColumns.foreignKey().name() ) ) {
|
||||
key.setForeignKeyName( pkJoinColumns.foreignKey().name() );
|
||||
}
|
||||
else if ( pkJoinColumn != null && pkJoinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
else if ( pkJoinColumn != null && ( pkJoinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| pkJoinColumn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault )) {
|
||||
// don't apply a constraint based on ConstraintMode
|
||||
key.setForeignKeyName( "none" );
|
||||
}
|
||||
|
@ -3093,7 +3097,8 @@ public final class AnnotationBinder {
|
|||
property,
|
||||
propertyHolder.getOverriddenForeignKey( StringHelper.qualify( propertyHolder.getPath(), propertyName ) ),
|
||||
joinColumn,
|
||||
joinColumns
|
||||
joinColumns,
|
||||
context
|
||||
);
|
||||
|
||||
String path = propertyHolder.getPath() + "." + propertyName;
|
||||
|
@ -3439,9 +3444,13 @@ public final class AnnotationBinder {
|
|||
XProperty property,
|
||||
javax.persistence.ForeignKey fkOverride,
|
||||
JoinColumn joinColumn,
|
||||
JoinColumns joinColumns) {
|
||||
if ( ( joinColumn != null && joinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT )
|
||||
|| ( joinColumns != null && joinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) ) {
|
||||
JoinColumns joinColumns,
|
||||
MetadataBuildingContext context) {
|
||||
final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault();
|
||||
if ( ( joinColumn != null && ( joinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| joinColumn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) )
|
||||
|| ( joinColumns != null && ( joinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| joinColumns.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) ) {
|
||||
value.setForeignKeyName( "none" );
|
||||
}
|
||||
else {
|
||||
|
@ -3450,7 +3459,8 @@ public final class AnnotationBinder {
|
|||
value.setForeignKeyName( fk.name() );
|
||||
}
|
||||
else {
|
||||
if ( fkOverride != null && fkOverride.value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
if ( fkOverride != null && ( fkOverride.value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| fkOverride.value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) {
|
||||
value.setForeignKeyName( "none" );
|
||||
}
|
||||
else if ( fkOverride != null ) {
|
||||
|
|
|
@ -1529,6 +1529,19 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||
*/
|
||||
String HBM2DDL_HALT_ON_ERROR = "hibernate.hbm2ddl.halt_on_error";
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This setting is used when you use {@link javax.persistence.ConstraintMode#PROVIDER_DEFAULT} strategy for foreign key mapping.
|
||||
* valid value is {@code CONSTRAINT} and {@code NO_CONSTRAINT}.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value is CONSTRAINT.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
String HBM2DDL_DEFAULT_CONSTRAINT_MODE = "hibernate.hbm2ddl.default_constraint_mode";
|
||||
|
||||
String JMX_ENABLED = "hibernate.jmx.enabled";
|
||||
String JMX_PLATFORM_SERVER = "hibernate.jmx.usePlatformServer";
|
||||
String JMX_AGENT_ID = "hibernate.jmx.agentId";
|
||||
|
|
|
@ -103,7 +103,8 @@ public class OneToOneSecondPass implements SecondPass {
|
|||
inferredData.getProperty(),
|
||||
inferredData.getProperty().getAnnotation( javax.persistence.ForeignKey.class ),
|
||||
inferredData.getProperty().getAnnotation( JoinColumn.class ),
|
||||
inferredData.getProperty().getAnnotation( JoinColumns.class )
|
||||
inferredData.getProperty().getAnnotation( JoinColumns.class),
|
||||
buildingContext
|
||||
);
|
||||
|
||||
PropertyBinder binder = new PropertyBinder();
|
||||
|
@ -275,7 +276,8 @@ public class OneToOneSecondPass implements SecondPass {
|
|||
* Note:<br/>
|
||||
* <ul>
|
||||
* <li>From the mappedBy side we should not create the PK nor the FK, this is handled from the other side.</li>
|
||||
* <li>This method is a dirty dupe of EntityBinder.bindSecondaryTable</i>.
|
||||
* <li>This method is a dirty dupe of EntityBinder.bindSecondaryTable</li>.
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
private Join buildJoinFromMappedBySide(PersistentClass persistentClass, Property otherSideProperty, Join originalJoin) {
|
||||
|
|
|
@ -32,7 +32,6 @@ import javax.persistence.OneToMany;
|
|||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.FetchMode;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.annotations.BatchSize;
|
||||
import org.hibernate.annotations.Cache;
|
||||
|
@ -89,11 +88,9 @@ import org.hibernate.cfg.PropertyHolderBuilder;
|
|||
import org.hibernate.cfg.PropertyInferredData;
|
||||
import org.hibernate.cfg.PropertyPreloadedData;
|
||||
import org.hibernate.cfg.SecondPass;
|
||||
import org.hibernate.criterion.Junction;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.mapping.Any;
|
||||
|
@ -1173,6 +1170,7 @@ public abstract class CollectionBinder {
|
|||
Collection collValue,
|
||||
Ejb3JoinColumn[] joinColumns,
|
||||
boolean cascadeDeleteEnabled,
|
||||
boolean noConstraintByDefault,
|
||||
XProperty property,
|
||||
PropertyHolder propertyHolder,
|
||||
MetadataBuildingContext buildingContext) {
|
||||
|
@ -1217,7 +1215,8 @@ public abstract class CollectionBinder {
|
|||
else {
|
||||
final CollectionTable collectionTableAnn = property.getAnnotation( CollectionTable.class );
|
||||
if ( collectionTableAnn != null ) {
|
||||
if ( collectionTableAnn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
if ( collectionTableAnn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| collectionTableAnn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
|
||||
key.setForeignKeyName( "none" );
|
||||
}
|
||||
else {
|
||||
|
@ -1248,7 +1247,8 @@ public abstract class CollectionBinder {
|
|||
foreignKeyValue = joinColumnAnn.foreignKey().value();
|
||||
}
|
||||
}
|
||||
if ( foreignKeyValue == ConstraintMode.NO_CONSTRAINT ) {
|
||||
if ( foreignKeyValue == ConstraintMode.NO_CONSTRAINT
|
||||
|| foreignKeyValue == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
|
||||
key.setForeignKeyName( "none" );
|
||||
}
|
||||
else {
|
||||
|
@ -1260,7 +1260,8 @@ public abstract class CollectionBinder {
|
|||
final javax.persistence.ForeignKey fkOverride = propertyHolder.getOverriddenForeignKey(
|
||||
StringHelper.qualify( propertyHolder.getPath(), property.getName() )
|
||||
);
|
||||
if ( fkOverride != null && fkOverride.value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
if ( fkOverride != null && ( fkOverride.value() == ConstraintMode.NO_CONSTRAINT ||
|
||||
fkOverride.value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) {
|
||||
key.setForeignKeyName( "none" );
|
||||
}
|
||||
else if ( fkOverride != null ) {
|
||||
|
@ -1270,7 +1271,8 @@ public abstract class CollectionBinder {
|
|||
else {
|
||||
final JoinColumn joinColumnAnn = property.getAnnotation( JoinColumn.class );
|
||||
if ( joinColumnAnn != null ) {
|
||||
if ( joinColumnAnn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
if ( joinColumnAnn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| joinColumnAnn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
|
||||
key.setForeignKeyName( "none" );
|
||||
}
|
||||
else {
|
||||
|
@ -1466,7 +1468,8 @@ public abstract class CollectionBinder {
|
|||
foreignKeyValue = joinColumnAnn.foreignKey().value();
|
||||
}
|
||||
}
|
||||
if ( joinTableAnn.inverseForeignKey().value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
if ( joinTableAnn.inverseForeignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| joinTableAnn.inverseForeignKey().value() == ConstraintMode.PROVIDER_DEFAULT && buildingContext.getBuildingOptions().isNoConstraintByDefault() ) {
|
||||
element.setForeignKeyName( "none" );
|
||||
}
|
||||
else {
|
||||
|
@ -1702,7 +1705,8 @@ public abstract class CollectionBinder {
|
|||
catch (AnnotationException ex) {
|
||||
throw new AnnotationException( "Unable to map collection " + collValue.getOwner().getClassName() + "." + property.getName(), ex );
|
||||
}
|
||||
SimpleValue key = buildCollectionKey( collValue, joinColumns, cascadeDeleteEnabled, property, propertyHolder, buildingContext );
|
||||
SimpleValue key = buildCollectionKey( collValue, joinColumns, cascadeDeleteEnabled,
|
||||
buildingContext.getBuildingOptions().isNoConstraintByDefault(), property, propertyHolder, buildingContext );
|
||||
if ( property.isAnnotationPresent( ElementCollection.class ) && joinColumns.length > 0 ) {
|
||||
joinColumns[0].setJPA2ElementCollection( true );
|
||||
}
|
||||
|
|
|
@ -985,7 +985,9 @@ public class EntityBinder {
|
|||
else {
|
||||
javax.persistence.SecondaryTable jpaSecondaryTable = findMatchingSecondaryTable( join );
|
||||
if ( jpaSecondaryTable != null ) {
|
||||
if ( jpaSecondaryTable.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault();
|
||||
if ( jpaSecondaryTable.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| jpaSecondaryTable.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
|
||||
( (SimpleValue) join.getKey() ).setForeignKeyName( "none" );
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -347,7 +347,8 @@ public class MapBinder extends CollectionBinder {
|
|||
if ( element != null ) {
|
||||
final javax.persistence.ForeignKey foreignKey = getMapKeyForeignKey( property );
|
||||
if ( foreignKey != null ) {
|
||||
if ( foreignKey.value() == ConstraintMode.NO_CONSTRAINT ) {
|
||||
if ( foreignKey.value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| foreignKey.value() == ConstraintMode.PROVIDER_DEFAULT && getBuildingContext().getBuildingOptions().isNoConstraintByDefault() ) {
|
||||
element.setForeignKeyName( "none" );
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.foreignkeys.disabled;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Yanming Zhou
|
||||
*/
|
||||
public class DefaultConstraintModeTest extends BaseUnitTestCase {
|
||||
|
||||
private static final String TABLE_NAME = "TestEntity";
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-14253")
|
||||
public void testForeignKeyShouldNotBeCreated() {
|
||||
testForeignKeyCreation(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-14253")
|
||||
public void testForeignKeyShouldBeCreated() {
|
||||
testForeignKeyCreation(true);
|
||||
}
|
||||
|
||||
private void testForeignKeyCreation(boolean created) {
|
||||
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
|
||||
.applySetting(Environment.HBM2DDL_DEFAULT_CONSTRAINT_MODE, created ? "CONSTRAINT" : "NO_CONSTRAINT").build();
|
||||
Metadata metadata = new MetadataSources(ssr).addAnnotatedClass(TestEntity.class).buildMetadata();
|
||||
assertThat(findTable(metadata, TABLE_NAME).getForeignKeys().isEmpty(), is(!created));
|
||||
}
|
||||
|
||||
private static Table findTable(Metadata metadata, String tableName) {
|
||||
return StreamSupport.stream(metadata.getDatabase().getNamespaces().spliterator(), false)
|
||||
.flatMap(namespace -> namespace.getTables().stream()).filter(t -> t.getName().equals(tableName))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@javax.persistence.Table(name = TABLE_NAME)
|
||||
public static class TestEntity {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn
|
||||
private TestEntity mate;
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue