【问题标题】:Spring JPA Hibernate : slow SELECT querySpring JPA Hibernate:慢速 SELECT 查询
【发布时间】:2017-06-01 16:43:56
【问题描述】:

我遇到了一个优化问题,我不知道为什么我的查询这么慢。

这是我的实体:

@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 都可以看到)。

这里是休眠查询: 休眠:从客户端 clientx0_ 中选择 clientx0_.CLIENT_ID 作为 CLIENT_ID1_4_,clientx0_.LOGIN 作为 LOGIN9_4_,clientx0_.PASSWORD 作为 PASSWORD10_4_,clientx0_.STATUT 作为 STATUT13_4_,其中 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

有什么想法吗?

【问题讨论】:

  • 什么是CustomAuditable&lt;Long&gt;?创建Client 实例是否昂贵?
  • 你试过在应用端测量事务执行时间吗?
  • 能贴出hibernate生成的实际sql吗?可能您正在运行一些急切加载或其他后续查询。 Hibernate 统计信息还可以向您展示实际的查询执行时间。
  • 我已经用 Hibernate 查询更新了帖子并删除了 CustomAuditable(在这种情况下没用,我不想详细说明如何不使帖子超载 - 结果是一样的)。为什么我要在应用端采取措施?在 DAO 方面它很慢。
  • 能否为密码、登录组合添加索引。它可能会提高查询性能。

标签: java spring oracle hibernate jpa


【解决方案1】:

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

  • 使用休眠:~200ms
  • 使用(注入)Spring JDBCTemplate 和 RowMapper:~70 毫秒
  • 使用 Java 语句:~2 毫秒
  • 使用 Java OracleStatement:~5 毫秒
  • 使用 Java PreparedStatement:~100ms
  • 使用 Java PreparedStatement 调整 Fetch size = 5000:~50ms
  • 使用 Java OraclePreparedStatement:~100 毫秒
  • 使用 Java OraclePreparedStatement 调整 PreFetch 大小 = 5000 : ~170ms

注意事项:

  • 由 Spring 而不是 new ClientDao() 注入的 DAO:+30ms 丢失 (-sick-)
  • 与数据库的连接时间:46 毫秒

我可以使用:

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

但是:

  • 不是很安全/安全
  • 少量行速度快,将 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<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;
    }
}

也许可以更好,欢迎提出任何建议。

【讨论】:

    猜你喜欢
    • 2016-01-10
    • 1970-01-01
    • 2016-05-22
    • 1970-01-01
    • 1970-01-01
    • 2012-03-30
    • 1970-01-01
    • 2018-03-17
    • 1970-01-01
    相关资源
    最近更新 更多