Spring JPA Hibernate:慢查询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, JpaSpecificationExecutor { Client findByPasswordAndLogin(@Param("login") String customerLogin,@Param("password") String customerHashedPassword); } 

当执行方法findByPasswordAndLogin时,完成大约需要200ms(通过Junit测试和JProfiler都可以看到)。

这里的Hibernate查询:Hibernate:选择clientx0_.CLIENT_ID作为CLIENT_ID1_4_,clientx0_.LOGIN作为LOGIN9_4_,clientx0_.PASSWORD作为PASSWORD10_4_,clientx0_.STATUT作为STATUT13_4_来自CLIENT clientx0_,其中clientx0_.PASSWORD =? 和clientx0_.LOGIN =?

当我在数据库上手动执行SQL查询时,只需3ms:

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

我们的开发环境中有4000个客户。 超过一百万的产量。

上下文:

  • JDK 8
  • Spring 4.1.6.RELEASE + JPA + Hibernate
  • Oracle数据库10

任何的想法 ?

我已经测试了不同类型的DAO(我不在这里发布代码,因为它太脏了):

  • 使用Hibernate :~200ms
  • 使用(Injected)Spring JDBCTemplate和RowMapper :~70 ms
  • 使用Java语句 :~2 ms
  • 使用Java OracleStatement :~5 ms
  • 使用Java PreparedStatement :~100ms
  • 使用Fetch size = 5000 :~50ms 调整Java PreparedStatement
  • 使用Java OraclePreparedStatement :~100ms
  • 使用Java OraclePreparedStatement调整PreFetch size = 5000:~170ms

备注:

  • 由Spring注入的DAO而不是新的ClientDao() :+ 30ms丢失​​(-sick-)
  • 到DB的连接时间:46ms

我可以用:

  • 带有手动清理字段的Java语句。
  • 应用程序启动时的预连接
  • 不要使用弹簧注射

但是:

  • 没有真正安全/安全
  • 对于少量行快速,在大量行上将ResultSet映射到实体的速度很慢(我也有这个用例)

所以:

带有RowMapperSpring JDBCTemplate似乎是提高特定情况下性能的最佳解决方案。 我们可以保持SQL查询的安全性。 但是需要编写特定的RowMapper来将ResultSet转换为Entity。

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

Client RowMapper的示例

 public class ClientRowMapper implements RowMapper { @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; } } 

也许可以更好,任何建议都是受欢迎的。