Spring JPA Hibernate:慢查询SELECT

时间:2017-06-01 16:43:56

标签: java spring oracle hibernate jpa

我遇到了一个优化问题,我无法弄清楚为什么我的查询太慢了。

这是我的实体:

@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时,完成大约需要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

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

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

  • 使用Hibernate :〜​​200ms
  • 使用(注入)Spring JDBCTemplate和RowMapper :~70 ms
  • 使用Java声明:~2 ms
  • 使用Java OracleStatement :~5 ms
  • 使用Java PreparedStatement :~100ms
  • 使用Fetch size = 5000 调整Java PreparedStatement:~50ms
  • 使用Java OraclePreparedStatement :~100ms
  • 使用PreFetch大小调整Java OraclePreparedStatement = 5000:~170ms

注意:

  • 由Spring注入的DAO代替 new ClientDao():+ 30ms丢失​​(-sick - )
  • 与DB的连接时间:46ms

我可以使用:

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

但是:

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

所以:

带有 RowMapper Spring 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<Client> find() {
        List<Client> 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<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;
    }
}

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