<img height="1" width="1" style="display:none;" alt="" src="https://px.ads.linkedin.com/collect/?pid=299788&amp;fmt=gif">

Using Dynamically Generated Enumerations

Software Development, Spring, Java

By Justin Freeman

In one of my earlier engagements at a medical company, we came across a problem I’m sure a lot of people have run up against: an application with lots of static data stored in the database. This could be something you inherited or it was purposely designed that way for business purposes. Either way, you are stuck with the age old question, "Should I just deal with it and hammer the database with constant calls or use Enumerations to take the load off the database?"

Naturally you run across the potential issue of data changes resulting in Enumerations which don’t match the database. What we did with this project was use dynamically generated Enumerations.


The concept is simple. During the build of your application, the application goes out to the database and builds all the necessary Enumerations for your application to run. Now you have all the static data ready to go, solving two problems. Your Enumerations will never be out of sync with the database and since you eliminated all those database calls while the application is running, your application runs much faster. You can even prevent the application from building if the Enumerations don’t get built properly. In this way your application is nearly foolproof against being out of sync with your database schema.


(Just a note: this method was used with Ant but can easily be used with Maven.)


So here is our build.xml, which is simple enough:


<target name="genEnum" depends="compile">
<java classname="com. generator.enums. EnumGenerator" fork="true">
<classpath refid="run.test.path" />
</java>
</target>


And our class for generating the Enumerations:


package com.enum.generator;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
public class EnumGenerator {
public EnumGenerator () {}
private ClassPathXmlApplicationContext ctx = null;
public static String className = "SomeEnum";
public static String dataSource = "dataSource"; // Should be configured in Spring
public static String sql = "someSQL";
public static void main(String args[]) {
EnumGenerator me = new EnumGenerator();
try {
me.generateEnum(className, dataSource, sql);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error generating the Enums!");
System.exit(1);
}
}
private void init() throws Exception {
//This file should have all your dataSources and properties files if you use them
ctx = new ClassPathXmlApplicationContext("applicationContextEnumGen.xml");
}
private void generateEnum(String className, String dataSourceName, String sql) throws Exception {
init();
DataSource dataSource = null;
try {
dataSource = (DataSource) ctx.getBean(dataSourceName);
List<Object> enums = getDataFromEnums(dataSource, sql);
StringBuilder sb = new StringBuilder();
for (Object enumData : enums) {
sb.append(" ");
sb.append(enumData);
sb.append("n");
}
String templateString = readFile("TemplateFile.template"); // See below for example of Template file
templateString = templateString.replace("<BASE_PACKAGE>", "com.enums.");
templateString = templateString.replace("<CLASS_NAME>", "MyEnum");
templateString = templateString.replace("<SQL_OUTPUT>", sb.toString());
writeStringToFile(new File("com.enums." + className + ".java"), templateString);
} catch (Exception e) {
throw e;
}
}
private List<Object> getDataFromEnums(DataSource someDataSource, String sql) throws Exception {
List<Object> result = null;
try {
if (StringUtils.isNotEmpty(sql)) {
ParameterizedRowMapper<Object> mapper = new ParameterizedRowMapper<Object>() {
@Override
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Object(); //This should be your Enum data
}
};
JdbcTemplate jdbc = new JdbcTemplate(someDataSource);
List<Object> aList = jdbc.query(sql, mapper, new HashMap());
if (aList != null && aList.size() > 0) {
result = aList;
} else {
System.out.println("tquery return no record, return null");
}
} else {
System.out.println("tsqlStr is null");
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return result;
}
private String readFile(String fileLocation) throws Exception {
BufferedReader reader = new BufferedReader(new FileReader(new File(fileLocation)));
String line = null;
StringBuilder sb = new StringBuilder();
try {
while ((line = reader.readLine()) != null) {
sb.append(line);
sb.append("n");
}
} catch (Exception e) {
throw e;
}
return sb.toString();
}
private void writeStringToFile(File aFile, String aContents) throws FileNotFoundException, IOException {
if (aFile == null) {
throw new IllegalArgumentException("File should not be null.");
}
Writer output = new BufferedWriter(new FileWriter(aFile));
try {
output.write(aContents);
} finally {
output.flush();
output.close();
}
}


Now it seems like there is a lot going on here, but not really. First we get a data source and our SQL query to get data from the Enumerations. We write the SQL data to a StringBuilder. Next we have a template file that we read in and then we do a string replace on the placeholders in the template file. After we have completed this, we write the StringBuilder to the same String where we just wrote the modified template file. Finally, we write the String out to a file and make it a Java file.

There you have it! Here is a sample template file:


package <BASE_PACKAGE>;
public enum <CLASS_NAME> {
<SQL_OUTPUT>;
private long codeId;
private String code;
private String desc;
private static final Map<Long, <CLASS_NAME>> codeIdMap = new HashMap<Long, <CLASS_NAME>>();
private static final Map<String, <CLASS_NAME>> codeMap = new HashMap<String, <CLASS_NAME>>();
private static final Map<String, <CLASS_NAME>> descMap = new HashMap<String, <CLASS_NAME>>();
}


Now the actual Enumerated data and how it is queried for is all dependent on how your data is built. In our example, we were fortunate to have foreign keys on each table we were getting the Enumerated data from. All we had to do was to query for that foreign key for each table. And then our SQL query would just return that data in a comma-delimited string.


Again, this approach was useful because of the amount of data that was being consumed from the database. I wouldn’t recommend this approach in all cases. In this case, it was very useful and helped prevent us from having out of date static data when we were necessarily in charge of maintaining that data. IN this case, I would recommend this approach... and you pick up some performance benefit as a result. Enjoy!

Managing JIRA at Scale White Paper

TAGS: Software Development, Spring, Java

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Subscribe to Our Newsletter

Recent Blog Posts