Skip to content

Clean code – store native SQL statements in external file

December 29, 2009
tags: ,

In certain schools of thought, an emphasis is placed on experientially reaching spiritual enlightenment, or nirvana, via meditation. When it comes to writing code, putting any person that may have to look at your code, including yourself, in a state free from suffering, takes writing clean and maintainable code.

Having attended a Clean Code book inspired talk on Clean Code a few months ago, and in piety worn the LIVESTRONG-like CLEAN CODE bracelet for two days, I had been itching to find something significant to clean. What had been irritating me for months, if not years, was native SQL statements contained in Java class files, as Strings, split over multiple lines and painstakingly indented to make them legible. It makes no sense to keep them in a Java class as Strings, they logically should be kept in plain text format.

Naturally I consulted the Web and I relied heavily on this post. My requirements where slightly different though:

  • It should take me no more than half an hour reading any requisite documentation.
  • Statements should ideally allow parameters, but not necessarily as the bulk of the native queries I wanted to move to a text file where reporting queries that did not require parameters i.e. I was willing to settle for second prize that involved some cleaning.
  • Queries should remain legible and not be split in any way. In other words, I should be able to cut and paste from .sql files or pgAdmin III, the PostgreSQL Administration Tool.

An example of what I wanted to clean is shown below. Apart from the fact that indenting the String so that the query remain legible takes ages, it just looks out of place and if you ever need to work on the query its a nightmare. In my mind it is best removed from the Java source and stored in a format that it is meant be stored in, plaintext.

import javax.persistence.EntityManager;
import java.util.Properties;
import javax.persistence.Query;
import java.io.InputStream;

EntityManager entityManager;

public String extremelyUsefulPercentage() {
 Properties properties = readProperties("native-postgresql-queries.xml");
 String somethingExtremelyUsefulSql
     = "SELECT round(unrounded_percentage,2) AS rounded_percentage " +
       "FROM" +
         "(SELECT (" +
             "(" +
                "(" +
                    "SELECT round(count(*),2)" +
                    "FROM glacier" +
                    "JOIN country ON (countryid = country.id)" +
                    "WHERE ismelting AND country.name LIKE 'n%')" +
                 "/" +
                 "(" +
                    "SELECT round(count(*),2)" +
                    "FROM glacier" +
                    "JOIN country ON (countryid = country.id)" +
                    "WHERE ismelting" +
                 ")" +
               ") * 100" +
          ") AS unrounded_percentage) AS subquery";
 Query extremelyUsefulQuery = entityManager.createNativeQuery(somethingExtremelyUsefulSql);
 return extremelyUsefulQuery.getResultList().get(0).toString();
}

public Properties readProperties(String xmlFileName) throws Exception {
 Properties properties = new Properties();
 InputStream is = this.getClass().getClassLoader().getResourceAsStream(xmlFileName);
 properties.loadFromXML(is);
 return properties;
}

The solution, which cuts the enormous native query Java String down to two neat lines, is shown below. It uses Java’s Properties class and the loadFromXML method. One could also use name value pairs, instead of loading from XML, but doing so will inevitably involve splitting up a lengthy query into multiple name value pairs, since the value part of a particular name must be on a single line. So using XML is the logical and cleanest option.


import javax.persistence.EntityManager;
import java.util.Properties;
import javax.persistence.Query;
import java.io.InputStream;

EntityManager entityManager;

public String extremelyUsefulPercentage() {
 Properties properties = readProperties("native-postgresql-queries.xml");
 String somethingExtremelyUsefulSql = properties.getProperty("something.extremely.useful.sql");
 Query extremelyUsefulQuery = entityManager.createNativeQuery(somethingExtremelyUsefulSql);
 return extremelyUsefulQuery.getResultList().get(0).toString();
}

public Properties readProperties(String xmlFileName) throws Exception {
 Properties properties = new Properties();
 InputStream is = this.getClass().getClassLoader().getResourceAsStream(xmlFileName);
 properties.loadFromXML(is);
 return properties;
}

File native-postgresql-queries.xml that now contains the previously offending native SQL query:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
 <comment>This query returns something extremely useful.
 </comment>
 <entry key="something.extremely.useful.sql">
 SELECT round(unrounded_percentage,2) AS rounded_percentage
 FROM
 (SELECT (
    (
      (
         SELECT round(count(*),2)
         FROM glacier
         JOIN country ON (countryid = country.id)
         WHERE ismelting AND country.name LIKE 'n%')
      /
      (
        SELECT round(count(*),2)
        FROM glacier
        JOIN country ON (countryid = country.id)
        WHERE ismelting
    )
  ) * 100
 ) AS unrounded_percentage) AS subquery
 </entry>
</properties>

I’m not sure if the approach I have used is best, and I may have commited a plethora of Clean Code sins in the examples above, but I feel the code has been made significantly more maintainable and I feel substantially more enlightened.

About these ads
6 Comments leave one →
  1. Shane permalink
    May 25, 2010 12:40 am

    Thanks for posting this…the lack of support for multiline strings in Java drove me to look for a cleaner solution as well. I ended up storing queries in text files and retrieving them with a util class.

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;

    public class SQLUtil {
    static public String readSql( String resourceFile ){
    String outStr = “”;
    String resourceFilePath = “/package/containing/sql/files/” + resourceFile + “.sql”;
    try{
    InputStream is = SQLUtil.class.getResourceAsStream( resourceFilePath );
    if( is != null ){
    InputStreamReader isr = new InputStreamReader(is);
    BufferedReader br = new BufferedReader(isr);
    String str;
    while( ( str = br.readLine() ) != null ){
    outStr += ” ” + str;
    }
    }
    } catch( IOException e){
    e.printStackTrace();
    }
    return outStr;
    }

    public static void main( String[] args ){
    String s = SQLUtil.readSql(“ExampleQuery”);
    System.out.println( s );
    }
    }

  2. Jan van der Vyver permalink
    November 29, 2011 11:16 am

    mmmm SQL belongs in ORM, not in code…. but that is just my 2c

  3. October 15, 2012 2:47 pm

    I packaged up a small library for externalising SQL – ElSql – https://github.com/OpenGamma/ElSql . I also added a few DSL type elements, but they are optional, so you could have something pretty similar to this. Although ElSql favours working with Spring.

    • nicodewet permalink*
      October 15, 2012 7:24 pm

      Sounds good, even better that its Spring-based (personal preference).

  4. pooja permalink
    January 22, 2013 5:14 pm

    hi…
    i want to move my sql queries to xml file or plain text file(not properties file) . Please tell me the solution

Trackbacks

  1. Good Post on Clean Code « The Blogger In Me

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.