简体   繁体   English

Spring JPA Hibernate:缓慢的SELECT查询

[英]Spring JPA Hibernate : slow SELECT query

I encounter an optimisation problem and I can't figure out why my query is so slow. 我遇到优化问题,无法弄清楚为什么查询这么慢。

Here my entity : 这是我的实体:

@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;

And the DAO 还有DAO

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

When the method findByPasswordAndLogin is executed, it takes about 200ms to be completed (seen both through Junit tests and with JProfiler). 执行findByPasswordAndLogin方法时,大约需要200毫秒才能完成(通过Junit测试和JProfiler均可看到)。

Here the Hibernate query : Hibernate: select clientx0_.CLIENT_ID as CLIENT_ID1_4_, clientx0_.LOGIN as LOGIN9_4_, clientx0_.PASSWORD as PASSWORD10_4_, clientx0_.STATUT as STATUT13_4_ from CLIENT clientx0_ where clientx0_.PASSWORD=? 在这里,Hibernate查询:休眠:从CLIENT clientx0_中选择clientx0_.CLIENT_ID作为CLIENT_ID1_4_,clientx0_.LOGIN作为LOGIN9_4_,clientx0_.PASSWORD作为PASSWORD10_4_,clientx0_.STATUT作为STATUT13_4_,其中clientx0_.PASSWORD =? and clientx0_.LOGIN=? 和clientx0_.LOGIN =?

When I execute manually the SQL query on the database, it takes only 3ms : 当我在数据库上手动执行SQL查询时,只需要3毫秒:

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

We have 4000 clients in our development environnement. 在我们的开发环境中,我们有4000个客户。 More than a million in production. 年生产量超过一百万。

Here the context : 这里的上下文:

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

Any idea ? 任何想法 ?

I have tested different types of DAO (I don't publish code here because it is so dirty) : 我已经测试了不同类型的DAO(因为它太脏了,所以我不在这里发布代码):

  • With Hibernate : ~200ms 使用休眠模式 :〜200ms
  • With (Injected) Spring JDBCTemplate and RowMapper : ~70 ms 使用(注入的)Spring JDBCTemplate和RowMapper〜70毫秒
  • With Java Statement : ~2 ms 使用Java语句 :〜2毫秒
  • With Java OracleStatement : ~5 ms 使用Java OracleStatement〜5毫秒
  • With Java PreparedStatement : ~100ms 使用Java PreparedStatement :〜100ms
  • With Java PreparedStatement adjusted with Fetch size = 5000 : ~50ms 通过将Java PreparedStatement调整为Fetch大小= 5000 :〜50ms
  • With Java OraclePreparedStatement : ~100ms 使用Java OraclePreparedStatement〜100ms
  • With Java OraclePreparedStatement adjusted with PreFetch size = 5000 : ~170ms 使用Java OraclePreparedStatement并通过PreFetch大小 = 5000 调整 :〜170ms

Notes : 注意事项:

  • DAO injected by Spring instead of new ClientDao() : +30ms lost (-sick-) Spring注入的DAO而不是新的ClientDao() :+ 30ms丢失​​(-sick-)
  • Connection time to DB : 46ms 与DB的连接时间:46ms

I could use : 我可以使用:

  • Java Statement with manual sanitized fields. 带有手动清除字段的Java语句。
  • Pre-connection on application launch 启动应用程序时进行预连接
  • Do not use Spring Injection 不要使用弹簧注射

But : 但是:

  • Not really secured / safe 不是很安全
  • Fast for a small number of rows, slow to map ResultSet to entity on large number of rows (I also have this use case) 快速处理少量行,慢速将ResultSet映射到大量行上的实体(我也有这种用例)

So : 因此:

The Spring JDBCTemplate with RowMapper seems to be the best solution to increase performances on specific case. 带有RowMapperSpring JDBCTemplate似乎是提高特定情况下性能的最佳解决方案。 And we can keep a security on SQL queries. 而且我们可以保证SQL查询的安全性。 But need to write specific RowMapper to transform ResultSet to Entity. 但是需要编写特定的RowMapper才能将ResultSet转换为Entity。

Example of Spring JDBCTemplate 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;
    }
}

Example of Client RowMapper 客户端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;
    }
}

Maybe can be better, any suggestion is welcome. 也许可以更好,任何建议都是值得欢迎的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM