You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@empire-db.apache.org by do...@apache.org on 2020/10/07 12:30:30 UTC

[empire-db] branch master updated: EMPIREDB-334 Improve customizablity of data model code generation

This is an automated email from the ASF dual-hosted git repository.

doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git


The following commit(s) were added to refs/heads/master by this push:
     new ef77ca0  EMPIREDB-334 Improve customizablity of data model code generation
ef77ca0 is described below

commit ef77ca02614684714d32871aa427396385296cb6
Author: Rainer Döbele <do...@apache.org>
AuthorDate: Wed Oct 7 14:30:23 2020 +0200

    EMPIREDB-334 Improve customizablity of data model code generation
---
 .../apache/empire/db/codegen/CodeGenConfig.java    | 31 ++++++++++-
 .../apache/empire/db/codegen/CodeGenParser.java    | 64 ++++++++++++++++------
 .../apache/empire/db/codegen/CodeGenWriter.java    | 61 +++++++++++++++------
 .../apache/empire/db/codegen/WriterService.java    | 28 +++++++---
 .../src/main/resources/templates/BaseRecord.vm     |  5 +-
 .../src/main/resources/templates/Database.vm       | 11 +++-
 .../src/main/resources/templates/Table.vm          |  5 +-
 .../src/main/resources/templates/View.vm           |  4 +-
 8 files changed, 159 insertions(+), 50 deletions(-)

diff --git a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfig.java b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfig.java
index 92f7d93..848cbf2 100644
--- a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfig.java
+++ b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfig.java
@@ -61,6 +61,10 @@ public class CodeGenConfig extends XMLConfiguration {
 	 * flag whether to parse and generate views
 	 */
 	private boolean generateViews = true;
+    /**
+     * flag whether to parse and generate views
+     */
+    private boolean generateRecords = true;
 	/**
 	 * Name of the timestamp column used for optimistic locking (may be null)
 	 * e.g. "UPDATE_TIMESTAMP";
@@ -223,7 +227,12 @@ public class CodeGenConfig extends XMLConfiguration {
 	/**
 	 * true if names of foreign-key-relations should be preserved
 	 */
-	private boolean preserveRelationNames = false; 
+	private boolean preserveRelationNames = false;
+	
+	/**
+	 * classname of the writerService
+	 */
+	private String writerServiceClass;
 
 	/**
 	 * Initialize the configuration.
@@ -333,6 +342,16 @@ public class CodeGenConfig extends XMLConfiguration {
         this.generateViews = generateViews;
     }
 
+    public boolean isGenerateRecords()
+    {
+        return generateRecords;
+    }
+
+    public void setGenerateRecords(boolean generateRecords)
+    {
+        this.generateRecords = generateRecords;
+    }
+
     public String getTimestampColumn() {
 		return timestampColumn;
 	}
@@ -516,7 +535,15 @@ public class CodeGenConfig extends XMLConfiguration {
 		this.preserveRelationNames = preserveRelationNames;
 	}
 	
-	private String fallback(String packageName, String defaultSubpackage){
+	public String getWriterServiceClass() {
+        return writerServiceClass;
+    }
+
+    public void setWriterServiceClass(String writerServiceClass)  {
+        this.writerServiceClass = writerServiceClass;
+    }
+
+    private String fallback(String packageName, String defaultSubpackage){
 		String pkg = packageName;
 		if( pkg == null && this.packageName != null){
 			pkg = this.packageName + "." + defaultSubpackage;
diff --git a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParser.java b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParser.java
index 4f2b112..5fc8a5a 100644
--- a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParser.java
+++ b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParser.java
@@ -25,6 +25,7 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Types;
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -117,7 +118,7 @@ public class CodeGenParser {
      * JDBC url, user and password for the connection are obained from the SampleConfig bean
      * Please use the config.xml file to change connection params.
      */
-    private Connection openJDBCConnection(CodeGenConfig config) throws SQLException{
+	protected Connection openJDBCConnection(CodeGenConfig config) throws SQLException{
         log.info("Connecting to Database'" + config.getJdbcURL() + "' / User=" + config.getJdbcUser());
         Connection conn = null;
         try {
@@ -129,13 +130,33 @@ public class CodeGenParser {
         log.info("Connected successfully");
         return conn;
     }
+
+	/**
+	 * Returns whether to add the table or ignore it
+	 * @param tableName
+	 */
+    protected boolean isPopulateTable(String tableName)
+    {
+        if (tableName.indexOf('$') >= 0)
+            return false;
+        return true;
+    }
+    
+    /**
+     * Returns whether to add the column or ignore it
+     * @param columnName
+     */
+    protected boolean isPopulateColumn(String columnName)
+    {
+        return true;
+    }
 	
 	/**
 	 * Queries the metadata of the database for tables and vies and populates the
 	 * database with those
 	 * @throws SQLException 
 	 */
-	private void populateDatabase(DBDatabase db) throws SQLException {
+    protected void populateDatabase(DBDatabase db) throws SQLException {
 		ResultSet tables = null;
 		ArrayList<String> populatedTables=new ArrayList<String>();
 		try{
@@ -160,13 +181,16 @@ public class CodeGenParser {
 	            // Add all tables and views 
 				while (tables.next()) {
 					String tableName = tables.getString("TABLE_NAME");
+			        if (!isPopulateTable(tableName)) {
+                        log.info("Ignoring table " + tableName);
+			            continue;
+			        }
+					// show
 					String tableType = tables.getString("TABLE_TYPE");
-					// Ignore system tables containing a '$' symbol (required for Oracle!)
-					if (tableName.indexOf('$') >= 0) {
-						log.info("Ignoring system table " + tableName);
-						continue;
-					}
-					log.info(tableType + ": " + tableName);
+					String tableSchema = tables.getString("TABLE_SCHEM");
+					String templ = StringUtils.isNotEmpty(tableSchema) ? "{0}: {1} ({2})" : "{0}: {1}"; 
+				    log.info(MessageFormat.format(templ, tableType, tableName, tableSchema));
+					// Table or View
 					if(tableType.equalsIgnoreCase("VIEW")){
 						InMemoryView view = new InMemoryView(tableName, db);
 						populateView(view);
@@ -196,7 +220,7 @@ public class CodeGenParser {
 		}
 	}
 	
-	private void gatherRelations(DBDatabase db, DatabaseMetaData dbMeta, ArrayList<String> tables) throws SQLException{
+    protected void gatherRelations(DBDatabase db, DatabaseMetaData dbMeta, ArrayList<String> tables) throws SQLException{
 		ResultSet relations = null;
 		String fkTableName, pkTableName, fkColName, pkColName, relName;
 		DBTableColumn fkCol, pkCol;
@@ -268,7 +292,7 @@ public class CodeGenParser {
 		}
 	}
 
-	private String getCatalogs(DatabaseMetaData dbMeta) throws SQLException {
+    protected String getCatalogs(DatabaseMetaData dbMeta) throws SQLException {
 		String retVal = "";
 		ResultSet rs = dbMeta.getCatalogs();
 		while (rs.next()) {
@@ -280,7 +304,7 @@ public class CodeGenParser {
 		return retVal;
 	}
 
-	private String getSchemata(DatabaseMetaData dbMeta) throws SQLException {
+    protected String getSchemata(DatabaseMetaData dbMeta) throws SQLException {
 		String retVal = "";
 		ResultSet rs = dbMeta.getSchemas();
 		while (rs.next()) {
@@ -296,7 +320,7 @@ public class CodeGenParser {
 	 * table with that information
 	 * @throws SQLException 
 	 */
-	private void populateTable(DBTable t) throws SQLException {
+    protected void populateTable(DBTable t) throws SQLException {
 		List<String> pkCols = this.findPkColumns(t.getName());
 		String lockColName = config.getTimestampColumn();
 		DBColumn[] keys = new DBColumn[pkCols.size()];
@@ -306,6 +330,8 @@ public class CodeGenParser {
 	        int i=0;
 			while (rs.next()) {
 				DBTableColumn c = addColumn(t, rs, lockColName);
+				if (c==null)
+				    continue;
 				// check if it is a KeyColumn
 				if (pkCols.contains(c.getName()))
 					keys[i++] = c;
@@ -328,7 +354,7 @@ public class CodeGenParser {
 	 * table with that information
 	 * @throws SQLException 
 	 */
-	private void populateView(InMemoryView v) throws SQLException {
+    protected void populateView(InMemoryView v) throws SQLException {
 		ResultSet rs = null;
 		try {
 			rs = dbMeta.getColumns(config.getDbCatalog(), config.getDbSchema(),
@@ -346,7 +372,7 @@ public class CodeGenParser {
 	 * table.
 	 * @throws SQLException 
 	 */
-	private List<String> findPkColumns(String tableName) throws SQLException {
+    protected List<String> findPkColumns(String tableName) throws SQLException {
 		List<String> cols = new ArrayList<String>();
 		ResultSet rs = null;
 		try {
@@ -365,9 +391,15 @@ public class CodeGenParser {
 	 * Adds DBColumn object to the given DBTable. The DBColumn is created from
 	 * the given ResultSet
 	 */
-	private DBTableColumn addColumn(DBTable t, ResultSet rs, String lockColName)
+    protected DBTableColumn addColumn(DBTable t, ResultSet rs, String lockColName)
 			throws SQLException {
-		String name = rs.getString("COLUMN_NAME");
+		
+	    String name = rs.getString("COLUMN_NAME");
+	    if (!isPopulateColumn(name)) {
+            log.info("Ignoring column " + name);
+            return null;
+	    }
+	    
 		DataType empireType = getEmpireDataType(rs.getInt("DATA_TYPE"));
 		double colSize = getColumnSize(empireType, rs.getInt("DATA_TYPE"), rs.getInt("COLUMN_SIZE"));
 
diff --git a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenWriter.java b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenWriter.java
index b9eafb9..220b06d 100644
--- a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenWriter.java
+++ b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenWriter.java
@@ -89,24 +89,23 @@ public class CodeGenWriter {
 	/**
 	 * Constructor
 	 */
-	public CodeGenWriter(CodeGenConfig config) {
-		this.writerService = new WriterService(config);
+	public CodeGenWriter(CodeGenConfig config, WriterService writerService) {
+		this.writerService = writerService;
 		this.config = config;
-		this.engine = new VelocityEngine();
+        this.engine = new VelocityEngine();
 		// we have to keep this in sync with our logging system
 		// http://velocity.apache.org/engine/releases/velocity-1.5/developer-guide.html#simpleexampleofacustomlogger
-		engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM,
-				new CommonsLogLogChute());
+		engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new CommonsLogLogChute());
 		if(config.getTemplateFolder() == null){
 			engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
 			engine.setProperty("classpath." + RuntimeConstants.RESOURCE_LOADER + ".class", ClasspathResourceLoader.class.getName());
-		}else{
+		} else {
 			File templateFolder = new File(config.getTemplateFolder());
 			if(!templateFolder.canRead()){
 				throw new RuntimeException("Provided template folder missing or not readable: " + config.getTemplateFolder());
 			}
 		}
-		
+		// init engine
 		try {
 			engine.init();
 		} catch (Exception e) {
@@ -116,6 +115,14 @@ public class CodeGenWriter {
 	}
 
 	/**
+	 * Overload using standard WriterService
+	 * @param config
+	 */
+    public CodeGenWriter(CodeGenConfig config) {
+        this(config, new WriterService(config));
+    }
+	
+	/**
 	 * Generates the java code files for the database
 	 * 
 	 * @param db
@@ -131,25 +138,35 @@ public class CodeGenWriter {
 		generatedFiles.add(this.createDatabaseClass(db));
 
 		// Create base table class
-		generatedFiles.add(this.createBaseTableClass(db));
+        if (config.getTableBaseName().equals("DBTable")==false) {
+            generatedFiles.add(this.createBaseTableClass(db));
+        }    
+
+        // Create base record class
+        if (config.isGenerateRecords()) {
+            generatedFiles.add(this.createBaseRecordClass(db));
+        }    
 		
 		// Create base view class
-		generatedFiles.add(this.createBaseViewClass(db));
-
-		// Create base record class
-		generatedFiles.add(this.createBaseRecordClass(db));
+        if (config.isGenerateViews() && config.getViewBaseName().equals("DBView")==false) {
+    		generatedFiles.add(this.createBaseViewClass(db));
+        }
+        
 		// Create table classes, record interfaces and record classes
 		for (DBTable table : db.getTables()) {
 			if (!config.isNestTables()) {
 				// if table nesting is disabled, create separate table classes 
 				generatedFiles.add(this.createTableClass(db, table));
 			}
-			generatedFiles.add(this.createRecordClass(db, table));
+            if (config.isGenerateRecords()) {
+                // generate record 
+                generatedFiles.add(this.createRecordClass(db, table));
+            }
 		}
 		
 		// Create view classes
 		for (DBView view : db.getViews()) {
-			if (!config.isNestViews()) {
+			if (config.isGenerateViews() && !config.isNestViews()) {
 				// if table nesting is disabled, create separate table classes 
 				generatedFiles.add(this.createViewClass(db, view));
 			}
@@ -170,15 +187,23 @@ public class CodeGenWriter {
 
 		// Clean out the directory so old code is wiped out.
 		FileUtils.cleanDirectory(this.baseDir);
-
+		
+		boolean createTables  = (!config.isNestTables() || !config.getTableBaseName().equals("DBTable"));
+        boolean createViews   = config.isGenerateViews()  && (!config.isNestViews()  || !config.getViewBaseName().equals("DBView"));
+		boolean craeteRecords = config.isGenerateRecords();
+		
+		// createViews
+		if (!createViews)
+            config.setViewPackageName(config.getPackageName());
+		
 		// Create the table package directory
-		this.tableDir = FileUtils.getFileFromPackage(targetDir, config.getTablePackageName());
+		this.tableDir = (createTables ? FileUtils.getFileFromPackage(targetDir, config.getTablePackageName()) : null);
 
 		// Create the record package directory
-		this.recordDir = FileUtils.getFileFromPackage(targetDir, config.getRecordPackageName());
+		this.recordDir = (craeteRecords ? FileUtils.getFileFromPackage(targetDir, config.getRecordPackageName()) : null);
 		
 		// Create the record package directory
-		this.viewDir = FileUtils.getFileFromPackage(targetDir, config.getViewPackageName());
+		this.viewDir = (createViews ? FileUtils.getFileFromPackage(targetDir, config.getViewPackageName()) : null);
 	}
 
 	private File createDatabaseClass(DBDatabase db) {
diff --git a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/WriterService.java b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/WriterService.java
index cb1fb1f..bb158b1 100644
--- a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/WriterService.java
+++ b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/WriterService.java
@@ -57,7 +57,7 @@ public class WriterService {
 	 * the method names.
 	 * @return the DBRecord method's names
 	 */
-	private Set<String> loadDBRecordMethodNames()
+	protected Set<String> loadDBRecordMethodNames()
 	{
 		Method[] dbrecMethods = DBRecord.class.getMethods();
 		Set<String> names = new HashSet<String>(dbrecMethods.length);
@@ -267,7 +267,7 @@ public class WriterService {
 	/**
 	 * Returns the default value of the given DBColumn.
 	 */
-	public String getDefaultValue(DBColumn c)
+	protected String getDefaultValue(DBColumn c)
 	{
 		DBTableColumn dbC = (DBTableColumn) c;
 		Object val = dbC.getDefaultValue();
@@ -286,7 +286,7 @@ public class WriterService {
 	/**
 	 * Derives a java class name from a database table name.
 	 */
-	private String deriveClassName(String name)
+	protected String deriveClassName(String name)
 	{
 		// PreserverCharacterCase
 		if (config.isPreserverCharacterCase()) {
@@ -328,7 +328,7 @@ public class WriterService {
 	 * @param isBoolean
 	 * @return
 	 */
-	private String deriveAccessorName(String attribute, Class<?> type)
+	protected String deriveAccessorName(String attribute, Class<?> type)
 	{
 		return deriveRecordMethodName(attribute, type, true);
 	}
@@ -339,7 +339,7 @@ public class WriterService {
 	 * so that getter and setter have matching suffixes if one or 
 	 * the other conflicts with an existing method.
 	 */
-	private String deriveRecordMethodName(String attribute, Class<?> type, boolean isGetter) {
+	protected String deriveRecordMethodName(String attribute, Class<?> type, boolean isGetter) {
 		attribute = deriveAttributeName(attribute);
 		StringBuilder attributeName = new StringBuilder();
 		attributeName.append(Character.toUpperCase(attribute.charAt(0)));
@@ -374,7 +374,7 @@ public class WriterService {
 		return attributeName.toString();
 	}
 	
-	private String getGetterPrefix(Class<?> type){
+	protected String getGetterPrefix(Class<?> type){
 		if (type == boolean.class || type == Boolean.class)
 		{
 			return "is";
@@ -391,7 +391,7 @@ public class WriterService {
 	 * @param attribute
 	 * @return
 	 */
-	private String deriveMutatorName(String attribute, Class<?> type)
+	protected String deriveMutatorName(String attribute, Class<?> type)
 	{
 		return deriveRecordMethodName(attribute, type, false);
 	}
@@ -402,8 +402,20 @@ public class WriterService {
 	 * @param attribute
 	 * @return
 	 */
-	private String deriveAttributeName(String column)
+	protected String deriveAttributeName(String column)
 	{
+        // find invalid chars
+        char[] invalidChars = new char[] { '$','#' };
+        for (int i=0; i<invalidChars.length; i++)
+        {   // Remove
+            char c = invalidChars[i];
+            if (column.indexOf(c)>=0)
+                column=StringUtils.remove(column, c);
+        }
+        // replace dash
+        if (column.indexOf('-')>=0)
+            column=column.replace('-','_');
+        // replace space
 		return column.replace(' ', '_');
 	}
 
diff --git a/empire-db-codegen/src/main/resources/templates/BaseRecord.vm b/empire-db-codegen/src/main/resources/templates/BaseRecord.vm
index 06b0563..b23f7e4 100644
--- a/empire-db-codegen/src/main/resources/templates/BaseRecord.vm
+++ b/empire-db-codegen/src/main/resources/templates/BaseRecord.vm
@@ -19,8 +19,11 @@
 package ${recordPackageName};
 
 import org.apache.empire.db.DBRecord;
+#if($baseTableClassName.equals('DBTable'))
+import org.apache.empire.db.DBTable;
+#else
 import ${tablePackageName}.${baseTableClassName};
-
+#end
 
 public abstract class ${baseRecordClassName}<T extends ${baseTableClassName}> extends DBRecord {
 
diff --git a/empire-db-codegen/src/main/resources/templates/Database.vm b/empire-db-codegen/src/main/resources/templates/Database.vm
index aafa65c..bfb7184 100644
--- a/empire-db-codegen/src/main/resources/templates/Database.vm
+++ b/empire-db-codegen/src/main/resources/templates/Database.vm
@@ -28,18 +28,23 @@ import org.apache.empire.db.DBDatabase;
 #if($preserveRelationNames == true)
 import org.apache.empire.db.DBRelation;
 #end
+#if($nestTables==true && $baseTableClassName.equals('DBTable'))
+import org.apache.empire.db.DBTable;
+#end
 #if($nestTables == true)
 import org.apache.empire.db.DBTableColumn;
 #end
+#if($nestViews==true && $baseViewClassName.equals('DBView'))
+import org.apache.empire.db.DBView;
+#end
 #if($nestViews == true)
 import org.apache.empire.exceptions.NotImplementedException;
 #end
 
-#if($nestTables==false || !($tablePackageName.equalsIgnoreCase($basePackageName)))
+#if($nestTables==false && !($tablePackageName.equalsIgnoreCase($basePackageName)))
 import $tablePackageName.*;
-// $tablePackageName vs $basePackageName 
 #end
-#if($nestViews==false || ($viewPackageName!=$basePackageName))
+#if($nestViews==false && !($viewPackageName.equalsIgnoreCase($basePackageName)))
 import $viewPackageName.*;
 #end
 
diff --git a/empire-db-codegen/src/main/resources/templates/Table.vm b/empire-db-codegen/src/main/resources/templates/Table.vm
index 4f8d382..aac0b1c 100644
--- a/empire-db-codegen/src/main/resources/templates/Table.vm
+++ b/empire-db-codegen/src/main/resources/templates/Table.vm
@@ -19,6 +19,9 @@
 #if($nestTables == false)
 package $tablePackageName;
 
+#if($baseTableClassName.equals('DBTable'))
+import org.apache.empire.db.DBTable;
+#end
 import org.apache.empire.data.DataType;
 import org.apache.empire.db.DBTableColumn;
 import $basePackageName.${dbClassName};
@@ -50,8 +53,8 @@ public class $parser.getTableClassName($table.name) extends ${baseTableClassName
 #end
 
 
+#if($table.keyColumns && $table.keyColumns.size()>0)
 		// configure key columns (primary key)
-#if($table.keyColumns.size()>0)
 #if($table.keyColumns.size()==1)
 		setPrimaryKey(${parser.getColumnName($table.keyColumns[0])});
 #else
diff --git a/empire-db-codegen/src/main/resources/templates/View.vm b/empire-db-codegen/src/main/resources/templates/View.vm
index 0c875b9..9400c34 100644
--- a/empire-db-codegen/src/main/resources/templates/View.vm
+++ b/empire-db-codegen/src/main/resources/templates/View.vm
@@ -20,12 +20,14 @@
 #if($nestViews == false)
 package $viewPackageName;
 
+#if($baseViewClassName.equals('DBView'))
+import org.apache.empire.db.DBView;
+#end
 import org.apache.empire.data.DataType;
 import org.apache.empire.db.DBCommandExpr;
 import org.apache.empire.exceptions.NotImplementedException;
 
 import $basePackageName.${dbClassName};
-
 #end
 
 #if($nestViews == true)