Skip to content

Commit d0098f3

Browse files
committed
HHH-8162 Make unique constraint handling on schema update configurable
Conflicts: hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java hibernate-core/src/test/java/org/hibernate/test/annotations/join/JoinOrderingTest.java
1 parent a303475 commit d0098f3

File tree

8 files changed

+330
-89
lines changed

8 files changed

+330
-89
lines changed

hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
*/
2424
package org.hibernate.cfg;
2525

26-
2726
/**
2827
* @author Steve Ebersole
2928
*/
@@ -650,4 +649,22 @@ public interface AvailableSettings {
650649
* Oracle), this is disabled by default.
651650
*/
652651
public static final String ENABLE_SYNONYMS = "hibernate.synonyms";
652+
653+
/**
654+
* Unique columns and unique keys both use unique constraints in most dialects.
655+
* SchemaUpdate needs to create these constraints, but DB's
656+
* support for finding existing constraints is extremely inconsistent. Further,
657+
* non-explicitly-named unique constraints use randomly generated characters.
658+
*
659+
* Therefore, select from these strategies.
660+
* {@link org.hibernate.tool.hbm2ddl.UniqueConstraintSchemaUpdateStrategy#DROP_RECREATE_QUIETLY} (DEFAULT):
661+
* Attempt to drop, then (re-)create each unique constraint.
662+
* Ignore any exceptions thrown.
663+
* {@link org.hibernate.tool.hbm2ddl.UniqueConstraintSchemaUpdateStrategy#RECREATE_QUIETLY}:
664+
* attempt to (re-)create unique constraints,
665+
* ignoring exceptions thrown if the constraint already existed
666+
* {@link org.hibernate.tool.hbm2ddl.UniqueConstraintSchemaUpdateStrategy#SKIP}:
667+
* do not attempt to create unique constraints on a schema update
668+
*/
669+
public static final String UNIQUE_CONSTRAINT_SCHEMA_UPDATE_STRATEGY = "hibernate.schema_update.unique_constraint_strategy";
653670
}

hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java

Lines changed: 67 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@
135135
import org.hibernate.service.internal.StandardServiceRegistryImpl;
136136
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
137137
import org.hibernate.tool.hbm2ddl.IndexMetadata;
138+
import org.hibernate.tool.hbm2ddl.SchemaUpdateScript;
138139
import org.hibernate.tool.hbm2ddl.TableMetadata;
140+
import org.hibernate.tool.hbm2ddl.UniqueConstraintSchemaUpdateStrategy;
139141
import org.hibernate.tuple.entity.EntityTuplizerFactory;
140142
import org.hibernate.type.BasicType;
141143
import org.hibernate.type.SerializationException;
@@ -1110,57 +1112,64 @@ public String[] generateSchemaCreationScript(Dialect dialect) throws HibernateEx
11101112
*
11111113
* @throws HibernateException Generally indicates a problem calling {@link #buildMappings()}
11121114
*
1113-
* @see org.hibernate.tool.hbm2ddl.SchemaExport
1115+
* @see org.hibernate.tool.hbm2ddl.SchemaUpdate
1116+
*
1117+
* @deprecated Use {@link #generateSchemaUpdateScriptList(Dialect, DatabaseMetadata)} instead
11141118
*/
11151119
@SuppressWarnings({ "unchecked" })
1120+
@Deprecated
11161121
public String[] generateSchemaUpdateScript(Dialect dialect, DatabaseMetadata databaseMetadata)
11171122
throws HibernateException {
1123+
List<SchemaUpdateScript> scripts = generateSchemaUpdateScriptList( dialect, databaseMetadata );
1124+
return SchemaUpdateScript.toStringArray( scripts );
1125+
}
1126+
1127+
/**
1128+
* @param dialect The dialect for which to generate the creation script
1129+
* @param databaseMetadata The database catalog information for the database to be updated; needed to work out what
1130+
* should be created/altered
1131+
*
1132+
* @return The sequence of DDL commands to apply the schema objects
1133+
*
1134+
* @throws HibernateException Generally indicates a problem calling {@link #buildMappings()}
1135+
*
1136+
* @see org.hibernate.tool.hbm2ddl.SchemaUpdate
1137+
*/
1138+
public List<SchemaUpdateScript> generateSchemaUpdateScriptList(Dialect dialect, DatabaseMetadata databaseMetadata)
1139+
throws HibernateException {
11181140
secondPassCompile();
11191141

11201142
String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
11211143
String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
1144+
UniqueConstraintSchemaUpdateStrategy constraintMethod = UniqueConstraintSchemaUpdateStrategy.interpret( properties
1145+
.get( Environment.UNIQUE_CONSTRAINT_SCHEMA_UPDATE_STRATEGY ) );
11221146

1123-
ArrayList<String> script = new ArrayList<String>( 50 );
1147+
List<SchemaUpdateScript> scripts = new ArrayList<SchemaUpdateScript>();
11241148

11251149
Iterator iter = getTableMappings();
11261150
while ( iter.hasNext() ) {
11271151
Table table = (Table) iter.next();
1128-
String tableSchema = ( table.getSchema() == null ) ? defaultSchema : table.getSchema() ;
1152+
String tableSchema = ( table.getSchema() == null ) ? defaultSchema : table.getSchema();
11291153
String tableCatalog = ( table.getCatalog() == null ) ? defaultCatalog : table.getCatalog();
11301154
if ( table.isPhysicalTable() ) {
11311155

1132-
TableMetadata tableInfo = databaseMetadata.getTableMetadata(
1133-
table.getName(),
1134-
tableSchema,
1135-
tableCatalog,
1136-
table.isQuoted()
1137-
);
1156+
TableMetadata tableInfo = databaseMetadata.getTableMetadata( table.getName(), tableSchema,
1157+
tableCatalog, table.isQuoted() );
11381158
if ( tableInfo == null ) {
1139-
script.add(
1140-
table.sqlCreateString(
1141-
dialect,
1142-
mapping,
1143-
tableCatalog,
1144-
tableSchema
1145-
)
1146-
);
1159+
scripts.add( new SchemaUpdateScript( table.sqlCreateString( dialect, mapping, tableCatalog,
1160+
tableSchema ), false ) );
11471161
}
11481162
else {
1149-
Iterator<String> subiter = table.sqlAlterStrings(
1150-
dialect,
1151-
mapping,
1152-
tableInfo,
1153-
tableCatalog,
1154-
tableSchema
1155-
);
1163+
Iterator<String> subiter = table.sqlAlterStrings( dialect, mapping, tableInfo, tableCatalog,
1164+
tableSchema );
11561165
while ( subiter.hasNext() ) {
1157-
script.add( subiter.next() );
1166+
scripts.add( new SchemaUpdateScript( subiter.next(), false ) );
11581167
}
11591168
}
11601169

11611170
Iterator<String> comments = table.sqlCommentStrings( dialect, defaultCatalog, defaultSchema );
11621171
while ( comments.hasNext() ) {
1163-
script.add( comments.next() );
1172+
scripts.add( new SchemaUpdateScript( comments.next(), false ) );
11641173
}
11651174

11661175
}
@@ -1169,55 +1178,47 @@ public String[] generateSchemaUpdateScript(Dialect dialect, DatabaseMetadata dat
11691178
iter = getTableMappings();
11701179
while ( iter.hasNext() ) {
11711180
Table table = (Table) iter.next();
1172-
String tableSchema = ( table.getSchema() == null ) ? defaultSchema : table.getSchema() ;
1181+
String tableSchema = ( table.getSchema() == null ) ? defaultSchema : table.getSchema();
11731182
String tableCatalog = ( table.getCatalog() == null ) ? defaultCatalog : table.getCatalog();
11741183
if ( table.isPhysicalTable() ) {
11751184

1176-
TableMetadata tableInfo = databaseMetadata.getTableMetadata(
1177-
table.getName(),
1178-
tableSchema,
1179-
tableCatalog,
1180-
table.isQuoted()
1181-
);
1182-
1183-
Iterator uniqueIter = table.getUniqueKeyIterator();
1184-
while ( uniqueIter.hasNext() ) {
1185-
final UniqueKey uniqueKey = (UniqueKey) uniqueIter.next();
1186-
// Skip if index already exists. Most of the time, this
1187-
// won't work since most Dialects use Constraints. However,
1188-
// keep it for the few that do use Indexes.
1189-
if ( tableInfo != null && StringHelper.isNotEmpty( uniqueKey.getName() ) ) {
1190-
final IndexMetadata meta = tableInfo.getIndexMetadata( uniqueKey.getName() );
1191-
if ( meta != null ) {
1192-
continue;
1185+
TableMetadata tableInfo = databaseMetadata.getTableMetadata( table.getName(), tableSchema,
1186+
tableCatalog, table.isQuoted() );
1187+
1188+
if (! constraintMethod.equals( UniqueConstraintSchemaUpdateStrategy.SKIP )) {
1189+
Iterator uniqueIter = table.getUniqueKeyIterator();
1190+
while ( uniqueIter.hasNext() ) {
1191+
final UniqueKey uniqueKey = (UniqueKey) uniqueIter.next();
1192+
// Skip if index already exists. Most of the time, this
1193+
// won't work since most Dialects use Constraints. However,
1194+
// keep it for the few that do use Indexes.
1195+
if ( tableInfo != null && StringHelper.isNotEmpty( uniqueKey.getName() ) ) {
1196+
final IndexMetadata meta = tableInfo.getIndexMetadata( uniqueKey.getName() );
1197+
if ( meta != null ) {
1198+
continue;
1199+
}
11931200
}
1201+
String constraintString = uniqueKey.sqlCreateString( dialect, mapping, tableCatalog, tableSchema );
1202+
if ( constraintString != null && !constraintString.isEmpty() )
1203+
if ( constraintMethod.equals( UniqueConstraintSchemaUpdateStrategy.DROP_RECREATE_QUIETLY ) ) {
1204+
String constraintDropString = uniqueKey.sqlDropString( dialect, tableCatalog, tableCatalog );
1205+
scripts.add( new SchemaUpdateScript( constraintDropString, true) );
1206+
}
1207+
scripts.add( new SchemaUpdateScript( constraintString, true) );
11941208
}
1195-
String constraintString = uniqueKey.sqlCreateString( dialect,
1196-
mapping, tableCatalog, tableSchema );
1197-
if (constraintString != null) script.add( constraintString );
11981209
}
11991210

12001211
if ( dialect.hasAlterTable() ) {
12011212
Iterator subIter = table.getForeignKeyIterator();
12021213
while ( subIter.hasNext() ) {
12031214
ForeignKey fk = (ForeignKey) subIter.next();
12041215
if ( fk.isPhysicalConstraint() ) {
1205-
boolean create = tableInfo == null || (
1206-
tableInfo.getForeignKeyMetadata( fk ) == null && (
1207-
//Icky workaround for MySQL bug:
1208-
!( dialect instanceof MySQLDialect ) ||
1209-
tableInfo.getIndexMetadata( fk.getName() ) == null
1210-
)
1211-
);
1216+
boolean create = tableInfo == null || ( tableInfo.getForeignKeyMetadata( fk ) == null && (
1217+
// Icky workaround for MySQL bug:
1218+
!( dialect instanceof MySQLDialect ) || tableInfo.getIndexMetadata( fk.getName() ) == null ) );
12121219
if ( create ) {
1213-
script.add(
1214-
fk.sqlCreateString(
1215-
dialect,
1216-
mapping,
1217-
tableCatalog,
1218-
tableSchema
1219-
)
1220-
);
1220+
scripts.add( new SchemaUpdateScript( fk.sqlCreateString( dialect, mapping,
1221+
tableCatalog, tableSchema ), false ) );
12211222
}
12221223
}
12231224
}
@@ -1233,14 +1234,8 @@ public String[] generateSchemaUpdateScript(Dialect dialect, DatabaseMetadata dat
12331234
continue;
12341235
}
12351236
}
1236-
script.add(
1237-
index.sqlCreateString(
1238-
dialect,
1239-
mapping,
1240-
tableCatalog,
1241-
tableSchema
1242-
)
1243-
);
1237+
scripts.add( new SchemaUpdateScript( index.sqlCreateString( dialect, mapping, tableCatalog,
1238+
tableSchema ), false ) );
12441239
}
12451240
}
12461241
}
@@ -1251,11 +1246,11 @@ public String[] generateSchemaUpdateScript(Dialect dialect, DatabaseMetadata dat
12511246
Object key = generator.generatorKey();
12521247
if ( !databaseMetadata.isSequence( key ) && !databaseMetadata.isTable( key ) ) {
12531248
String[] lines = generator.sqlCreateStrings( dialect );
1254-
script.addAll( Arrays.asList( lines ) );
1249+
scripts.addAll( SchemaUpdateScript.fromStringArray( lines, false ) );
12551250
}
12561251
}
12571252

1258-
return ArrayHelper.toStringArray( script );
1253+
return scripts;
12591254
}
12601255

12611256
public void validateSchema(Dialect dialect, DatabaseMetadata databaseMetadata)throws HibernateException {

hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/DatabaseMetadata.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@
4949
/**
5050
* JDBC database metadata
5151
* @author Christoph Sturm, Teodor Danciu
52-
*/
53-
/**
5452
* @author Brett Meyer
5553
*/
5654
public class DatabaseMetadata {

hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,9 @@ public void execute(Target target) {
209209
outputFileWriter = new FileWriter( outputFile );
210210
}
211211

212-
String[] sqlStrings = configuration.generateSchemaUpdateScript( dialect, meta );
213-
for ( String sql : sqlStrings ) {
214-
String formatted = formatter.format( sql );
212+
List<SchemaUpdateScript> scripts = configuration.generateSchemaUpdateScriptList( dialect, meta );
213+
for ( SchemaUpdateScript script : scripts ) {
214+
String formatted = formatter.format( script.getScript() );
215215
try {
216216
if ( delimiter != null ) {
217217
formatted += delimiter;
@@ -223,17 +223,19 @@ public void execute(Target target) {
223223
outputFileWriter.write( formatted + "\n" );
224224
}
225225
if ( target.doExport() ) {
226-
LOG.debug( sql );
226+
LOG.debug( script.getScript() );
227227
stmt.executeUpdate( formatted );
228228
}
229229
}
230230
catch ( SQLException e ) {
231-
if ( haltOnError ) {
232-
throw new JDBCException( "Error during DDL export", e );
231+
if (!script.isQuiet()) {
232+
if ( haltOnError ) {
233+
throw new JDBCException( "Error during DDL export", e );
234+
}
235+
exceptions.add( e );
236+
LOG.unsuccessful(script.getScript());
237+
LOG.error(e.getMessage());
233238
}
234-
exceptions.add( e );
235-
LOG.unsuccessful(sql);
236-
LOG.error(e.getMessage());
237239
}
238240
}
239241

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* JBoss, Home of Professional Open Source
5+
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
6+
* as indicated by the @authors tag. All rights reserved.
7+
* See the copyright.txt in the distribution for a
8+
* full listing of individual contributors.
9+
*
10+
* This copyrighted material is made available to anyone wishing to use,
11+
* modify, copy, or redistribute it subject to the terms and conditions
12+
* of the GNU Lesser General Public License, v. 2.1.
13+
* This program is distributed in the hope that it will be useful, but WITHOUT A
14+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15+
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16+
* You should have received a copy of the GNU Lesser General Public License,
17+
* v.2.1 along with this distribution; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19+
* MA 02110-1301, USA.
20+
*/
21+
package org.hibernate.tool.hbm2ddl;
22+
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
26+
/**
27+
* Pairs a SchemaUpdate SQL script with the boolean 'quiet'. If true, it allows
28+
* the script to be run, ignoring all exceptions.
29+
*
30+
* @author Brett Meyer
31+
*/
32+
public class SchemaUpdateScript {
33+
34+
private final String script;
35+
36+
private final boolean quiet;
37+
38+
public SchemaUpdateScript(String script, boolean quiet) {
39+
this.script = script;
40+
this.quiet = quiet;
41+
}
42+
43+
public String getScript() {
44+
return script;
45+
}
46+
47+
public boolean isQuiet() {
48+
return quiet;
49+
}
50+
51+
public static String[] toStringArray(List<SchemaUpdateScript> scripts) {
52+
String[] scriptsArray = new String[scripts.size()];
53+
for (int i = 0; i < scripts.size(); i++) {
54+
scriptsArray[i] = scripts.get( i ).getScript();
55+
}
56+
return scriptsArray;
57+
}
58+
59+
public static List<SchemaUpdateScript> fromStringArray(String[] scriptsArray, boolean quiet) {
60+
List<SchemaUpdateScript> scripts = new ArrayList<SchemaUpdateScript>();
61+
for (String script : scriptsArray) {
62+
scripts.add( new SchemaUpdateScript( script, quiet ) );
63+
}
64+
return scripts;
65+
}
66+
}

0 commit comments

Comments
 (0)