2012-01-20 19 views
5

Avere strano problema di prestazioni usando Hibernate 3.3.2GA dietro JPA (e il resto dei pacchetti inclusi in Hibernate JBoss 5.)JPA (Hibernate) nativo di query per la dichiarazione preparata SLOW

sto usando Native Query e assemblando SQL in una dichiarazione preparata.

EntityManager em = getEntityManager(MY_DS); 
final Query query = em.createNativeQuery(fullSql, entity.getClass()); 

L'SQL ha molti join, ma in realtà è molto semplice, con un singolo parametro. Ad esempio:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like ? 

e la query viene eseguita in meno di un secondo su MSSQL Studio.

Se aggiungo

query.setParameter(0, "ABC123%"); 

la query una pausa di 9 secondi

2012-01-20 14:36:21 - TRACE: - AbstractBatcher.getPreparedStatement:(484) | preparing statement 
2012-01-20 14:36:21 - TRACE: - StringType.nullSafeSet:(133) | binding 'ABC123%' to parameter: 1 
2012-01-20 14:36:30 - DEBUG: - AbstractBatcher.logOpenResults:(382) | about to open ResultSet (open ResultSets: 0, globally: 0) 

Tuttavia, se basta sostituire il "?" con il valore (rendendolo non una dichiarazione preparata, ma solo una query SQL dritto.

fullSql = fullSql.replace("?", "'ABC123%'"); 

la query completare in meno di un secondo.

Vorrei davvero preferisco noi una dichiarazione preparata (la ingresso per i parametri viene estratta dai dati utente) per prevenire attacchi di iniezione.

Tracciare lungo il punto lento nel codice, sono arrivato in profondità all'interno del pacchetto jtds-1.2.2. la riga all'origine sembra essere SharedSocket linea 841 "getIn(). readFully (hdrBuf);" Non c'è nulla di ovvio lì però ...

private byte[] readPacket(byte buffer[]) 
     throws IOException { 
    // 
    // Read rest of header 
    try { 
     getIn().readFully(hdrBuf); 
    } catch (EOFException e) { 
     throw new IOException("DB server closed connection."); 
    } 

Arrivati ​​a questo stack attraverso ...

at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(SharedSocket.java:841) 
    at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(SharedSocket.java:722) 
    at net.sourceforge.jtds.jdbc.ResponseStream.getPacket(ResponseStream.java:466) 
    at net.sourceforge.jtds.jdbc.ResponseStream.read(ResponseStream.java:103) 
    at net.sourceforge.jtds.jdbc.ResponseStream.peek(ResponseStream.java:88) 
    at net.sourceforge.jtds.jdbc.TdsCore.wait(TdsCore.java:3928) 
    at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java:1045) 
    at net.sourceforge.jtds.jdbc.TdsCore.microsoftPrepare(TdsCore.java:1178) 
    at net.sourceforge.jtds.jdbc.ConnectionJDBC2.prepareSQL(ConnectionJDBC2.java:657) 
    at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java:776) 
    at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208) 
    at org.hibernate.loader.Loader.getResultSet(Loader.java:1808) 
    at org.hibernate.loader.Loader.doQuery(Loader.java:697) 
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259) 
    at org.hibernate.loader.Loader.doList(Loader.java:2228) 
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2125) 
    at org.hibernate.loader.Loader.list(Loader.java:2120) 
    at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:312) 
    at org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1722) 
    at org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:165) 
    at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:175) 
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:67) 
+0

Consentitemi di aggiungere jtds-1.2.2 allo stack tecnologico. Ho eseguito il debugging tramite Hibernate in JTDS – javatestcase

+0

provato jtds-1.2.4, ma nessuna gioia ... – javatestcase

+0

Passare a com.microsoft.sqlserver.jdbc.SQLServerDriver produce effettivamente risultati identici ... Devo dare un'occhiata a SQL Server, forse c'è qualcosa ... – javatestcase

risposta

8

Lascerò questa domanda e risponderò qui nel caso in cui qualcuno abbia lo stesso problema in futuro.

Il problema è nel modo in cui i driver JTDS inviano le stringhe dei parametri a MSSQL. Apparentemente Java tenterà di inviare i parametri Unicode per impostazione predefinita e MSSQL lo tradurrà in Ascii. Perché ci vogliono 9 secondi, non lo so.

Lotto di riferimenti a questo fuori, ma nulla che mi abbia aiutato fino a quando sono stato in grado di isolare che si trattava di un problema con il driver per la connessione MSSQL.

Questo link è stato utile:

[http://server.pramati.com/blog/2010/06/02/perfissues-jdbcdrivers-mssqlserver/]

Questa è la stringa utilizzando Microsoft conducente.

jdbc:sqlserver://localhost\SQLEXPRESS; 
    DatabaseName=TESTDB; 
    sendStringParametersAsUnicode=false 

Hai solo bisogno di ottenere il SendStringParametersAsUnicode = false passato al vostro setup URL del driver e si sono buoni.

+8

Ci vogliono 9 secondi perché non traduce il parametro in ascii (in quanto potrebbe perdere i dati in quel modo). Cambia ogni valore di colonna in unicode prima di confrontarlo con il tuo parametro. Ciò significa che non può trarre il massimo vantaggio da alcun indice sul campo "stringId" che porta a prestazioni molto più lente (credo che anch'io abbia avuto un problema come questo in passato). – Gareth

+0

BTW, sono felice che tu abbia risolto il tuo problema. – Gareth

+0

grazie. la tua spiegazione aiuta davvero a capire il problema. – javatestcase

3

Controllare la query prevede che SQL Server sta producendo. Le dichiarazioni preparate possono essere particolarmente problematiche.

Mi spiego ...

Se fate questo:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like 'ABC123%'; 

e si dispone di un indice su "stringID" server SQL sa che può usarlo.

Tuttavia, se si esegue questa operazione:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like ?; 

SQL server non sa è possibile utilizzare l'indice quando si crea l'istruzione preparata (come si potrebbe riempire il parametro con '% ABC123' invece di ' ABC123% ') e quindi può scegliere un piano di query completamente diverso.

+0

Grazie Gareth. Quello che dici è vero al 100%. In questo caso il problema principale è con la connessione MSSQL driver. Penso che MSSQL possa memorizzare nella cache i piani di query, e in realtà ci sono solo poche possibili iterazioni dell'SP, con diverse sotto-query per le relazioni uno-a-molti. Dopo aver risolto il problema, 100 query vengono restituite in 47 secondi, un enorme miglioramento di oltre 9 secondi ciascuna! – javatestcase

1

E un'altra risposta per le persone potenzialmente utilizzano Oracle con un simile problema Unicode ...

controllare che qualcuno non ha impostato la proprietà oracle.jdbc.defaultNChar = true

Questo a volte è fatto per risolvere i problemi unicode ma significa che tutte le colonne sono trattate come nvarchars. Se si dispone di un indice su una colonna varchar, non verrà utilizzato perché oracle deve utilizzare una funzione per convertire la codifica dei caratteri.

Problemi correlati