I am writing a program that reads from DB and outputs to a CSV file. Besides the regular columnar data there are 2 JSON fields data as well. The table layout looks like this (other fields removed for brevity):
+----+--------------+-------------+-----------------------+
| ID | Product_Type | Json_Data | Demographic_Questions |
+----+--------------+-------------+-----------------------+
| 1 | DPI | {some_JSON} | {another_JSON} |
+----+--------------+-------------+-----------------------+
| 2 | Travel | {some_JSON} | {another_JSON} |
+----+--------------+-------------+-----------------------+
Program logic
- Read data from DB
- Store columnar data into a
map
- Convert JSON data into CSV format and store into the
map
- Write
map
into CSV file
The main program
public static void extractData(String lastRunDateTime, String extractionType) throws Exception
{
List<LinkedHashMap<String, String>> flatJson = new ArrayList<LinkedHashMap<String, String>>();
String result = "";
ResultSet rsData = null;
List<String> productType = new ArrayList<>(); // Store Product Type name for SQL & CSV creation
try {
conn = dbUtil.dbConnect();
String sqlQuery = "SELECT DISTINCT Product_Type FROM Mapping WHERE Extraction_Type = '"+ extractionType +"'";
st = conn.prepareStatement(sqlQuery);
rsData = st.executeQuery();
while(rsData.next()) {
productType.add(rsData.getString("Product_Type"));
}
// Currently there are 4 product types in DB
for(int i = 0; i < productType.size(); i++)
{
flatJson.clear();
isEmptyRS = true;
String sProductType = productType.get(i);
LOG.debug("Extraction Started for WebDB data " + sProductType + " V" + extractionType);
sqlQuery =
"SELECT b.Rate, b.Comments, a.* " +
"FROM users_data a LEFT OUTER JOIN users_ratings b ON a.sys_policy_no = b.sys_policy_no " +
"WHERE a.Product_Type = '" + sProductType + "' " +
"AND CAST(a.Submitted_Date as date) BETWEEN '2016-08-01 00:00:00' AND '" + currentDateTime + "' " +
"ORDER BY a.Submitted_Date DESC";
st = conn.prepareStatement(sqlQuery);
rsData = st.executeQuery();
while(rsData.next())
{
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
//LOG.debug("Sys_Policy_No = " + rsData.getString("Sys_Policy_No"));
map.put("ID", rsData.getString("ID"));
map.put("Product_type", rsData.getString("Product_type"));
// Read JSON data and convert to columns
result = rsData.getString("Json_Data");
if(result != null && result.length() != 0) {
addKeys("", new ObjectMapper().readTree(result), map);
}
// Read Demographic data and convert to columns
result = rsData.getString("Demographic_Questions");
if(result != null && result.length() != 0) {
addKeys("", new ObjectMapper().readTree(result), map);
}
flatJson.add(map);
}
String filepath = config.getPropValue("GetAllJsonDataFilePath");
String filename = filepath + sProductType + "_csv_json_all_V" + extractionType + ".csv";
LOG.debug("Writing " + filename + "...");
CSVWriter writer = new CSVWriter();
writer.writeAsCSV(flatJson , filename);
LOG.debug("Extraction Completed for WebDB data " + sProductType + " V" + extractionType + "\n");
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
st.close();
if(conn!=null) conn.close();
}
}
JSON conversion program
private static void addKeys(String currentPath, JsonNode jsonNode, Map<String, String> map) {
if (jsonNode.isObject()) {
ObjectNode objectNode = (ObjectNode) jsonNode;
Iterator<Map.Entry<String, JsonNode>> iter = objectNode.fields();
String pathPrefix = currentPath.isEmpty() ? "" : currentPath + ".";
while (iter.hasNext()) {
Map.Entry<String, JsonNode> entry = iter.next();
addKeys(pathPrefix + entry.getKey(), entry.getValue(), map);
}
} else if (jsonNode.isArray()) {
ArrayNode arrayNode = (ArrayNode) jsonNode;
for (int i = 0; i < arrayNode.size(); i++) {
addKeys(currentPath + "_" + i, arrayNode.get(i), map);
}
} else if (jsonNode.isValueNode()) {
ValueNode valueNode = (ValueNode) jsonNode;
String value = valueNode.asText().replace("\n", ". ").replace("\r", "");
map.put(currentPath, value);
}
}
CSV Writer program
public void writeAsCSV(List<LinkedHashMap<String, String>> flatJson, String fileName) throws IOException {
LinkedHashSet<String> headers = collectHeadersOrdered(flatJson);
String output = StringUtils.join(headers.toArray(), ",") + "\n";
for (LinkedHashMap<String, String> linkedMap : flatJson) {
output = output + getCommaSeparatedRow(headers, linkedMap) + "\n";
}
writeToFile(output, fileName);
}
private LinkedHashSet<String> collectHeadersOrdered(List<LinkedHashMap<String, String>> flatJson) {
LinkedHashSet<String> headers = new LinkedHashSet<String>();
for (LinkedHashMap<String, String> linkedMap : flatJson) {
headers.addAll(linkedMap.keySet());
}
return headers;
}
private String getCommaSeparatedRow(Set<String> headers, Map<String, String> map) {
List<String> items = new ArrayList<String>();
for (String header : headers) {
String value = map.get(header) == null ? "" : map.get(header).replace(",", "");
items.add(value);
}
return StringUtils.join(items.toArray(), ",");
}
private void writeToFile(String output, String fileName) throws IOException {
try (BufferedWriter bw =
new BufferedWriter(new FileWriter(fileName))) {
LOG.debug("Generating " + fileName + " ...");
bw.write(output);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
}
While the program runs fine without error it is taking way too much time to execute, roughly 3-4 hours. Currently, the biggest CSV filesize is at 40 MB (around 200k rows, 1300 columns). The 2 JSON fields are subjected to very frequent change and I've seen it growing by 30 data elements every other months.
What can I do to increase the performance?