2010-11-08 19 views
5

Jesteśmy zobowiązani do dołączania numerów zapytań do każdego zapytania, które wykonuje nasza aplikacja.Czy możemy przypisać niestandardowe podpowiedzi do zapytań do JPA NamedQueries

EX: WYBIERZ * Z ... GDZIE ... QUERYNO 123456;

OpenJPA obsługuje wskazówki kwerendy, ale tylko dla konkretnych wskazówek dotyczących konkretnych implementacji.

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

Ale według specyfikacji JPA i OpenJPA „Nieprawidłowe podpowiedzi i wskazówki, które nie mogą być przetwarzane przez konkretnej bazy danych są ignorowane. W przeciwnym razie, nieważne podpowiedzi spowoduje ArgumentException wyrzucane”. Określenie "QUERYNO" jako wskazówka wydaje się nie mieć żadnego wpływu.

Jak utworzyć niestandardową wskazówkę dotyczącą kwerendy do określenia w czasie wykonywania?

... Zapytanie q = em.createQuery ("wybierz m z Magazynu m gdzie ..."); q.setHint ("com.me.CustomQueryNoHint", new Integer (2234)); Lista r = q.getResultList(); ...

+0

+ 1 JPA2 nadal potrzebuje niektórych funkcji out-of-box;) – dira

+0

zobacz część UPDATED ANSWER w mojej poprzedniej odpowiedzi. – dira

+0

@ becomputer06 - Myślę, że nie wyrażam jasno mojej potrzeby. To drugie rozwiązanie dotyczy tylko rejestrowania i nie jest wysyłane do bazy danych. QUERYNO jest specjalną klauzulą, podobnie jak WHERE lub 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

Odpowiedz

0

określenie "QUERYNO" jako wskazówki wydaje się nie mieć wpływu.

Prawidłowo. Zgodnie z cytowanym dokumentem "QUERYNO" jest nieprawidłową wskazówką, więc jest ignorowane. Dokument jest nieco zagmatwany, ale wierzę, że jeśli można go zinterpretować, aby wspierać zachowanie, które zaobserwowałeś. :-)

Jak utworzyć niestandardową wskazówkę dotyczącą kwerendy do określenia w czasie wykonywania?

To o wiele wyższe zadanie. Nie wierzę, że OpenJPA została zaprojektowana, aby umożliwić pisanie niestandardowych wskazówek dotyczących zapytań.

Pomyślałem trochę więcej o twoim aktualnym problemie z chęcią dołączenia konkretnego ciągu do KAŻDEGO SQL i nie sądzę, że byłoby to bardzo łatwe w OpenJPA. Być może mógłbyś napisać wrapper dla swojego sterownika JDBC i dołączyć ten łańcuch do każdego SQL?

+0

Czy opakowanie musi być określone w kontenerze sieciowym Konfiguracja źródła danych, czy mogę powiedzieć, że OpenJPA używa opakowania z jakimkolwiek sterownikiem podanym przez kontener? Problem polega na tym, że nasze DBA pozwalają tylko na bardzo specyficzny sterownik JDBC i zarządzają jego konfiguracją we wszystkich środowiskach, więc jeśli nie można tego zrobić wyłącznie ze strony .war, natychmiast się nie da. – Eddie

0

nie jest kompletna odpowiedź, ale tylko wskaźnik ...

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

Wykorzystanie

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

Konsola

[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 ma podobną koncepcję 1.8.7. Database-Specific Hints. Sprawdź, czy te konkretne wskazówki mogą rozwiązać Twój cel.

AKTUALIZACJA ODPOWIEDŹ

@Eddie, sprawdzić, czy to może pomóc ...........

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

Konsola

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

Dzięki, ale wygląda na to, że dodajesz komentarze między/* * /, które dla OpenJPA mogą być efektowne za pomocą OracleHint. Jednak QUERYNO nie jest komentarzem, ale klauzulą ​​zapytania DB2, musi znajdować się na końcu instrukcji i poza blokami komentarzy. A dokumentacja, którą sugerujesz, sugeruje, że dla OpenJPA wskazówki są używane tylko dla Oracle i MySQL. Dla mojego słownika DB2 albo ciąg podpowiedzi jest po prostu ignorowany. – Eddie

0

Zamiast używać JPQL i próbować zmusić dostawcy specyficzne rzeczy do zapytania, czy za pisanie rodem zapytanie odwzorowane na typ zwracany przez jednostkę?

em.createNativeQuery(YOUR_DB2_NATIVE_SQL_QUERY_STRING, Magazine.class) 

To będzie więcej pracy dla Ciebie od ojczystym kwerendy musi wybrać wartości kolumny, które odpowiadają z odwzorowanych kolumn w klasie jednostki, ale powinno działać. Podczas korzystania z natywnego SQL aparat kwerendy nie może analizować i interpretować specyficznego dla dostawcy kodu SQL, tak aby Twoja klauzula DB2 na końcu była przekazywana do bazowej bazy danych w środowisku wykonawczym.