I solved it, but my setup is different. I use hibernate only in validation mode and run the scripts to update the database by hand. The scripts are generated by hibernate org.hibernate.tool.hbm2ddl.SchemaExport
. Then I take the generated file, added set foreign_key_checks = 0;
in the beginning and set foreign_key_checks = 1; at the end. Then I commented out (add -
- at the beginning) every line that match the pattern alter table *. drop foreign key *.;
This is my old Schema Generator, based on SchemaExport
:
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import javax.persistence.Entity;
import org.apache.commons.io.FileUtils;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.reflections.Reflections;
public class SchemaGenerator {
/** The configuration. */
private final Configuration cfg;
/**
* @param packageNames the package namesused
*/
public SchemaGenerator(final List<String> packageNames) throws Exception {
if (beanValidationInClasspath()) {
this.cfg = new ConfigurationWithBeanValidation();
} else {
this.cfg = new Configuration();
}
this.cfg.setProperty(AvailableSettings.HBM2DDL_AUTO, "create");
this.cfg.setProperty("javax.persistence.validation.mode", "ddl");
List<Class<?>> classes = getClasses(packageNames);
Collections.sort(classes, ClassComparator.INSTANCE);
for (Class<?> clazz : classes) {
this.cfg.addAnnotatedClass(clazz);
}
}
/**
* Method that actually creates the file.
*/
public void generate(final String fileName) {
this.cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
SchemaExport export = new SchemaExport(this.cfg);
export.setDelimiter(";");
export.setOutputFile(fileName);
export.execute(true, false, false, false);
try {
addAdditinalStatements(fileName);
} catch (IOException e) {
throw new RuntimeException("error will extending dll with addtional statement", e);
}
try {
addCommentDropConstraintStatements(fileName);
} catch (IOException e) {
throw new RuntimeException("error will extending dll by escaping drop foraign key relation ships", e);
}
}
private void addAdditinalStatements(final String fileName) throws IOException {
File outputFile = new File(fileName);
String original = FileUtils.readFileToString(outputFile, "utf-8");
String extended = "set foreign_key_checks = 1;\n"
+ original
+ "set foreign_key_checks = 0;\n";
FileUtils.writeStringToFile(outputFile, extended); }
void addCommentDropConstraintStatements(final String fileName) throws IOException {
File outputFile = new File(fileName);
List<String> original = FileUtils.readLines(outputFile, "utf-8");
String withComment = addCommentDropConstraintStatements(original);
FileUtils.writeStringToFile(outputFile, withComment);
}
private Pattern dropKeyStatementPattern = Pattern.compile("alter table .* drop foreign key [^;]*;");
String addCommentDropConstraintStatements(final List<String> original) {
StringBuilder shirnked = new StringBuilder();
for (int i = 0; i < original.size(); i++) {
if ((i + 2) < original.size()) {
String combined3Lines = original.get(i).trim() + " " + original.get(i + 1).trim() + " "
+ original.get(i + 2).trim();
if (dropKeyStatementPattern.matcher(combined3Lines).matches()) {
shirnked.append("-- " + combined3Lines + "\n");
i += 2; //skip the next two lines
} else {
shirnked.append(original.get(i) + "\n");
}
} else {
shirnked.append(original.get(i) + "\n");
}
}
return shirnked.toString();
}
/**
* Utility method used to fetch Class list based on a package name.
*
* @param packageNames (should be the package containing your annotated beans.
*
* @return the classes
*
* @throws Exception the exception
*/
private List<Class<?>> getClasses(final List<String> packageNames) throws Exception {
List<Class<?>> classes = new ArrayList<Class<?>>();
for (String packageName : packageNames) {
System.out.println("scan:" + packageName);
Reflections reflections = new Reflections(packageName);
classes.addAll(reflections.getTypesAnnotatedWith(Entity.class));
}
return classes;
}
/**
* This filter accepts only java class files.
*/
public static class ClassFileFilter implements FileFilter {
/**
* The holy instance of class file filter.
*/
public static final ClassFileFilter INSTANCE = new ClassFileFilter();
@Override
public boolean accept(final File pathname) {
return pathname.isFile() && pathname.getName().endsWith(".class");
}
};
/**
* This filter accepts only java class files.
*/
public static class PackageDirectoryFilter implements FileFilter {
/**
* The holy instance of normal directory filter.
*/
public static final PackageDirectoryFilter INSTANCE = new PackageDirectoryFilter();
@Override
public boolean accept(final File pathname) {
return pathname.isDirectory() && !pathname.isHidden() && !pathname.getName().equalsIgnoreCase(".svn")
&& !pathname.getName().contains(".");
}
};
/**
* Check if Bean validation is in classpath.
*
* @return true, if NotNull class is found.
*/
private boolean beanValidationInClasspath() {
try {
Class.forName("javax.validation.constraints.NotNull");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
and the bugfix class ConfigurationWithBeanValidation
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Properties;
import org.hibernate.MappingException;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.MySQLInnoDBDialect;
/**
* Problem: Hibernate 4.x Schema Export tool does not pay attention to
* jsr303 annotations for ddl generation.
*
* This class fixes that problem. (use it instead of {@link Configuration}).
*
* This integration is usually performed by BeanValidationIntegrator.
* Unfortunately, that integration will only be activated upon
* initialization of the ServiceRegistry, which initializes
* DatasourceConnectionProviderImpl, which looks up the datasource,
* which requires a JNDI context ...
* We therefore reimplement the relevant parts of BeanValidatorIntegrator.
* Since that must occur after secondPassCompile(), which is invoked by
* Configuration.generateSchemaCreationScript, which is invoked by
* SchemaExport, some fancy subclassing is needed to invoke the integration
* at the right time.
*
* https://forum.hibernate.org/viewtopic.php?f=1&t=1014535
*/
public class ConfigurationWithBeanValidation extends Configuration {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = -6277290406810542021L;
@Override
protected void secondPassCompile() throws MappingException {
super.secondPassCompile();
try {
// thank you, hibernate folks, for making this useful class package private ...
Method applyDDL = Class.forName("org.hibernate.cfg.beanvalidation.TypeSafeActivator").getMethod("applyDDL",
Collection.class,
Properties.class,
MySQLInnoDBDialect.class);
applyDDL.setAccessible(true);
applyDDL.invoke(null, classes.values(), getProperties(), MySQLInnoDBDialect.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
Usage:
new SchemaGenerator(Arrays.asList("myPackage.com")).generate("ddl.sql");
org.reflections.Reflections
is from https://code.google.com/p/reflections/
libary (meanwhile moved to https://github.com/ronmamo/reflections
)