2012-01-20 31 views
5

Có vấn đề hiệu suất lạ sử dụng Hibernate 3.3.2GA đằng sau JPA (và phần còn lại của gói Hibernate trong JBoss 5.)JPA (Hibernate) Native Query cho tuyên bố chuẩn bị CHẬM

Tôi đang sử dụng Native Query, và lắp ráp SQL vào một câu lệnh chuẩn bị.

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

SQL có nhiều lần tham gia, nhưng thực sự rất cơ bản, với một tham số duy nhất. Giống như:

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

và truy vấn chạy dưới một giây trên MSSQL Studio.

Nếu tôi thêm

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

Truy vấn sẽ tạm dừng trong 9 giây

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) 

Tuy nhiên, nếu tôi chỉ cần thay thế "?" với giá trị (làm cho nó không phải là một tuyên bố chuẩn bị, nhưng chỉ cần một truy vấn SQL thẳng.

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

truy vấn sẽ hoàn thành trong vòng chưa đầy một giây.

Tôi thực sự muốn cho chúng ta một tuyên bố chuẩn bị (các đầu vào cho các tham số đang được trích xuất từ ​​dữ liệu của người dùng) để ngăn chặn các cuộc tấn công bằng cách tiêm

Truy tìm điểm chậm trong mã, tôi đến sâu bên trong gói jtds-1.2.2. "getIn(). readFully (hdrBuf);" Không có gì thực sự rõ ràng ở đó mặc dù ...

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

Đến để qua đống này ...

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

Hãy để tôi thêm jtds-1.2.2 vào ngăn xếp công nghệ. Tôi đã gỡ lỗi thông qua Hibernate vào JTDS – javatestcase

+0

đã cố gắng jtds-1.2.4, nhưng không có niềm vui ... – javatestcase

+0

Chuyển sang com.microsoft.sqlserver.jdbc.SQLServerDriver thực sự tạo ra kết quả giống nhau ... Gotta hãy xem SQL Server, có thể một cái gì đó ở đó ... – javatestcase

Trả lời

8

Tôi sẽ để lại câu hỏi và câu trả lời này tại đây trong trường hợp bất kỳ ai có cùng vấn đề trong tương lai.

Vấn đề nằm trong cách trình điều khiển JTDS gửi chuỗi tham số đến MSSQL. Rõ ràng Java sẽ cố gắng gửi các tham số Unicode theo mặc định, và MSSQL sẽ dịch nó sang Ascii. Tại sao phải mất 9 giây, tôi không biết.

Rất nhiều tài liệu tham khảo về điều này, nhưng không có gì giúp tôi cho đến khi tôi có thể cô lập rằng đó là một vấn đề với trình điều khiển kết nối MSSQL.

Liên kết này là hữu ích:

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

Đây là chuỗi bằng cách sử dụng Microsoft người lái xe.

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

Bạn chỉ cần nhận được sendStringParametersAsUnicode = false được chuyển đến thiết lập URL trình điều khiển và bạn tốt.

+8

Mất 9 giây vì nó không dịch thông số của bạn thành ascii (vì nó có thể mất dữ liệu theo cách đó). Nó thay đổi mỗi giá trị cột thành unicode trước khi so sánh với tham số của bạn. Điều này có nghĩa là nó không thể tận dụng tối đa bất kỳ chỉ mục nào trên trường "stringId" dẫn đến hiệu suất chậm hơn nhiều (tôi tin rằng tôi cũng đã có một vấn đề như thế này trong quá khứ). – Gareth

+0

BTW, tôi rất vui vì bạn đã khắc phục được sự cố của mình. – Gareth

+0

cảm ơn. lời giải thích của bạn thực sự giúp hiểu vấn đề. – javatestcase

3

Kiểm tra truy vấn lên kế hoạch rằng SQL máy chủ được sản xuất. Các câu lệnh chuẩn bị có thể đặc biệt có vấn đề.

Hãy để tôi giải thích ...

Nếu bạn làm điều này:

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

và bạn có một chỉ mục trên "stringId" SQL server biết nó có thể sử dụng nó.

Tuy nhiên nếu bạn làm điều này:

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

SQL máy chủ không biết điều đó có thể sử dụng các chỉ số khi nó tạo ra những tuyên bố chuẩn bị (như bạn có thể điền vào các thông số với '% ABC123' thay vì ' ABC123% ') và do đó có thể chọn một kế hoạch truy vấn hoàn toàn khác.

+0

Cảm ơn Gareth. Những gì bạn nói là đúng 100%. Trong trường hợp này, vấn đề chính là với kết nối driver-MSSQL. Tôi nghĩ rằng MSSQL có thể cache các kế hoạch truy vấn, và thực tế chỉ có một vài lần lặp lại của SP, với các truy vấn phụ khác nhau cho các mối quan hệ một-nhiều. Sau khi khắc phục vấn đề đó, 100 truy vấn sẽ trả về sau 47 giây, một cải tiến lớn hơn 9 giây mỗi lần! – javatestcase

1

Và một câu trả lời cho những người có khả năng sử dụng Oracle với một vấn đề tương tự Unicode ...

Kiểm tra để chắc chắn rằng ai đó đã không thiết lập thuộc tính oracle.jdbc.defaultNChar = true

này đôi khi được thực hiện để giải quyết vấn đề unicode nhưng nó có nghĩa là tất cả các cột được coi là nvarchars. Nếu bạn có một chỉ mục trên một cột varchar, nó sẽ không được sử dụng bởi vì oracle phải sử dụng một hàm để chuyển đổi mã hóa ký tự.

Các vấn đề liên quan