2010-11-08 11 views
5

Wir müssen Abfrage-Nummern an jede Abfrage, die unsere Anwendung ausführt, anhängen.Können wir JPA NamedQueries benutzerdefinierte Abfrage-Tipps zuweisen

EX: AUSWÄHLEN * VON ... WO ... QUERYNO 123456;

OpenJPA unterstützt Abfragehinweise, jedoch nur für spezifische Hinweise zu bestimmten Implementierungen.

... 
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(); 
... 

Aber nach der JPA-Spezifikation und openjpa „Ungültige Tipps oder Hinweise, die ignoriert werden durch eine bestimmte Datenbank nicht verarbeitet werden können. Andernfalls wird zur Folge haben ungültige Hinweise in eine Argument geworfen.“ Das Angeben von "QUERYNO" als Hinweis scheint keine Auswirkungen zu haben.

Wie erstelle ich einen benutzerdefinierten Abfrage-Tipp, um zur Laufzeit anzugeben?

... Frage abfragen q = em.createQuery ("wähle m aus Magazin m where ..."); q.setHint ("com.me.CustomQueryNoHint", neue Ganzzahl (2234)); Liste r = q.getResultList(); ...

+0

+ 1 JPA2 benötigt immer noch einige Funktionen out-of-box;) – dira

+0

siehe den Teil AKTUALISIERTE ANTWORT in meiner vorherigen Antwort. – dira

+0

@ bestemputer06 - Ich denke, dass ich nicht klar mein Bedürfnis darlege. Die letztere Lösung gilt nur für die Protokollierung und wird nicht an die Datenbank gesendet. QUERYNO ist eine spezifische Klausel, genau wie WHERE oder 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

Antwort

0

Angabe "QUERYNO" als Hinweis scheint keine Auswirkungen zu haben.

Korrekt. Laut dem von Ihnen angegebenen Dokument ist "QUERYNO" ein ungültiger Hinweis und wird daher ignoriert. Das Dokument ist etwas verwirrend, aber ich glaube, es kann so interpretiert werden, dass es das von Ihnen beobachtete Verhalten unterstützt. :-)

Wie erstelle ich einen benutzerdefinierten Abfrage-Tipp zur Laufzeit anzugeben?

Das ist eine viel größere Aufgabe. Ich glaube nicht, dass OpenJPA entworfen wurde, um das Schreiben von benutzerdefinierten Abfragehinweisen zu ermöglichen.

Ich dachte etwas mehr über Ihr tatsächliches Problem der Anfügen einer bestimmten Zeichenfolge an jede SQL und ich denke nicht, dass es in OpenJPA sehr einfach sein würde. Vielleicht könnten Sie einen Wrapper für Ihren JDBC-Treiber schreiben und diese Zeichenfolge an jedes SQL anhängen?

+0

Müsste der Wrapper im Webcontainer angegeben werden Datenquellenkonfiguration, oder kann ich OpenJPA anweisen, einen Wrapper mit dem Treiber zu verwenden, den er vom Container erhält? Das Problem ist, dass unsere DBAs nur einen sehr spezifischen JDBC-Treiber erlauben und seine Konfiguration in allen Umgebungen verwalten. Wenn es also nicht ausschließlich von der .war-Seite aus durchgeführt werden kann, ist das ein sofortiges No-Go. – Eddie

0

Nicht vollständige Antwort, sondern nur ein Zeiger ...

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()); 
    } 
} 

Nutzungs

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(); 

Console

[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 hat ein ähnliches Konzept 1.8.7. Database-Specific Hints. Sehen Sie, wenn diese spezifischen Hinweise Ihren Zweck lösen können.

AKTUALISIERT ANTWORT

@Eddie, ob dies kann Ihnen helfen ...........

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)"/> 

-Test

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(); 

Console

............ 
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

Danke, aber es sieht so aus, als würdest du nur Kommentare zwischen/* */hinzufügen, die für OpenJPA mithilfe von OracleHint genutzt werden können. QUERYNO ist jedoch kein Kommentar, sondern eine DB2-Abfrageklausel. Sie muss am Ende von Anweisungen und außerhalb von Kommentarblöcken stehen. Und wie die Dokumentation, auf die Sie hinweisen, sogar andeutet, werden die Hinweise für OpenJPA nur für Oracle und MySQL verwendet. Für mein DB2-Wörterbuch wird eine der beiden Hinweiszeichenfolgen einfach ignoriert. – Eddie

0

Anstatt JPQL verwenden und versuchen, herstellerspezifische Sachen in die Abfrage zu zwingen, haben Sie eine native betrachtet schreiben Abfrage, die einem Entitätsrückgabetyp zugeordnet ist?

em.createNativeQuery(YOUR_DB2_NATIVE_SQL_QUERY_STRING, Magazine.class) 

Es wird mehr Arbeit für Sie da Ihre Mutter Abfrage muss Spaltenwerte auswählen, die mit den zugeordneten Spalten in der Entity-Klasse entsprechen, aber es sollte funktionieren. Wenn Sie natives SQL verwenden, darf die Abfrage-Engine den herstellerspezifischen SQL-Code nicht analysieren und interpretieren. Daher sollte Ihre DB2-spezifische Klausel am Ende zur Laufzeit an die zugrunde liegende Datenbank übergeben werden.