de.jpdigital.maven.plugins.hibernate6ddl.GenerateDdlMojo Maven / Gradle / Ivy
/*
* Copyright (C) 2024 Jens Pelzetter
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package de.jpdigital.maven.plugins.hibernate6ddl;
import java.io.File;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
/**
* Base class the the Mojo class providing the {@code gen-ddl} goal. In the
* plugins it should be enough to create an empty class which extends this class
* and is annotated with the {@link Mojo} annotation.
*
* @author Jens Pelzetter
*/
@Mojo(
name = "gen-ddl",
defaultPhase = LifecyclePhase.PROCESS_CLASSES,
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
threadSafe = true
)
public class GenerateDdlMojo extends AbstractMojo {
private final static String[] DEFAULT_PROPERTIES_TO_USE = new String[]{
"format_sql",
"use_sql_comments",
"hibernate.id.new_generator_mappings",
"org.hibernate.envers.audit_strategy"
};
/**
* Location of the output file.
*/
@Parameter(
defaultValue
= "${project.build.directory}/generated-resources/sql/ddl/auto",
property = "outputDir",
required = true
)
private File outputDirectory;
/**
* If set each name of an output file will be prefixed with the value of
* this parameter.
*/
@Parameter(required = false, defaultValue = "")
private String outputFileNamePrefix = "";
/**
* If set the value of this parameter will be appended to the name of each
* output file.
*/
@Parameter(required = false, defaultValue = "")
private String outputFileNameSuffix = "";
/**
* If set to true and if only one dialect is configured
* and either {@link #outputFileNamePrefix} or
* {@link #outputFileNameSuffix} are set the dialect name will be omitted
* from the name of the DDL file.
*/
@Parameter(required = false)
private boolean omitDialectFromFileName;
/**
* Packages containing the entity files for which the SQL DDL scripts shall
* be generated.
*/
@Parameter(required = false)
private String[] packages;
/**
* Set to {@code true} to include classes in {@code src/test}.
*/
@Parameter(required = false)
private boolean includeTestClasses;
/**
* Database dialects for which create scripts shall be generated. For
* available dialects refer to the documentation the {@link Dialect}
* enumeration.
*/
@Parameter(required = false)
private String[] dialects;
@Parameter(required = false)
private String[] customDialects;
/**
* Set this to {@code true} to include drop statements into the generated
* DDL file.
*/
@Parameter(required = false)
private boolean createDropStatements;
/**
* The {@code persistence.xml} file to use to read properties etc. Default
* value is {@code src/main/resources/META-INF/persistence.xml}. If the file
* is not present it is ignored. If the file is present all properties set
* using a {@code } element are set on the Hibernate
* configuration.
*/
@Parameter(
defaultValue = "${basedir}/src/main/resources/META-INF/persistence.xml",
required = false
)
private File persistenceXml;
@Parameter(required = false)
private String[] persistencePropertiesToUse;
@Parameter(required = false)
private Map persistenceProperties;
@Parameter(defaultValue = "${project}", readonly = true)
private transient MavenProject project;
public GenerateDdlMojo() {
this.persistenceProperties = new HashMap<>();
}
/**
* The Mojo's execute method.
*
* @throws MojoExecutionException if the Mojo can't be executed.
* @throws MojoFailureException if something goes wrong while to Mojo is
* executed.
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
final File outputDir = outputDirectory;
getLog().info(
String.format(
"Generating DDL SQL files in %s.", outputDir.getAbsolutePath()
)
);
//Check if the output directory exists.
if (!outputDir.exists()) {
final boolean result = outputDir.mkdirs();
if (!result) {
throw new MojoFailureException(
"Failed to create output directory for SQL DDL files."
);
}
}
final Set> entityClasses;
final Set annotatedPackages;
if (packages == null || packages.length == 0) {
final EntityFinder entityFinder = EntityFinder.forClassPath(
project, getLog(), includeTestClasses
);
entityClasses = entityFinder.findEntities();
annotatedPackages = entityFinder.findPackages();
} else {
// Find the entity classes in the packages.
entityClasses = new HashSet<>();
annotatedPackages = new HashSet<>();
for (final String packageName : packages) {
final EntityFinder entityFinder = EntityFinder.forPackage(
project,
getLog(),
packageName,
includeTestClasses
);
final Set> packageEntities = entityFinder
.findEntities();
final Set packagesWithAnnotations = entityFinder
.findPackages();
entityClasses.addAll(packageEntities);
annotatedPackages.addAll(packagesWithAnnotations);
}
}
getLog().info(
String.format("Found %d entities.", entityClasses.size())
);
if (annotatedPackages != null && !annotatedPackages.isEmpty()) {
getLog().info(
String.format(
"Found %d annotated packages.", annotatedPackages.size()
)
);
}
if (getPersistenceProperties().isEmpty()) {
getLog().info("No persistence properties set in POM.");
} else {
getLog().info("Persistence properties set in POM:");
getPersistenceProperties()
.entrySet()
.stream()
.forEach(
property -> getLog().info(
String.format(
"\t%s = %s",
property.getKey(),
property.getValue()
)
)
);
}
// Find the DDL generator implementation to use.
final ServiceLoader serviceLoader = ServiceLoader.load(
DdlGenerator.class
);
final DdlGenerator ddlGenerator;
if (serviceLoader.iterator().hasNext()) {
ddlGenerator = serviceLoader.iterator().next();
} else {
throw new MojoFailureException(
String.format(
"No implementation of '%s' is available.",
DdlGenerator.class.getName()
)
);
}
for (final Dialect dialect : convertDialects()) {
ddlGenerator.generateDdl(
dialect,
annotatedPackages,
entityClasses,
this
);
}
if (customDialects != null) {
for (final String customDialect : customDialects) {
ddlGenerator.generateDdl(
customDialect,
annotatedPackages,
entityClasses,
this
);
}
}
}
public File getOutputDirectory() {
return outputDirectory;
}
public void setOutputDirectory(final File outputDirectory) {
this.outputDirectory = outputDirectory;
}
public String getOutputFileNamePrefix() {
return outputFileNamePrefix;
}
public void setOutputFileNamePrefix(final String outputFileNamePrefix) {
this.outputFileNamePrefix = outputFileNamePrefix;
}
public String getOutputFileNameSuffix() {
return outputFileNameSuffix;
}
public void setOutputFileNameSuffix(final String outputFileNameSuffix) {
this.outputFileNameSuffix = outputFileNameSuffix;
}
public boolean isOmitDialectFromFileName() {
return omitDialectFromFileName;
}
public void setOmitDialectFromFileName(
final boolean omitDialectFromFileName
) {
this.omitDialectFromFileName = omitDialectFromFileName;
}
public String[] getPackages() {
return Arrays.copyOf(packages, packages.length);
}
public void setPackages(final String... packages) {
this.packages = Arrays.copyOf(packages, packages.length);
}
public String[] getDialects() {
return Arrays.copyOf(dialects, dialects.length);
}
public void setDialects(final String... dialects) {
this.dialects = Arrays.copyOf(dialects, dialects.length);
}
public String[] getCustomDialects() {
return Arrays.copyOf(customDialects, customDialects.length);
}
public void setCustomDialects(final String... customDialects) {
this.customDialects = Arrays.copyOf(
customDialects,
customDialects.length
);
}
public boolean isCreateDropStatements() {
return createDropStatements;
}
@SuppressWarnings("PMD.LongVariable")
public void setCreateDropStatements(final boolean createDropStatments) {
this.createDropStatements = createDropStatments;
}
public File getPersistenceXml() {
return persistenceXml;
}
public void setPersistenceXml(final File persistenceXml) {
this.persistenceXml = persistenceXml;
}
public String[] getPersistencePropertiesToUse() {
if (persistencePropertiesToUse == null
|| persistencePropertiesToUse.length == 0) {
return Arrays.copyOf(
DEFAULT_PROPERTIES_TO_USE,
DEFAULT_PROPERTIES_TO_USE.length
);
} else {
return Arrays.copyOf(
persistencePropertiesToUse,
persistencePropertiesToUse.length
);
}
}
public void setPersistencePropertiesToUse(
final String... persistencePropertiesToUse
) {
if (persistencePropertiesToUse == null) {
this.persistencePropertiesToUse = null;
} else {
this.persistencePropertiesToUse = Arrays.copyOf(
persistencePropertiesToUse,
persistencePropertiesToUse.length
);
}
}
public Map getPersistenceProperties() {
return new HashMap<>(persistenceProperties);
}
public void setPersistenceProperties(
final Map persistenceProperties
) {
this.persistenceProperties = new HashMap<>(persistenceProperties);
}
protected MavenProject getProject() {
return project;
}
protected void setProject(final MavenProject project) {
this.project = project;
}
public boolean isIncludeTestClasses() {
return includeTestClasses;
}
public void setIncludeTestClasses(final boolean includeTestClasses) {
this.includeTestClasses = includeTestClasses;
}
/**
* Helper method which reads the dialects from the parameter and converts
* them into instances of the {@link Dialect} enumeration.
*
* @return A list of all dialects to use
*
* @throws MojoFailureException If an error occurs.
*/
private Set convertDialects() throws MojoFailureException {
final Set dialectsList = new HashSet<>();
if (dialects != null) {
for (final String dialect : dialects) {
convertDialect(dialect, dialectsList);
}
}
return dialectsList;
}
/**
* Helper method for converting the dialects from {@code String} to
* instances of the {@link Dialect} enumeration.
*
* @param dialect The dialect to convert.
* @param dialectsList The lists of dialects where the converted dialect is
* stored.
*
* @throws MojoFailureException If the dialect string could not be
* converted, for example if it is misspelled.
* This will cause a {@code Build Failure}
*/
private void convertDialect(
final String dialect,
final Set dialectsList
)
throws MojoFailureException {
try {
dialectsList.add(
Dialect.valueOf(dialect.toUpperCase(Locale.ENGLISH))
);
} catch (IllegalArgumentException ex) {
final StringBuffer buffer = new StringBuffer();
for (final Dialect avilable : Dialect.values()) {
buffer.append(avilable.toString()).append('\n');
}
throw new MojoFailureException(
String.format(
"Can't convert the configured dialect '%s' to a dialect "
+ "classname. Available dialects are:%n"
+ "%s",
dialect,
buffer.toString()),
ex
);
}
}
protected void writeOutputFile(
final String dialectClassName,
final Path tmpDir
) throws MojoFailureException {
final OutputFileWriter writer = OutputFileWriterBuilder
.withOutputDirectory(outputDirectory)
.omitDialectFromFileFile(
omitDialectFromFileName
&& dialects.length == 1
)
.withFileNamePrefix(outputFileNamePrefix)
.withFileNameSuffix(outputFileNameSuffix)
.build();
writer.writeOutputFile(dialectClassName, tmpDir);
}
public String getDialectNameFromClassName(final String dialectClassName) {
final int pos = dialectClassName.lastIndexOf('.');
if (dialectClassName.toLowerCase(Locale.ROOT).endsWith("dialect")) {
return dialectClassName
.substring(
pos + 1,
dialectClassName.length() - "dialect".length()
)
.toLowerCase(Locale.ROOT);
} else {
return dialectClassName.substring(pos + 1).toLowerCase(Locale.ROOT);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy