package com.miragesql.miragesql.tool; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import com.miragesql.miragesql.annotation.Column; import com.miragesql.miragesql.annotation.PrimaryKey; import com.miragesql.miragesql.annotation.PrimaryKey.GenerationType; import com.miragesql.miragesql.annotation.Table; import com.miragesql.miragesql.dialect.Dialect; import com.miragesql.miragesql.naming.NameConverter; import com.miragesql.miragesql.type.BigDecimalValueType; import com.miragesql.miragesql.type.BooleanPrimitiveValueType; import com.miragesql.miragesql.type.BooleanValueType; import com.miragesql.miragesql.type.ByteArrayValueType; import com.miragesql.miragesql.type.DoublePrimitiveValueType; import com.miragesql.miragesql.type.DoubleValueType; import com.miragesql.miragesql.type.FloatPrimitiveValueType; import com.miragesql.miragesql.type.FloatValueType; import com.miragesql.miragesql.type.IntegerPrimitiveValueType; import com.miragesql.miragesql.type.IntegerValueType; import com.miragesql.miragesql.type.LongPrimitiveValueType; import com.miragesql.miragesql.type.LongValueType; import com.miragesql.miragesql.type.ShortPrimitiveValueType; import com.miragesql.miragesql.type.ShortValueType; import com.miragesql.miragesql.type.SqlDateValueType; import com.miragesql.miragesql.type.StringValueType; import com.miragesql.miragesql.type.TimeValueType; import com.miragesql.miragesql.type.TimestampValueType; import com.miragesql.miragesql.type.UtilDateValueType; import com.miragesql.miragesql.type.ValueType; import com.miragesql.miragesql.util.JdbcUtil; import com.miragesql.miragesql.util.StringUtil; /** * Entity Generation Tool. * <pre> // setup * EntityGen gen = new EntityGen(); * gen.setPackageName("com.miragesql.miragesql.entity"); * gen.setNameConverter(new DefaultNameConverter()); * gen.setDialect(new StandardDialect()); * * // generate * String source = gen.getEntitySource(conn, "BOOK", null, null); </pre> * <ul> * <li>TODO - Generate Javadoc from DB Comments</li> * </ul> * @author Naoki Takezoe */ public class EntityGen { private String packageName; private NameConverter nameConverter; private List<ValueType<?>> valueTypes = new ArrayList<>(); private Dialect dialect; private GenerationType generationType; private static final String LINE_SEPARATOR = System.getProperty("line.separator"); public EntityGen(){ addValueType(new StringValueType()); addValueType(new IntegerValueType()); addValueType(new IntegerPrimitiveValueType()); addValueType(new LongValueType()); addValueType(new LongPrimitiveValueType()); addValueType(new ShortValueType()); addValueType(new ShortPrimitiveValueType()); addValueType(new DoubleValueType()); addValueType(new DoublePrimitiveValueType()); addValueType(new FloatValueType()); addValueType(new FloatPrimitiveValueType()); addValueType(new BooleanValueType()); addValueType(new BooleanPrimitiveValueType()); addValueType(new BigDecimalValueType()); addValueType(new SqlDateValueType()); addValueType(new UtilDateValueType()); addValueType(new TimeValueType()); addValueType(new TimestampValueType()); addValueType(new ByteArrayValueType()); // addValueType(new com.miragesql.miragesql.type.DefaultValueType()); } public void setDialect(Dialect dialect){ this.dialect = dialect; } public void setPackageName(String packageName) { this.packageName = packageName; } public void setNameConverter(NameConverter nameConverter) { this.nameConverter = nameConverter; } public void addValueType(ValueType<?> valueType){ this.valueTypes.add(valueType); } public void setGenerationType(GenerationType generationType) { this.generationType = generationType; } /** * Returns the source code of the entity class which corresponds to the specified table. * @throws SQLException if a something goes wrong when working with the database */ public String getEntitySource(Connection conn, String tableName, String catalog, String schema) throws SQLException { StringBuilder sb = new StringBuilder(); // package declaration sb.append("package ").append(packageName).append(";").append(LINE_SEPARATOR); sb.append(LINE_SEPARATOR); // import statements appendImport(sb, Table.class); appendImport(sb, Column.class); appendImport(sb, PrimaryKey.class); appendImport(sb, GenerationType.class); sb.append(LINE_SEPARATOR); // class declaration sb.append("@Table(name=\"").append(tableName).append("\")").append(LINE_SEPARATOR); sb.append("public class ").append(tableToEntity(tableName)).append(" {").append(LINE_SEPARATOR); sb.append(LINE_SEPARATOR); // Entity properties DatabaseMetaData meta = conn.getMetaData(); List<String> primaryKeys = new ArrayList<>(); ResultSet keys = meta.getPrimaryKeys(catalog, schema, tableName); while(keys.next()){ String columnName = keys.getString("COLUMN_NAME"); primaryKeys.add(columnName); } keys.close(); ResultSet columns = meta.getColumns(catalog, schema, tableName, "%"); while(columns.next()){ String columnName = columns.getString("COLUMN_NAME"); int sqlType = columns.getInt("DATA_TYPE"); if(primaryKeys.contains(columnName)){ sb.append(" @PrimaryKey"); if(generationType == null){ generationType = GenerationType.APPLICATION; } sb.append("(generationType=GenerationType.").append(generationType.name()); if(generationType == GenerationType.SEQUENCE){ sb.append(", generator=\"").append(tableName).append("_").append(columnName).append("_SEQ\""); } sb.append(")"); sb.append(LINE_SEPARATOR); } sb.append(" @Column(name=\"").append(columnName).append("\")").append(LINE_SEPARATOR); sb.append(" public ").append(getJavaTypeName(sqlType)).append(" "). append(nameConverter.columnToProperty(columnName)).append(";").append(LINE_SEPARATOR); sb.append(LINE_SEPARATOR); } columns.close(); sb.append("}").append(LINE_SEPARATOR); return sb.toString(); } /** * Generates the entity source file into the given source directory. * * @throws SQLException if a something goes wrong when working with the database * @throws IOException if the java file can't be written */ public void saveEntitySource(File srcDir, String charset, Connection conn, String tableName, String catalog, String schema) throws SQLException, IOException { String source = getEntitySource(conn, tableName, catalog, schema); String packageDir = packageName.replace('.', '/'); String fileName = packageDir + "/" + tableToEntity(tableName) + ".java"; File file = new File(srcDir, fileName); createDir(file.getParentFile()); FileOutputStream out = new FileOutputStream(file); out.write(source.getBytes(charset)); out.close(); } /** * Generates the entity source files for all tables in the specified catalog and schema, that respect the specified * pattern, except those ignored. * * @param srcDir the directory where the generated sources will be written * @param charset the used Character set * @param conn the database connection * @param catalogS the catalogue * @param schemaS the String * @param tableNamePattern positive pattern for the tables to generate entities for * @param ignoreTableNamePattern negative pattern for the table NOT to generate entities for * @throws SQLException if a something goes wrong when working with the database * @throws IOException if the Java files can't be written */ public void saveAllEntitySources(File srcDir, String charset, Connection conn,String catalogS, String schemaS, String tableNamePattern, String ignoreTableNamePattern) throws SQLException, IOException { DatabaseMetaData meta = conn.getMetaData(); ResultSet rs = meta.getTables(catalogS, schemaS, "%", new String[]{"TABLE"}); while(rs.next()){ String catalog = rs.getString("TABLE_CAT"); String schema = rs.getString("TABLE_SCHEM"); String tableName = rs.getString("TABLE_NAME"); if(StringUtil.isNotEmpty(tableNamePattern)){ if(!tableName.matches(tableNamePattern)){ continue; } } if(StringUtil.isNotEmpty(ignoreTableNamePattern) && tableName.matches(ignoreTableNamePattern)){ continue; } this.saveEntitySource(srcDir, charset, conn, tableName, catalog, schema); System.out.println(String.format(" %s.%s", schema, tableName)); } JdbcUtil.close(rs); } private static void createDir(File dir){ if(!dir.getParentFile().exists()){ createDir(dir.getParentFile()); } if(!dir.exists()){ dir.mkdir(); } } private String getJavaTypeName(int sqlType){ for(ValueType<?> valueType: getValueTypes()){ Class<?> type = valueType.getJavaType(sqlType); if(type != null){ String typeName = type.getName(); typeName = typeName.replaceFirst("^java\\.lang\\.", ""); return typeName; } } return "String"; } private List<ValueType<?>> getValueTypes(){ List<ValueType<?>> valueTypes = new ArrayList<>(); if(this.dialect.getValueType() != null){ valueTypes.add(this.dialect.getValueType()); } for(ValueType<?> valueType: this.valueTypes){ valueTypes.add(valueType); } return valueTypes; } private static void appendImport(StringBuilder sb, Class<?> clazz){ sb.append("import ").append(clazz.getName().replace('$', '.')).append(";").append(LINE_SEPARATOR); } private static String tableToEntity(String tableName){ StringBuilder sb = new StringBuilder(); boolean uppercase = true; for(int i=0; i < tableName.length(); i++){ String letter = tableName.substring(i, i + 1); if(letter.equals("_")){ uppercase = true; continue; } if(uppercase){ sb.append(letter.toUpperCase()); } else { sb.append(letter.toLowerCase()); } uppercase = false; } return sb.toString(); } }