Skip to content

Instantly share code, notes, and snippets.

@sbcoba
Last active March 13, 2023 16:04
Show Gist options
  • Save sbcoba/e4264f4b4217746767e682c61f9dc3a6 to your computer and use it in GitHub Desktop.
Save sbcoba/e4264f4b4217746767e682c61f9dc3a6 to your computer and use it in GitHub Desktop.
JPA Entity to DDL Generator ( Hibernate 5.x )
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQL5InnoDBDialect;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import javax.persistence.Entity;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* JPA Entity 클래스를 가지고 DDL 쿼리를 생성할 수 있는 클래스
*
* @author sbcoba
*/
public class JpaEntityDdlExport {
/**
* 생성될 파일명
*/
private static final String SCHEMA_SQL = "schema_%s.sql";
/**
* 도메인 클래스 경로 위치 ( 범위가 넓어도 @Entity 를 가지는 class만 찾음 )
*/
private final static String PATTERN = "classpath*:**/*.class";
/**
* DDL 생성할 DB타입
* org.hibernate.dialect.* 패키지 참조*
*
* - 오라클 Oracle10gDialect.class
* - H2 H2Dialect.class
* ...
*
*/
private final static Class<? extends Dialect> DIALECT_CLASS = MySQL5InnoDBDialect.class;
public static void main(String[] args) {
Map<String, Object> settings = new HashMap<>();
settings.put("hibernate.dialect", DIALECT_CLASS);
settings.put("hibernate.format_sql", true);
settings.put("hibernate.physical_naming_strategy","org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
settings.put("hibernate.implicit_naming_strategy","org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
settings.put("hibernate.id.new_generator_mappings", false);
StandardServiceRegistry standardServiceRegistry = new StandardServiceRegistryBuilder()
.applySettings(settings)
.build();
MetadataSources metadata = new MetadataSources(standardServiceRegistry);
String pattern = getPattern(args);
List<Class<?>> classes = getClassesByAnnotation(Entity.class, pattern);
classes.forEach(metadata::addAnnotatedClass);
MetadataImplementor metadataImplementor = (MetadataImplementor) metadata.getMetadataBuilder().build();
SchemaExport schema = new SchemaExport(metadataImplementor);
String outputFile = getOutputFilename(args);
schema.setOutputFile(outputFile);
schema.create(true, false);
appendSemicolon(outputFile);
appendMetaData(outputFile, settings);
}
private static String getPattern(String[] args) {
String pattern = PATTERN;
if(args != null && args.length >= 3
&& StringUtils.hasText(args[2])) {
pattern = args[2];
}
return pattern;
}
private static void appendMetaData(String outputFile, Map<String, Object> settings) {
String charsetName = "UTF-8";
File ddlFile = new File(outputFile);
try {
StringBuilder sb = new StringBuilder();
sb.append("/* Generate Environment\n");
for (Map.Entry<String, Object> entry : settings.entrySet()) {
sb.append(entry.getKey().toString() + ": " + entry.getValue() + "\n");
}
sb.append("*/\n");
String ddlFileContents = StreamUtils.copyToString(new FileInputStream(ddlFile), Charset.forName(charsetName));
sb.append(ddlFileContents);
FileCopyUtils.copy(sb.toString().getBytes(charsetName), ddlFile);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 쿼리의 끝 부분에 세미콜론(;) 추가
* @param outputFile
*/
private static void appendSemicolon(String outputFile) {
String charsetName = "UTF-8";
File ddlFile = new File(outputFile);
try {
String ddlFileContents = StreamUtils.copyToString(new FileInputStream(ddlFile), Charset.forName(charsetName));
ddlFileContents = ddlFileContents.replaceAll("\n\n", ";\n\n");
ddlFileContents = ddlFileContents.replaceAll("\n.*(?![\f\n\r])$", ";\n");
FileCopyUtils.copy(ddlFileContents.getBytes(charsetName), ddlFile);
} catch (IOException e) {
e.printStackTrace();
}
}
private static List<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation, String pattern) {
return getResources(pattern).stream()
.map(r -> metadataReader(r))
.filter(Objects::nonNull)
.filter(mr -> mr.getAnnotationMetadata().hasAnnotation(annotation.getName()))
.map(mr -> entityClass(mr))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 패턴에 해당하는 리소스 정보를 가져 온다.
* @param pattern
* @return
*/
private static List<Resource> getResources(String pattern) {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources;
try {
resources = resolver.getResources(pattern);
} catch (IOException e) {
throw new RuntimeException(e);
}
return Arrays.asList(resources);
}
private static Class<?> entityClass(MetadataReader mr) {
String className = mr.getClassMetadata().getClassName();
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
System.err.printf("%s Class not found", className);
return null;
}
return clazz;
}
private static MetadataReader metadataReader(Resource r) {
MetadataReader mr;
try {
mr = new SimpleMetadataReaderFactory().getMetadataReader(r);
} catch (IOException e) {
System.err.printf(e.getMessage());
return null;
}
return mr;
}
private static String getOutputFilename(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
String currentDate = sdf.format(Calendar.getInstance().getTime());
if(args != null && args.length > 0
&& StringUtils.hasText(args[0])) {
String customSchemaName = args[0];
if(customSchemaName.contains("%s")) {
return String.format(customSchemaName, currentDate);
}
return customSchemaName;
}
return String.format(SCHEMA_SQL, currentDate);
}
}
@sbcoba
Copy link
Author

sbcoba commented Jan 27, 2017

하이버네이트 5.x 이상

@jmrives
Copy link

jmrives commented May 1, 2018

Thank you for the code. However, I get a large number of exceptions when I run it. Here is an example:

java.lang.ClassNotFoundException: org.springframework.hateoas.config.EnableHypermediaSupport$HypermediaType

java.lang.ClassNotFoundException: org.jboss.logging.annotations.Message$Format

I am not using JBoss.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment