2010-11-24 11 views
7

¿Cuál es el mejor lugar para colocar consultas SQL en una aplicación?En general, ¿dónde colocar consultas SQL en una aplicación?

Las consultas pueden ser grandes y requieren formateo.

Agregar la consulta utilizando StringBuilder parece muy abarrotada.

El almacenamiento en archivos y leer cada vez que se hace una solicitud - parece una mala idea (pero creo que la lectura del archivo se puede poner en un bloque estático)

+0

guardarlas como vistas y procedimientos almacenados en la base de datos SQL en sí. – onedaywhen

Respuesta

12

Mantenga la consulta SQL en un archivo de recursos que se lee a una constante en tiempo de carga de clase:

private static final String PERSON_QUERY; 

static{ 
    InputStream str = null; 
    try{ 
     str = ThisClass.class.getResourceAsStream("/path/to/query.sql"); 
     PERSON_QUERY = IOUtils.toString(str); 
    }catch(IOException e){ 
     throw new IllegalStateException("Failed to read SQL query", e); 
    }finally{ 
     IOUtils.closeQuitely(str); 
    } 

} 

De esa manera usted puede utilizar su editor favorito para editar el SQL, pero aún así obtener la consulta en una constante en java.

Si hace esto mucho, extraer el código de un método de ayuda:

public static String loadResourceToString(final String path){ 
    final InputStream stream = 
     Thread 
      .currentThread() 
      .getContextClassLoader() 
      .getResourceAsStream(path); 
    try{ 
     return IOUtils.toString(stream); 
    } catch(final IOException e){ 
     throw new IllegalStateException(e); 
    } finally{ 
     IOUtils.closeQuietly(stream); 
    } 
} 

y el uso que en sus bloques estáticos:

private static final String PERSON_QUERY; 
private static final String ADDRESS_QUERY; 
private static final String AGE_QUERY; 

static{ 
    PERSON_QUERY = Helper.loadResourceToString("queries/personQuery.sql"); 
    ADDRESS_QUERY = Helper.loadResourceToString("queries/addressQuery.sql"); 
    AGE_QUERY = Helper.loadResourceToString("queries/ageQuery.sql"); 
} 

En mi opinión, diferentes idiomas deben estar siempre apartado. Es una práctica horrible ensamblar SQL, HTML, XML, JavaScript, etc. desde código Java. Use plantillas simples o motores de plantillas como Velocity siempre que sea posible. Eso le da muchos beneficios, uno de ellos es que puede cambiar la plantilla sin volver a compilar la clase java.

PD: Estoy usando Apache Commons/IO en el código anterior, pero no es necesario, simplemente más fácil.

+0

Si ya usa IOUtils, es mucho más fácil reemplazar la mayoría de su código con ** FileUtils.readFileToString (nuevo archivo (ruta)); ** – kolobok

+0

@ kapelko si está en un archivo, está en lo cierto. Pero mi código funciona para las entradas de Archivos y Jar. –

+0

¡Trabaja para mí! ¡Estupendo! –

2

leer sobre PreparedStatement

.

En este no es necesario almacenar todas las partes variables de la consulta como , insert into table_x values (?,?,?);

y el uso de statement.setString(1,"hello");, statement.setInt(2,1);, statement.setDouble (3,4.555);

y finalmente statement.execute(); puede insertar los valores ..

PD: Se recomienda guardar las cadenas de instrucciones preparadas en un archivo de propiedades.

+2

el problema es dónde colocar la consulta "insertar en tabla_x ...". Especialmente cuando la consulta es, por ejemplo, 20 líneas. – HanuAthena

1

Sí, es bueno ir. Y un archivo de propiedad no sería una mala idea. Pero a veces tenemos que crear consultas sobre la marcha, porque el enfoque StringBuiler está bien.

+1

@Ansari: el archivo SQL cuando se formatea puede ir a unas 20-30 líneas, que quiero conservar para fines de legibilidad. Por lo tanto, ponerlos en el archivo de propiedades podría no ser de ayuda. – HanuAthena

+0

@HanuAthena: ¿Por qué no? Podemos escribir el valor de una propiedad en múltiples líneas. Y no creo que arruine la legibilidad. En todo caso, harías lo mismo en el código para evitar el desplazamiento horizontal. –

+0

@HanuAthena: Ahora 'darioo' se le ocurrió el ejemplo. Espero que ahora puedas ver mi punto. –

1

Puede ponerlos en un archivo .properties. Utilizando Apache Commons para la configuración, puede avoid reading files every time.

Si se elige el camino con esta ruta, puede ayudar legibilidad mediante la ruptura de una consulta en varias filas utilizando barras invertidas:

myLongQuery: select col1, col2, col3, col4 from \ 
      table1 where \ 
      col1 = 'something' 
0

En mi caso, tengo un DAO específico donde todas mis consultas SQL son "registrado" en un bloque static final.

Ejemplo:

public class MySQLUserDAO extends UserDAO { 

    private static final String SQL_COUNT = "SELECT COUNT(1) AS TOTAL FROM USER"; 
// private static final String SQL_CREATE = "INSERT INTO USER(FIRST_NAME, MIDDLE_NAME, LAST_NAME, EMAIL_ADDRESS, DOB) VALUES (?, ?, ?, ?, ?)"; 
    private static final String SQL_DELETE = "DELETE FROM USER WHERE USER_ID = ?"; 
    private static final String SQL_RETRIEVE = "SELECT * FROM USER WHERE USER_ID = ?"; 
    private static final String SQL_UPDATE = "UPDATE USER SET FIRST_NAME = ?, MIDDLE_NAME = ?, LAST_NAME = ?, GENDER = ?, EMAIL_ADDRESS = ?, DOB = ? WHERE USER_ID = ?"; 
    private static final String SQL_FIND_EMAIL = "SELECT * FROM USER WHERE EMAIL_ADDRESS = ?"; 
    private static final String SQL_FIND_FIRST_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(FIRST_NAME))) = LOWER(?)"; 
    private static final String SQL_FIND_FIRST_NAME_LIKE = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(FIRST_NAME))) LIKE ?"; 
    private static final String SQL_FIND_LAST_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(LAST_NAME))) = LOWER(?)"; 
    private static final String SQL_FIND_LAST_NAME_LIKE = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(LAST_NAME))) LIKE ?"; 
    private static final String SQL_FIND_BY_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(CONCAT_WS(' ', FIRST_NAME, LAST_NAME)))) LIKE ?"; 

Pero para las consultas que requiere la creación de sentencias dinámicas, que lo colocan en el método que se utiliza para.

Ejemplo:

/* (non-Javadoc) 
    * @see net.imatri.dao.JdbcDAO#create(java.lang.Object) 
    */ 
    @Override 
    public boolean create(UserEntity user) throws DAOException { 
     // TODO Auto-generated method stub 
     PreparedStatement ps = null; 
     ResultSet generatedKeyResultSet = null; 
     boolean created = false; 

     String SQL_CREATE = "INSERT INTO USER(FIRST_NAME, MIDDLE_NAME, LAST_NAME, EMAIL_ADDRESS"; 
     String sqlValues = "(?, ?, ?, ?"; 

     if (user.getGender() != null) { 
      SQL_CREATE += ", GENDER"; 
      sqlValues += ", ?"; 
     } 

     if (user.getBirthDate() != null) { 
      SQL_CREATE += ", DOB"; 
      sqlValues += ", ?"; 
     } 

     SQL_CREATE += ") VALUES " + sqlValues + ")"; 

     try { 
      ps = getConnection().prepareStatement(SQL_CREATE, Statement.RETURN_GENERATED_KEYS); 
      ps.setString(1, user.getFirstName()); 
      ps.setString(2, user.getMiddleName()); 
      ps.setString(3, user.getLastName()); 

      int pos = 4; 
      if (user.getGender() != null) { 
       ps.setString(pos++, user.getGender().toString()); 
      } 

      ps.setString(pos++, user.getEmailAddress()); 

      if (user.getBirthDate() != null) 
       ps.setDate(pos++, new Date(user.getBirthDate().getTime())); 

      ps.executeUpdate(); 
      generatedKeyResultSet = ps.getGeneratedKeys(); 
      if (generatedKeyResultSet != null && generatedKeyResultSet.next()) { 
       user.setId(generatedKeyResultSet.getLong(1)); 
      } 
      created = true; 
     } catch (SQLException e) { 
      // TODO Auto-generated catch block 
      throw new DAOException(e); 
     } finally { 
      try { 
       close(generatedKeyResultSet, ps); 
      } catch (SQLException e) { 
       // TODO Auto-generated catch block 
       logger.error("Error closing statement or resultset.", e); 
      } 
     } 

     return created; 
    } 

Su enfoque no es malo. Acabamos de utilizar DAO que contiene SQL en un bloque static final.

Si su SQL puede hacer crecer muchas líneas, puede usar StringBuilder (sin sincronización) o StringBuffer (con sincronización) para la manipulación de cadenas.

0

Solía ​​ponerlos en el archivo de propiedades especiales que se empaqueta en mi jar. Luego lo extraje usando Properties.load(getClass().getResourceAsStream("queries.properties")) y utilizo una declaración preparada.

Pero pasaron los años desde que utilicé esta técnica la última vez y ahora creo que no es muy recomendable a menos que tenga un motivo serio para hacerlo.

Creo que el uso de JPA es una solución "correcta" para grandes proyectos.Si está desarrollando una herramienta de mapeo de uso de proyecto más pequeña como iBatis que le permite escribir consultas como anotaciones.

0

Las consultas estáticas, que dependen únicamente de los parámetros de enlace, se ajustan perfectamente a las clases *DAO, que abstraen el acceso a la base de datos; solo se trata de API DAO como loadUser(int userId) o saveUser(User user). De esta manera, cómo se almacenan las consultas en el DAO no es una gran pregunta, haz lo que quieras.
Normalmente no utilizo consultas dinámicas, por lo que no puedo dar buenos consejos sobre ellas.

2

Me inclinaría personalmente a colocar esas consultas en un archivo XML; el archivo de propiedades es una pesadilla para las consultas complejas (sin olvidar el \ después de cada línea de consulta). Y mientras lo hace, ¿por qué no simplemente utiliza un marco DAO simple como iBatis (now MyBatis) que es un placer utilizar para proyectos simples y complejos. :-)

0

Una cosa en la que puede que desee investigar es en los Procedimientos almacenados o vistas. No estoy seguro de qué tipo de base de datos está utilizando, pero en MS SQL y MySQL ambas son una opción. Ofrecen no solo un lugar para almacenar sus consultas largas, sino que al pasar variables en lugar de solo ejecutar una consulta, esto también protege nuevamente la temida dun dun dunnnnnnn inyección SQL. Ahora, tampoco sé cuán compleja es su aplicación, pero en general tiendo a usar una solución donde mis consultas se almacenan en el extremo de la base de datos, en lugar de en alguna aplicación.

Un poco de lectura: (. Artículos wiki sí, pero hay buenas referencias en la parte inferior) http://en.wikipedia.org/wiki/Stored_procedure http://en.wikipedia.org/wiki/View_(database)

Cuestiones relacionadas