2010-11-08 14 views
5

Estamos obligados a anexar números de consulta a cada consulta que ejecuta nuestra aplicación.Podemos asignar sugerencias de consulta personalizadas a JPA NamedQueries

EX: SELECCIONAR * FROM ... DONDE ... QUERYNO 123456;

OpenJPA admite sugerencias de consulta, pero solo para sugerencias específicas sobre implementaciones específicas.

... 
Query q = em.createQuery("select m from Magazine m where ... "); 
q.setHint("openjpa.hint.OptimizeResultCount", new Integer(2)); 
q.setHint("openjpa.FetchPlan.ReadLockMode","WRITE"); 
List r = q.getResultList(); 
... 

Pero de acuerdo con la especificación JPA, y openjpa "consejos no válidos o indirectas, que no pueden ser procesados ​​por una determinada base de datos se ignoran. De lo contrario, consejos válidos darán lugar a un ArgumentException ser lanzado." SI, especificar "QUERYNO" como sugerencia parece no tener ningún impacto.

¿Cómo creo una sugerencia de consulta personalizada para especificar en tiempo de ejecución?

... Consulta q = em.createQuery ("seleccionar m de la revista m donde ..."); q.setHint ("com.me.CustomQueryNoHint", nuevo Entero (2234)); Lista r = q.getResultList(); ...

+0

+ 1 JPA2 todavía tiene algunas características fuera de la caja;) – dira

+0

ver la parte RESPUESTA actualizado en mi respuesta anterior. – dira

+0

@olvipseter06 - Creo que no estoy transmitiendo claramente mi necesidad. Su última solución solo se aplica al registro y no se envía a la base de datos. QUERYNO es una cláusula específica, como WHERE u OPTIMIZE FOR X ROWS. http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.sqlref/db2z_sql_querynoclause.htm – Eddie

Respuesta

0

especificando "QUERYNO" como una sugerencia parece no tener ningún impacto.

Correcto. Según el documento que citó, "QUERYNO" es una sugerencia no válida por lo que se ignora. El documento es algo confuso, pero creo que se puede interpretar para apoyar el comportamiento que observaste. :-)

¿Cómo creo una sugerencia de consulta personalizada para especificar en tiempo de ejecución?

Esa es una tarea mucho más alta. No creo que OpenJPA haya sido diseñado para permitir escribir sugerencias de consulta personalizadas.

Pensé un poco más acerca de su problema real de querer agregar una cadena específica a CADA SQL y no creo que sería muy fácil hacerlo en OpenJPA. ¿Quizás podría escribir un contenedor para su controlador JDBC y hacer que añada su cadena a cada SQL?

+0

necesitaría la envoltura que se especificarán en el contenedor web de Configuración del origen de datos, ¿o puedo marcar Telll OpenJPA para usar un contenedor con el controlador de qué es el contenedor? El problema es que nuestro DBA solamente a permitir que un conductor muy específico JDBC y administrar su configuración en todos los ambientes, por lo menos que se puede hacer únicamente desde el lado .war, su inmediata un no-go. – Eddie

0

respuesta no completa pero sólo un puntero ...

QueryCounter

public class QueryCounter { 
    private static long COUNTER = 0; 

    private static long next() { 
     return ++COUNTER; 
    } 

    private static String getHintValue() { 
     return "/*Query No. " + next() + " */"; 
    } 

    public static void setQueryCount(Query query) { 
     /* EclipseLink */ 
     //query.setHint(QueryHints.HINT, getHintValue()); 
      query.setHint("eclipselink.sql.hint", getHintValue()); 

     /* OpenJPA + Oracle */ 
     //query.setHint("openjpa.hint.OracleSelectHint", getQueryHint()); 

     /* OpenJPA + MySQL */ 
     //query.setHint("openjpa.hint.MySQLSelectHin", getQueryHint()); 
    } 
} 

Uso

Organization sun = new Organization("Sun"); 
     em.persist(sun); 
     tx.commit(); 
     Assert.assertNotNull(sun.getEntityId()); 

     Query query = em.createQuery("SELECT org.entityId FROM Organization org WHERE org.entityId = " + sun.getEntityId()); 
     QueryCounter.setQueryCount(query); 
     query.getResultList(); 

     /*ServerSession does NOT log ReadObjectQuery??*/ 
     query = em.createQuery("SELECT org FROM Organization org WHERE org.entityId = " + sun.getEntityId()); 
     QueryCounter.setQueryCount(query); 
     query.getResultList(); 

     query = em.createQuery("SELECT org.entityId FROM Organization org WHERE org.entityId = " + sun.getEntityId()); 
     QueryCounter.setQueryCount(query); 
     query.getResultList(); 

consola

[EL Finest]: 2010-11-20 19:06:16.45--UnitOfWork(717879615)--Thread(Thread[main,5,main])--Execute query ReportQuery(referenceClass=Organization sql="SELECT entity_id FROM organization_tt WHERE (entity_id = ?)") 
[EL Fine]: 2010-11-20 19:06:16.475--ServerSession(699542937)--Connection(1949550475)--Thread(Thread[main,5,main])--SELECT /*Query No. 1 */ entity_id FROM organization_tt WHERE (entity_id = ?) 
    bind => [1] 
[EL Finest]: 2010-11-20 19:06:23.372--UnitOfWork(717879615)--Thread(Thread[main,5,main])--Execute query ReadObjectQuery(referenceClass=Organization sql="SELECT entity_id, name FROM organization_tt WHERE (entity_id = ?)") 
[EL Finest]: 2010-11-20 19:06:35.916--UnitOfWork(717879615)--Thread(Thread[main,5,main])--Execute query ReportQuery(referenceClass=Organization sql="SELECT entity_id FROM organization_tt WHERE (entity_id = ?)") 
[EL Fine]: 2010-11-20 19:06:35.92--ServerSession(699542937)--Connection(1949550475)--Thread(Thread[main,5,main])--SELECT /*Query No. 3 */ entity_id FROM organization_tt WHERE (entity_id = ?) 
    bind => [1] 

OpenJPA tiene un concepto similar 1.8.7. Database-Specific Hints. Vea si estas sugerencias específicas pueden resolver su propósito.

RESPUESTA ACTUALIZADO

@Eddie, a ver si esto puede ayudarle a ...........

CustomLogFactory

public class MyLogFactory extends org.apache.openjpa.lib.log.LogFactoryImpl { 

    /* copied from LogFactoryImpl.NEWLINE */ 
    private static final String NEWLINE = J2DoPrivHelper.getLineSeparator(); 

    private boolean sqlLogger; 

    @Override 
    public Log getLog(String channel) { 
     if("openjpa.jdbc.SQL".equals(channel)) { // OR equalsIgnoreCase(channel) ??? 
      sqlLogger = true; 
     } 
     return super.getLog(channel); 
    } 

    @Override 
    protected LogImpl newLogImpl() { 
     if(sqlLogger) { 
      sqlLogger = false; /* once an SQL Logger is created, we dont't need any more instances */ 
      return new LogImpl() { 
       private long sqlCounter = 0; 

       @Override 
       protected String formatMessage(short level, String message, Throwable t) { 
        if(isSQLString(message)) { 
         StringBuffer formattedMessage = new StringBuffer(super.formatMessage(level, message, t)); 
         StringBuffer queryNo = new StringBuffer(); 
         queryNo.append(" [Query # ").append(++sqlCounter).append("]").append(NEWLINE); 
         formattedMessage.delete(formattedMessage.length() - NEWLINE.length(), formattedMessage.length()); 
         formattedMessage.append(queryNo); 
         return formattedMessage.toString(); 
        } 
        return super.formatMessage(level, message, t); 
       } 

       /* just a sample implementation; checks whether message contains the word "executing" 
       * more concrete implementation should check the message for SELECT, UPDATE, INSERT INTO, ALTER.... clauses */ 
       private boolean isSQLString(String message) { 
        if(message.contains("executing")) { 
         return true; 
        } 
        return false; 
       } 

      }; 
     } 
     return super.newLogImpl(); 
    } 

} 

peristence.xml

<property name="openjpa.Log" value="org.opensource.logger.MyLogFactory(DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SQL=TRACE)"/> 

prueba

EntityManager em = Persistence.createEntityManagerFactory("default").createEntityManager(); 
EntityTransaction tx = em.getTransaction(); 
tx.begin(); 
Person person = new Person(); 
person.setName("Bond-OO7"); 
person.setAge(22); 
em.persist(person); 
tx.commit(); 
em.close(); 

consola

............ 
2084 default TRACE [main] openjpa.jdbc.SQL - <t 346613126, conn 1551158018> executing prepstmnt 556472773 SELECT SEQUENCE_OWNER AS SEQUENCE_SCHEMA, SEQUENCE_NAME FROM ALL_SEQUENCES [Query # 1] 
2136 default TRACE [main] openjpa.jdbc.SQL - <t 346613126, conn 1551158018> [52 ms] spent 
2305 default TRACE [main] openjpa.jdbc.SQL - <t 346613126, conn 2026561073> executing prepstmnt 6637010 INSERT INTO Person (id, age, name) VALUES (?, ?, ?) [params=?, ?, ?] [Query # 2] 
2306 default TRACE [main] openjpa.jdbc.SQL - <t 346613126, conn 2026561073> [1 ms] spent 
............ 

Reference

+0

Gracias, pero parece que solo está agregando comentarios entre/* */que para OpenJPA se puede aprovechar con OracleHint. Sin embargo, QUERYNO no es un comentario, pero una cláusula de consultas de DB2, debe estar en el extremo de los estados y el exterior de bloques de comentarios. Y como la documentación que señala incluso sugiere, para OpenJPA las sugerencias solo se usan para Oracle y MySQL. Para mi diccionario de DB2, cualquier cadena de sugerencia simplemente se ignora. – Eddie

0

En lugar de utilizar JPQL y tratar de forzar la materia específica del proveedor en la consulta, ¿ha considerado escribir un nativo consulta que se asigna a un tipo de devolución de entidad?

em.createNativeQuery(YOUR_DB2_NATIVE_SQL_QUERY_STRING, Magazine.class) 

Se habrá más trabajo para usted ya que su consulta nativa necesita para seleccionar los valores de columna que coinciden con las columnas asignadas en la clase de entidad, pero debería funcionar. Cuando se utiliza SQL nativo del motor de consulta no debe analizar e interpretar específica del proveedor SQL, por lo que su cláusula específica de DB2 al final debe ser pasado a través de la base de datos subyacente en tiempo de ejecución.

Cuestiones relacionadas