JPA (Hibernate) Собственный запрос для подготовленного оператора SLOW - PullRequest
6 голосов
/ 21 января 2012

Странная проблема с производительностью при использовании Hibernate 3.3.2GA за JPA (и остальные пакеты Hibernate включены в JBoss 5.)

Я использую Native Query и собираю SQL в подготовленный оператор.

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

SQL имеет много объединений, но на самом деле он очень простой, с одним параметром. Как:

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

и запрос выполняется менее чем за секунду в MSSQL Studio.

Если я добавлю

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

Запрос будет приостановлен на 9 секунд

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)

Однако, если я просто заменю "?" со значением (что делает его не подготовленным оператором, а простым запросом SQL.

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

запрос будет выполнен менее чем за секунду.

Я бы действительно предпочел нам подготовленное выражение (входные данные для параметров извлекаются из пользовательских данных) для предотвращения атак с использованием инъекций.

Отследив медленную точку в коде, я оказался глубоко внутри пакета jtds-1.2.2. Повреждающая строка выглядит как строка SharedSocket 841 "getIn (). ReadFully (hdrBuf);" Там нет ничего действительно очевидного ...

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.");
    }

Прибыл через этот стек ...

  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)

Ответы [ 3 ]

9 голосов
/ 21 января 2012

Я оставлю этот вопрос и отвечу здесь на случай, если у кого-то возникнет такая же проблема в будущем.

Проблема в том, что драйверы JTDS отправляют строки параметров в MSSQL. По-видимому, Java попытается отправить параметры Unicode по умолчанию, а MSSQL переведет его в Ascii. Почему это занимает 9 секунд, я не знаю.

Существует множество ссылок на это, но ничего, что помогло мне, пока я не смог изолировать, что это была проблема с драйвером для подключения MSSQL.

Эта ссылка была полезной:

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

Это строка, использующая драйвер Microsoft.

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

Вам просто нужно передать sendStringParametersAsUnicode = false в настройку URL вашего драйвера, и вы в порядке.

3 голосов
/ 21 января 2012

Проверьте планы запросов, которые создает сервер SQL. Подготовленные заявления могут быть особенно проблематичными.

Позвольте мне объяснить ...

Если вы сделаете это:

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

и у вас есть индекс на SQL-сервере "stringId", который знает, что может его использовать.

Однако, если вы сделаете это:

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

SQL-сервер не знает, что он может использовать индекс при создании подготовленного оператора (поскольку вы можете заполнить параметр с помощью «% ABC123» вместо «ABC123%») и, таким образом, может выбрать совершенно другой план запроса.

1 голос
/ 18 октября 2013

И еще один ответ для людей, потенциально использующих Oracle с аналогичной проблемой Unicode ...

Убедитесь, что кто-то не установил свойство oracle.jdbc.defaultNChar = true

Этоиногда делается для решения проблем с юникодом, но это означает, что все столбцы обрабатываются как nvarchars.Если у вас есть индекс для столбца varchar, он не будет использоваться, потому что oracle должен использовать функцию для преобразования кодировки символов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...