Hi all,
I've written a patch for this based on hay7777's post.
Well, I actually wrote a subclass of Configuration and inserted the code snippet below into the 2nd while loop of the overridden generateSchemaUpdateScript() method.
Code:
// Hack that generates indexes that are omitted when using
// hibernate.hbm2ddl.auto=update. See commented-out code below.
Iterator idxIter = table.getIndexIterator();
while (idxIter.hasNext()) {
Index index = (Index) idxIter.next();
// Skip if index already exists
if (tableInfo != null) {
IndexMetadata meta = tableInfo.getIndexMetadata(index.getName());
if (meta != null) {
continue;
}
}
script.add(index.sqlCreateString(dialect,
getMapping(),
defaultCatalog,
defaultSchema));
}
Below is the entire source code of my DBConfiguration class. It's a bit kludgey, I know.
Code:
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeMap;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.engine.Mapping;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.Index;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Table;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
import org.hibernate.tool.hbm2ddl.IndexMetadata;
import org.hibernate.tool.hbm2ddl.TableMetadata;
import org.hibernate.util.ArrayHelper;
public class DBConfiguration extends Configuration {
/**
* Generate DDL for altering tables
*
* @see org.hibernate.tool.hbm2ddl.SchemaUpdate
*/
public String[] generateSchemaUpdateScript(Dialect dialect, DatabaseMetadata databaseMetadata)
throws HibernateException {
secondPassCompile();
String defaultCatalog = getProperties().getProperty(Environment.DEFAULT_CATALOG);
String defaultSchema = getProperties().getProperty(Environment.DEFAULT_SCHEMA);
ArrayList script = new ArrayList( 50 );
Iterator iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = ( Table ) iter.next();
if ( table.isPhysicalTable() ) {
TableMetadata tableInfo = databaseMetadata.getTableMetadata( table.getName(),
table.getSchema(),
table.getCatalog() );
if ( tableInfo == null ) {
script.add( table.sqlCreateString(
dialect,
getMapping(),
defaultCatalog,
defaultSchema ) );
}
else {
Iterator subiter = table.sqlAlterStrings(
dialect,
getMapping(),
tableInfo,
defaultCatalog,
defaultSchema );
while ( subiter.hasNext() ) script.add( subiter.next() );
}
Iterator comments = table.sqlCommentStrings(dialect, defaultCatalog, defaultSchema);
while ( comments.hasNext() ) {
script.add( comments.next() );
}
}
}
iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = ( Table ) iter.next();
if ( table.isPhysicalTable() ) {
TableMetadata tableInfo = databaseMetadata.getTableMetadata( table.getName(),
table.getSchema(),
table.getCatalog() );
// Hack that generates indexes that are omitted when using
// hibernate.hbm2ddl.auto=update. See commented-out code below.
Iterator idxIter = table.getIndexIterator();
while (idxIter.hasNext()) {
Index index = (Index) idxIter.next();
// Skip if index already exists
if (tableInfo != null) {
IndexMetadata meta = tableInfo.getIndexMetadata(index.getName());
if (meta != null) {
continue;
}
}
script.add(index.sqlCreateString(dialect,
getMapping(),
defaultCatalog,
defaultSchema));
}
if ( dialect.hasAlterTable() ) {
Iterator subIter = table.getForeignKeyIterator();
while ( subIter.hasNext() ) {
ForeignKey fk = ( ForeignKey ) subIter.next();
if ( fk.isPhysicalConstraint() ) {
boolean create = tableInfo == null || (
tableInfo.getForeignKeyMetadata( fk.getName() ) == null && (
//Icky workaround for MySQL bug:
!( dialect instanceof MySQLDialect ) ||
tableInfo.getIndexMetadata( fk.getName() ) == null
)
);
if ( create ) script.add( fk.sqlCreateString(
dialect,
getMapping(),
defaultCatalog,
defaultSchema ) );
}
}
}
}
/*//broken, 'cos we don't generate these with names in SchemaExport
subIter = table.getIndexIterator();
while ( subIter.hasNext() ) {
Index index = (Index) subIter.next();
if ( !index.isForeignKey() || !dialect.hasImplicitIndexForForeignKey() ) {
if ( tableInfo==null || tableInfo.getIndexMetadata( index.getFilterName() ) == null ) {
script.add( index.sqlCreateString(dialect, mapping) );
}
}
}
//broken, 'cos we don't generate these with names in SchemaExport
subIter = table.getUniqueKeyIterator();
while ( subIter.hasNext() ) {
UniqueKey uk = (UniqueKey) subIter.next();
if ( tableInfo==null || tableInfo.getIndexMetadata( uk.getFilterName() ) == null ) {
script.add( uk.sqlCreateString(dialect, mapping) );
}
}*/
}
iter = iterateGenerators( dialect );
while ( iter.hasNext() ) {
PersistentIdentifierGenerator generator = ( PersistentIdentifierGenerator ) iter.next();
Object key = generator.generatorKey();
if ( !databaseMetadata.isSequence( key ) && !databaseMetadata.isTable( key ) ) {
String[] lines = generator.sqlCreateStrings( dialect );
for ( int i = 0; i < lines.length; i++ ) script.add( lines[i] );
}
}
return ArrayHelper.toStringArray( script );
}
private Iterator iterateGenerators(Dialect dialect) throws MappingException {
TreeMap generators = new TreeMap();
String defaultCatalog = getProperties().getProperty(Environment.DEFAULT_CATALOG);
String defaultSchema = getProperties().getProperty(Environment.DEFAULT_SCHEMA);
Iterator iter = classes.values().iterator();
while ( iter.hasNext() ) {
PersistentClass pc = ( PersistentClass ) iter.next();
if ( !pc.isInherited() ) {
IdentifierGenerator ig = pc.getIdentifier()
.createIdentifierGenerator(
dialect,
defaultCatalog,
defaultSchema,
(RootClass) pc
);
if ( ig instanceof PersistentIdentifierGenerator ) {
generators.put( ( ( PersistentIdentifierGenerator ) ig ).generatorKey(), ig );
}
}
}
iter = collections.values().iterator();
while ( iter.hasNext() ) {
Collection collection = ( Collection ) iter.next();
if ( collection.isIdentified() ) {
IdentifierGenerator ig = ( ( IdentifierCollection ) collection ).getIdentifier()
.createIdentifierGenerator(
dialect,
defaultCatalog,
defaultSchema,
null
);
if ( ig instanceof PersistentIdentifierGenerator ) {
generators.put( ( ( PersistentIdentifierGenerator ) ig ).generatorKey(), ig );
}
}
}
return generators.values().iterator();
}
protected Mapping getMapping() throws HibernateException {
Mapping m = null;
try {
Field f = Configuration.class.getDeclaredField("mapping");
f.setAccessible(true);
m = (Mapping) f.get(this);
} catch (Exception x) {
throw new HibernateException("failed to reflect mapping field", x);
}
return m;
}
}
I've tested it on MySQL 4.1.7 (InnoDBDialect) and haven't encountered any issue yet.
Let me know if it works for you guys.
Luke