[hibernate-dev] Preventing duplicate ForeignKey generation

Milo van der Zee milo at vanderzee.org
Tue Dec 27 17:05:33 EST 2016


Hello all,

During development of applications I'm used to set the schema creation 
to 'update' (hbm2ddl.auto = update). This makes life a bit easier.
Issue with the newer version of Hibernate is that the name of the 
generated keys changed and so all keys are regenerated. For the large 
databases I use this takes hours and has to be done every time a fresh 
copy from production is taken to the development environment.

I do use meaningful names for the indexes where possible. But when using 
abstract classes used by the entities that is not possible because the 
same fields from the abstract are used by many entity classes and so 
would end up having the same index names.

I checked the code that decides if the index needs to be created and 
found that it only checks the name of the index. Not what the index 
actually does. This is why I changed that piece of code to be a bit 
smarter. It is desinged for simple constraints from one column to 
another column. Not for multi to multi column indexes and constraints.

I created a Jira issue for it but nobody notices it and there are no 
comments or anything else. So now I try it here :)
Jira HHH-10934 (https://hibernate.atlassian.net/browse/HHH-10934)

Code fragment I put in SchemaMigratorImpl.java:

private  ForeignKeyInformation findMatchingForeignKey(ForeignKey foreignKey, TableInformation tableInformation) {
	if  (foreignKey.getName() ==null) {
		return  null;
	}

	/*
	 * Find existing keys based on referencing column and referencedTable
	 */
	String  referencingColumn = foreignKey.getColumn(0).getName();
	String  referencedTableName = foreignKey.getReferencedTable().getName();
	Iterable<ForeignKeyInformation> existingForeignKeys = tableInformation.getForeignKeys();
	for  (ForeignKeyInformation existingKey : existingForeignKeys) {
		Iterable<ColumnReferenceMapping> columnReferenceMappings = existingKey.getColumnReferenceMappings();
		for  (ColumnReferenceMapping mapping : columnReferenceMappings) {
			String  existingReferencingColumn = mapping.getReferencingColumnMetadata().getColumnIdentifier().getText();
			String  existingReferencedTableName = mapping.getReferencedColumnMetadata().getContainingTableInformation().getName().getTableName().getCanonicalName();
			if  (referencingColumn.equals(existingReferencingColumn) && referencedTableName.equals(existingReferencedTableName)) {
				return  existingKey;
			}
		}
	}

	// If not yet found check based on key name 	return  tableInformation.getForeignKey(Identifier.toIdentifier(foreignKey.getName()));
}

Or if you prever the Java 8 way:

private  ForeignKeyInformation findMatchingForeignKey(ForeignKey foreignKey, TableInformation tableInformation) {
	log.debug("findMatchingForeignKey");
	if  (foreignKey.getName() ==null)return  null;

	/*
	 * Find existing keys based on referencing column and referencedTable
	 */
	String  referencingColumn = foreignKey.getColumn(0).getName();
	String  referencedTableName = foreignKey.getReferencedTable().getName();
	Predicate<ColumnReferenceMapping> mappingPredicate = m -> referencingColumn.equals(m.getReferencingColumnMetadata().getColumnIdentifier().getText())
			&& referencedTableName.equals(m.getReferencedColumnMetadata().getContainingTableInformation().getName().getTableName().getCanonicalName());
	for  (ForeignKeyInformation existingKey : tableInformation.getForeignKeys()) {
		boolean  found = StreamSupport.stream(existingKey.getColumnReferenceMappings().spliterator(),false).anyMatch(mappingPredicate);
		if  (found)return  existingKey;
	}

	// If not yet found check based on key name 	return  tableInformation.getForeignKey(Identifier.toIdentifier(foreignKey.getName()));
}

The calling method does not use the returned value. It only checks if 
the returned value is null or not. So this could also be cleaned by 
changing the method to return a boolean and then remove the for loop in 
java-8 and use flatmap. But first let us agree on the validity of the 
idea to change this piece of code.

I hope anybody would like to have a look at it and if there is any 
change that the idea (not this actual very quick/dirty implementation) 
goes into the system I'll clean it up and do some actual tests for more 
complex database structures. I did not even check the junit tests yet.
At the moment it is good enough for me but I think it could be something 
more people would benefit from.

Thanks,
Milo van der Zee




More information about the hibernate-dev mailing list