2017-06-01 111 views
0

我遇到一個優化問題,我找不出爲什麼我的查詢太慢了。Spring JPA Hibernate:slow SELECT查詢

這裏我實體:

@Entity 
@Table(name = "CLIENT") 
public class Client { 

private static final long serialVersionUID = 1L; 
@Id 
@Column(name = "CLIENT_ID") 
@SequenceGenerator(name = "ID_GENERATOR", sequenceName = "CLIENT_S", allocationSize = 1, initialValue = 1) 
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID_GENERATOR") 
private Long id; 

@Column(name="LOGIN") 
private String login; 

@Column(name="PASSWORD") 
private String password; 

而且DAO

@NoRepositoryBean 
public interface ClientDao extends JpaRepository<Client, Long>, JpaSpecificationExecutor<Client> { 
    Client findByPasswordAndLogin(@Param("login") String customerLogin,@Param("password") String customerHashedPassword); 
} 

當執行方法findByPasswordAndLogin,大約需要200毫秒內完成(既通過JUnit測試,並與JProfiler的看到)。

這裏休眠查詢: 休眠:從CLIENT clientx0_其中clientx0_.PASSWORD =選擇clientx0_.CLIENT_ID如CLIENT_ID1_4_,clientx0_.LOGIN如LOGIN9_4_,clientx0_.PASSWORD如PASSWORD10_4_,clientx0_.STATUT如STATUT13_4_?和clientx0_.LOGIN =?

當我在數據庫中執行手動SQL查詢,只需要3毫秒:

select * from CLIENT where PASSWORD='xxxxx' and LOGIN='yyyyyyyy' 

我們有4000個客戶在我們的發展環境在。超過一百萬的生產。

這裏的背景:

  • JDK 8
  • 春4.1.6.RELEASE + JPA + Hibernate的
  • Oracle數據庫10

任何想法?

+0

什麼是'CustomAuditable '?創建一個'Client'實例是否昂貴? –

+0

您是否嘗試過測量應用程序端的事務執行時間? – galovics

+0

你能發佈由休眠產生的實際sql嗎?可能您有一些預先加載或其他後續查詢正在運行。 Hibernate統計信息也可以縮短實際的查詢執行時間。 –

回答

2

我已經測試了不同類型的DAO(這裏我就不公佈的代碼,因爲它是如此骯髒的):

  • 使用Hibernate:〜200ms的
  • 隨着(注入)Spring的JdbcTemplate和RowMapper的:〜70毫秒
  • 使用Java聲明:〜2毫秒
  • 使用Java OracleStatemen噸:〜5毫秒
  • 使用Java的PreparedStatement:〜100ms的
  • 使用Java的PreparedStatement調整與取大小= 5000:〜50ms的
  • 利用Java OraclePreparedStatement:〜100ms的
  • 隨着使用PreFetch大小調整Java OraclePreparedStatement = 5000:〜170ms

注:

  • DAO由Spring注入,而不是新ClientDao():+ 30毫秒丟失(-sick-)
  • 連接時DB:46ms

我可以使用:

  • 帶有手動清理字段的Java語句。在應用程序啓動
  • 預連接
  • 不要使用Spring注入

但是:

  • 沒有真正固定/安全
  • 快了少量的行,慢慢的地圖ResultSet到大量行的實體(我也有這個用例)

所以:

Spring的JdbcTemplate的RowMapper似乎是增加特定情況下表現最佳的解決方案。 而且我們可以保持SQL查詢的安全性。 但是需要編寫特定的RowMapper來將ResultSet轉換爲實體。Spring的JdbcTemplate

@Repository 
public class ClientJdbcTemplateDao { 


    private final Logger logger = LoggerFactory.getLogger(ClientJdbcTemplateDao.class); 

    private JdbcTemplate jdbcTemplate; 

    @Autowired 
    public void setDataSource(DataSource dataSource) { 
     this.jdbcTemplate = new JdbcTemplate(dataSource); 
    } 

    public List<Client> find() { 
     List<Client> c = this.jdbcTemplate.query("SELECT login FROM Client WHERE LOGIN='xxxx' AND PASSWORD='xxx'", new ClientRowMapper()); 
     return c; 
    } 
} 

客戶的RowMapper實例的

public class ClientRowMapper implements RowMapper<Client> { 

    @Override 
    public Client mapRow(ResultSet arg0, int arg1) throws SQLException { 
     // HERE IMPLEMENTS THE CONVERTER 
     // Sample : 
     // String login = arg0.getString("LOGIN") 
     // Client client = new Client(login); 
     // return client; 
    } 
} 

也許可以得到更好的,任何的建議是值得歡迎的。