You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2020/07/02 08:01:57 UTC

[shardingsphere-elasticjob-lite] branch master updated: Use spring boot + JPA to deal with search dao (#884)

This is an automated email from the ASF dual-hosted git repository.

zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere-elasticjob-lite.git


The following commit(s) were added to refs/heads/master by this push:
     new 8cdd5d8  Use spring boot + JPA to deal with search dao (#884)
8cdd5d8 is described below

commit 8cdd5d887db65e888c37ffa96a8d8b8aeb2cc1e0
Author: viviel <37...@users.noreply.github.com>
AuthorDate: Thu Jul 2 16:01:51 2020 +0800

    Use spring boot + JPA to deal with search dao (#884)
---
 elastic-job-lite-console/pom.xml                   |   1 +
 .../controller/EventTraceHistoryController.java    |  14 +-
 .../lite/console/dao/search/JobExecutionLog.java   |  86 ++++++
 .../dao/search/JobExecutionLogRepository.java      |  25 ++
 .../lite/console/dao/search/JobStatusTraceLog.java |  86 ++++++
 .../dao/search/JobStatusTraceLogRepository.java    |  25 ++
 .../lite/console/dao/search/RDBJobEventSearch.java | 321 ++++++++-------------
 .../elasticjob/lite/console/util/BeanUtils.java    |  56 ++++
 .../console/dao/search/RDBJobEventSearchTest.java  | 113 +++-----
 .../search/RDBJobEventSearchTestConfiguration.java |  51 ++++
 10 files changed, 504 insertions(+), 274 deletions(-)

diff --git a/elastic-job-lite-console/pom.xml b/elastic-job-lite-console/pom.xml
index 1426b6a..4491c63 100644
--- a/elastic-job-lite-console/pom.xml
+++ b/elastic-job-lite-console/pom.xml
@@ -109,6 +109,7 @@
         <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
+            <scope>compile</scope>
         </dependency>
     </dependencies>
     
diff --git a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/controller/EventTraceHistoryController.java b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/controller/EventTraceHistoryController.java
index a1b3e7c..3e7a4ff 100644
--- a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/controller/EventTraceHistoryController.java
+++ b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/controller/EventTraceHistoryController.java
@@ -54,9 +54,13 @@ public final class EventTraceHistoryController {
     
     private EventTraceDataSourceConfigurationService eventTraceDataSourceConfigurationService;
     
+    private final RDBJobEventSearch rdbJobEventSearch;
+    
     @Autowired
-    public EventTraceHistoryController(final EventTraceDataSourceConfigurationService eventTraceDataSourceConfigurationService) {
+    public EventTraceHistoryController(final EventTraceDataSourceConfigurationService eventTraceDataSourceConfigurationService,
+                                       final RDBJobEventSearch rdbJobEventSearch) {
         this.eventTraceDataSourceConfigurationService = eventTraceDataSourceConfigurationService;
+        this.rdbJobEventSearch = rdbJobEventSearch;
     }
     
     /**
@@ -69,9 +73,9 @@ public final class EventTraceHistoryController {
     @GetMapping(value = "/execution", produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
     public Result<JobExecutionEvent> findJobExecutionEvents(@RequestParam final MultiValueMap<String, String> requestParams) throws ParseException {
         if (!eventTraceDataSourceConfigurationService.loadActivated().isPresent()) {
-            return new Result<>(0, new ArrayList<JobExecutionEvent>());
+            return new Result<>(0L, new ArrayList<JobExecutionEvent>());
         }
-        return new RDBJobEventSearch(setUpEventTraceDataSource()).findJobExecutionEvents(buildCondition(requestParams, new String[]{"jobName", "ip", "isSuccess"}));
+        return rdbJobEventSearch.findJobExecutionEvents(buildCondition(requestParams, new String[]{"jobName", "ip", "isSuccess"}));
     }
     
     /**
@@ -84,9 +88,9 @@ public final class EventTraceHistoryController {
     @GetMapping(value = "/status", produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
     public Result<JobStatusTraceEvent> findJobStatusTraceEvents(@RequestParam final MultiValueMap<String, String> requestParams) throws ParseException {
         if (!eventTraceDataSourceConfigurationService.loadActivated().isPresent()) {
-            return new Result<>(0, new ArrayList<JobStatusTraceEvent>());
+            return new Result<>(0L, new ArrayList<JobStatusTraceEvent>());
         }
-        return new RDBJobEventSearch(setUpEventTraceDataSource()).findJobStatusTraceEvents(buildCondition(requestParams, new String[]{"jobName", "source", "executionType", "state"}));
+        return rdbJobEventSearch.findJobStatusTraceEvents(buildCondition(requestParams, new String[]{"jobName", "source", "executionType", "state"}));
     }
     
     private DataSource setUpEventTraceDataSource() {
diff --git a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobExecutionLog.java b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobExecutionLog.java
new file mode 100644
index 0000000..5780d78
--- /dev/null
+++ b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobExecutionLog.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.lite.console.dao.search;
+
+import lombok.Data;
+import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobExecutionEvent;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.Date;
+
+@Data
+@Entity(name = "JOB_EXECUTION_LOG")
+public class JobExecutionLog {
+    
+    @Id
+    private String id;
+    
+    @Column(name = "job_name")
+    private String jobName;
+    
+    @Column(name = "task_id")
+    private String taskId;
+    
+    @Column(name = "hostname")
+    private String hostname;
+    
+    @Column(name = "ip")
+    private String ip;
+    
+    @Column(name = "sharding_item")
+    private Integer shardingItem;
+    
+    @Column(name = "execution_source")
+    private String executionSource;
+    
+    @Column(name = "failure_cause")
+    private String failureCause;
+    
+    @Column(name = "is_success")
+    private Boolean isSuccess;
+    
+    @Column(name = "start_time")
+    private Date startTime;
+    
+    @Column(name = "complete_time")
+    private Date completeTime;
+    
+    /**
+     * JobExecutionLog convert to JobExecutionEvent.
+     *
+     * @return JobExecutionEvent entity
+     */
+    public JobExecutionEvent toJobExecutionEvent() {
+        return new JobExecutionEvent(
+                id,
+                hostname,
+                ip,
+                taskId,
+                jobName,
+                JobExecutionEvent.ExecutionSource.valueOf(executionSource),
+                shardingItem,
+                startTime,
+                completeTime,
+                isSuccess,
+                failureCause
+        );
+    }
+    
+}
diff --git a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobExecutionLogRepository.java b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobExecutionLogRepository.java
new file mode 100644
index 0000000..c928d94
--- /dev/null
+++ b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobExecutionLogRepository.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.lite.console.dao.search;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface JobExecutionLogRepository
+        extends JpaRepository<JobExecutionLog, String>, JpaSpecificationExecutor<JobExecutionLog> {
+}
diff --git a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobStatusTraceLog.java b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobStatusTraceLog.java
new file mode 100644
index 0000000..79d522d
--- /dev/null
+++ b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobStatusTraceLog.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.lite.console.dao.search;
+
+import lombok.Data;
+import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobStatusTraceEvent;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.Date;
+
+@Data
+@Entity(name = "JOB_STATUS_TRACE_LOG")
+public class JobStatusTraceLog {
+    
+    @Id
+    private String id;
+    
+    @Column(name = "job_name")
+    private String jobName;
+    
+    @Column(name = "original_task_id")
+    private String originalTaskId;
+    
+    @Column(name = "task_id")
+    private String taskId;
+    
+    @Column(name = "slave_id")
+    private String slaveId;
+    
+    @Column(name = "source")
+    private String source;
+    
+    @Column(name = "execution_type")
+    private String executionType;
+    
+    @Column(name = "sharding_item")
+    private String shardingItem;
+    
+    @Column(name = "state")
+    private String state;
+    
+    @Column(name = "message")
+    private String message;
+    
+    @Column(name = "creation_time")
+    private Date creationTime;
+    
+    /**
+     * JobStatusTraceLog convert to JobStatusTraceEvent.
+     *
+     * @return JobStatusTraceEvent entity
+     */
+    public JobStatusTraceEvent toJobStatusTraceEvent() {
+        return new JobStatusTraceEvent(
+                id,
+                jobName,
+                originalTaskId,
+                taskId,
+                slaveId,
+                JobStatusTraceEvent.Source.valueOf(source),
+                executionType,
+                shardingItem,
+                JobStatusTraceEvent.State.valueOf(state),
+                message,
+                creationTime
+        );
+    }
+    
+}
diff --git a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobStatusTraceLogRepository.java b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobStatusTraceLogRepository.java
new file mode 100644
index 0000000..c6858e6
--- /dev/null
+++ b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/JobStatusTraceLogRepository.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.lite.console.dao.search;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface JobStatusTraceLogRepository
+        extends JpaRepository<JobStatusTraceLog, String>, JpaSpecificationExecutor<JobStatusTraceLog> {
+}
diff --git a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearch.java b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearch.java
index 77d06ca..c46082e 100644
--- a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearch.java
+++ b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearch.java
@@ -7,7 +7,7 @@
  * the License.  You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
- *  
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,252 +17,167 @@
 
 package org.apache.shardingsphere.elasticjob.lite.console.dao.search;
 
-import com.google.common.base.CaseFormat;
 import com.google.common.base.Strings;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shardingsphere.elasticjob.lite.console.util.BeanUtils;
 import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobExecutionEvent;
-import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobExecutionEvent.ExecutionSource;
 import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobStatusTraceEvent;
-import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobStatusTraceEvent.Source;
-import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobStatusTraceEvent.State;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Component;
 
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Timestamp;
+import javax.persistence.criteria.Predicate;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Date;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * RDB job event search.
  */
-@RequiredArgsConstructor
 @Slf4j
+@Component
 public final class RDBJobEventSearch {
     
-    private static final String TABLE_JOB_EXECUTION_LOG = "JOB_EXECUTION_LOG";
-    
-    private static final String TABLE_JOB_STATUS_TRACE_LOG = "JOB_STATUS_TRACE_LOG";
-    
-    private static final List<String> FIELDS_JOB_EXECUTION_LOG = Arrays.asList(
-            "id", "hostname", "ip", "task_id", "job_name", "execution_source", "sharding_item", "start_time", "complete_time", "is_success", "failure_cause");
+    private final JobExecutionLogRepository jobExecutionLogRepository;
     
-    private static final List<String> FIELDS_JOB_STATUS_TRACE_LOG = Arrays.asList(
-            "id", "job_name", "original_task_id", "task_id", "slave_id", "source", "execution_type", "sharding_item", "state", "message", "creation_time");
+    private final JobStatusTraceLogRepository jobStatusTraceLogRepository;
     
-    private final DataSource dataSource;
+    @Autowired
+    public RDBJobEventSearch(final JobExecutionLogRepository jobExecutionLogRepository,
+                             final JobStatusTraceLogRepository jobStatusTraceLogRepository) {
+        this.jobExecutionLogRepository = jobExecutionLogRepository;
+        this.jobStatusTraceLogRepository = jobStatusTraceLogRepository;
+    }
     
     /**
      * Find job execution events.
-     * 
+     *
      * @param condition query condition
      * @return job execution events
      */
     public Result<JobExecutionEvent> findJobExecutionEvents(final Condition condition) {
-        return new Result<>(getEventCount(TABLE_JOB_EXECUTION_LOG, FIELDS_JOB_EXECUTION_LOG, condition), getJobExecutionEvents(condition));
+        dealFields(condition.getFields());
+        Page<JobExecutionEvent> jobExecutionEvents = getJobExecutionEvents(condition);
+        return new Result<>(jobExecutionEvents.getTotalElements(), jobExecutionEvents.getContent());
+    }
+    
+    private void dealFields(final Map<String, Object> fields) {
+        if (Objects.isNull(fields)) {
+            return;
+        }
+        Object isSuccessField = fields.get("isSuccess");
+        if (!Objects.isNull(isSuccessField)) {
+            fields.put("isSuccess", Objects.equals(isSuccessField, "1"));
+        }
     }
     
     /**
      * Find job status trace events.
-     * 
+     *
      * @param condition query condition
      * @return job status trace events
      */
     public Result<JobStatusTraceEvent> findJobStatusTraceEvents(final Condition condition) {
-        return new Result<>(getEventCount(TABLE_JOB_STATUS_TRACE_LOG, FIELDS_JOB_STATUS_TRACE_LOG, condition), getJobStatusTraceEvents(condition));
-    }
-    
-    private List<JobExecutionEvent> getJobExecutionEvents(final Condition condition) {
-        List<JobExecutionEvent> result = new LinkedList<>();
-        try (
-                Connection connection = dataSource.getConnection();
-                PreparedStatement preparedStatement = createDataPreparedStatement(connection, TABLE_JOB_EXECUTION_LOG, FIELDS_JOB_EXECUTION_LOG, condition);
-                ResultSet resultSet = preparedStatement.executeQuery()
-                ) {
-            while (resultSet.next()) {
-                JobExecutionEvent jobExecutionEvent = new JobExecutionEvent(resultSet.getString(1), resultSet.getString(2), resultSet.getString(3), resultSet.getString(4),
-                        resultSet.getString(5), ExecutionSource.valueOf(resultSet.getString(6)), Integer.valueOf(resultSet.getString(7)), 
-                        new Date(resultSet.getTimestamp(8).getTime()), resultSet.getTimestamp(9) == null ? null : new Date(resultSet.getTimestamp(9).getTime()), 
-                        resultSet.getBoolean(10), resultSet.getString(11));
-                result.add(jobExecutionEvent);
-            }
-        } catch (final SQLException ex) {
-            // TODO log failure directly to output log, consider to be configurable in the future
-            log.error("Fetch JobExecutionEvent from DB error:", ex);
+        Page<JobStatusTraceEvent> jobStatusTraceEvents = getJobStatusTraceEvents(condition);
+        return new Result<>(jobStatusTraceEvents.getTotalElements(), jobStatusTraceEvents.getContent());
+    }
+    
+    private Page<JobExecutionEvent> getJobExecutionEvents(final Condition condition) {
+        Specification<JobExecutionLog> specification =
+                getSpecification(JobExecutionLog.class, condition, "startTime");
+        Page<JobExecutionLog> page =
+                jobExecutionLogRepository.findAll(specification, getPageable(condition, JobExecutionLog.class));
+        return new PageImpl<>(
+                page.get().map(JobExecutionLog::toJobExecutionEvent).collect(Collectors.toList()),
+                page.getPageable(),
+                page.getTotalElements()
+        );
+    }
+    
+    private <T> Pageable getPageable(final Condition condition, final Class<T> clazz) {
+        int page = 0;
+        int perPage = Condition.DEFAULT_PAGE_SIZE;
+        if (condition.getPage() > 0 && condition.getPerPage() > 0) {
+            page = condition.getPage() - 1;
+            perPage = condition.getPerPage();
         }
-        return result;
-    }
-    
-    private List<JobStatusTraceEvent> getJobStatusTraceEvents(final Condition condition) {
-        List<JobStatusTraceEvent> result = new LinkedList<>();
-        try (
-                Connection connection = dataSource.getConnection();
-                PreparedStatement preparedStatement = createDataPreparedStatement(connection, TABLE_JOB_STATUS_TRACE_LOG, FIELDS_JOB_STATUS_TRACE_LOG, condition);
-                ResultSet resultSet = preparedStatement.executeQuery()
-                ) {
-            while (resultSet.next()) {
-                JobStatusTraceEvent jobStatusTraceEvent = new JobStatusTraceEvent(resultSet.getString(1), resultSet.getString(2), resultSet.getString(3), resultSet.getString(4),
-                        resultSet.getString(5), Source.valueOf(resultSet.getString(6)), resultSet.getString(7), resultSet.getString(8),
-                        State.valueOf(resultSet.getString(9)), resultSet.getString(10), new Date(resultSet.getTimestamp(11).getTime()));
-                result.add(jobStatusTraceEvent);
-            }
-        } catch (final SQLException ex) {
-            // TODO log failure directly to output log, consider to be configurable in the future
-            log.error("Fetch JobStatusTraceEvent from DB error:", ex);
-        }
-        return result;
-    }
-    
-    private int getEventCount(final String tableName, final Collection<String> tableFields, final Condition condition) {
-        int result = 0;
-        try (
-                Connection connection = dataSource.getConnection();
-                PreparedStatement preparedStatement = createCountPreparedStatement(connection, tableName, tableFields, condition);
-                ResultSet resultSet = preparedStatement.executeQuery()
-                ) {
-            resultSet.next();
-            result = resultSet.getInt(1);
-        } catch (final SQLException ex) {
-            // TODO log failure directly to output log, consider to be configurable in the future
-            log.error("Fetch EventCount from DB error:", ex);
-        }
-        return result;
-    }
-    
-    private PreparedStatement createDataPreparedStatement(final Connection conn, final String tableName, final Collection<String> tableFields, final Condition condition) throws SQLException {
-        String sql = buildDataSql(tableName, tableFields, condition);
-        PreparedStatement preparedStatement = conn.prepareStatement(sql);
-        setBindValue(preparedStatement, tableFields, condition);
-        return preparedStatement;
-    }
-    
-    private PreparedStatement createCountPreparedStatement(final Connection conn, final String tableName, final Collection<String> tableFields, final Condition condition) throws SQLException {
-        String sql = buildCountSql(tableName, tableFields, condition);
-        PreparedStatement preparedStatement = conn.prepareStatement(sql);
-        setBindValue(preparedStatement, tableFields, condition);
-        return preparedStatement;
-    }
-    
-    private String buildDataSql(final String tableName, final Collection<String> tableFields, final Condition condition) {
-        StringBuilder sqlBuilder = new StringBuilder();
-        String selectSql = buildSelect(tableName, tableFields);
-        String whereSql = buildWhere(tableName, tableFields, condition);
-        String orderSql = buildOrder(tableFields, condition.getSort(), condition.getOrder());
-        String limitSql = buildLimit(condition.getPage(), condition.getPerPage());
-        sqlBuilder.append(selectSql).append(whereSql).append(orderSql).append(limitSql);
-        return sqlBuilder.toString();
-    }
-    
-    private String buildCountSql(final String tableName, final Collection<String> tableFields, final Condition condition) {
-        StringBuilder sqlBuilder = new StringBuilder();
-        String selectSql = buildSelectCount(tableName);
-        String whereSql = buildWhere(tableName, tableFields, condition);
-        sqlBuilder.append(selectSql).append(whereSql);
-        return sqlBuilder.toString();
+        return PageRequest.of(page, perPage, getSort(condition, clazz));
     }
     
-    private String buildSelectCount(final String tableName) {
-        return String.format("SELECT COUNT(1) FROM %s", tableName);
-    }
-    
-    private String buildSelect(final String tableName, final Collection<String> tableFields) {
-        StringBuilder sqlBuilder = new StringBuilder();
-        sqlBuilder.append("SELECT ");
-        for (String each : tableFields) {
-            sqlBuilder.append(each).append(",");
+    private <T> Sort getSort(final Condition condition, final Class<T> clazz) {
+        Sort sort = Sort.unsorted();
+        boolean sortFieldIsPresent = Arrays.stream(clazz.getDeclaredFields())
+                .map(Field::getName)
+                .anyMatch(e -> e.equals(condition.getSort()));
+        if (!sortFieldIsPresent) {
+            return sort;
         }
-        sqlBuilder.deleteCharAt(sqlBuilder.length() - 1);
-        sqlBuilder.append(" FROM ").append(tableName);
-        return sqlBuilder.toString();
-    }
-    
-    private String buildWhere(final String tableName, final Collection<String> tableFields, final Condition condition) {
-        StringBuilder sqlBuilder = new StringBuilder();
-        sqlBuilder.append(" WHERE 1=1");
-        if (null != condition.getFields() && !condition.getFields().isEmpty()) {
-            for (Map.Entry<String, Object> entry : condition.getFields().entrySet()) {
-                String lowerUnderscore = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, entry.getKey());
-                if (null != entry.getValue() && tableFields.contains(lowerUnderscore)) {
-                    sqlBuilder.append(" AND ").append(lowerUnderscore).append("=?");
-                }
+        if (!Strings.isNullOrEmpty(condition.getSort())) {
+            Sort.Direction order = Sort.Direction.ASC;
+            try {
+                order = Sort.Direction.valueOf(condition.getOrder());
+            } catch (IllegalArgumentException ignored) {
             }
+            sort = Sort.by(order, condition.getSort());
         }
-        if (null != condition.getStartTime()) {
-            sqlBuilder.append(" AND ").append(getTableTimeField(tableName)).append(">=?");
-        }
-        if (null != condition.getEndTime()) {
-            sqlBuilder.append(" AND ").append(getTableTimeField(tableName)).append("<=?");
-        }
-        return sqlBuilder.toString();
-    }
-    
-    private void setBindValue(final PreparedStatement preparedStatement, final Collection<String> tableFields, final Condition condition) throws SQLException {
-        int index = 1;
-        if (null != condition.getFields() && !condition.getFields().isEmpty()) {
-            for (Map.Entry<String, Object> entry : condition.getFields().entrySet()) {
-                String lowerUnderscore = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, entry.getKey());
-                if (null != entry.getValue() && tableFields.contains(lowerUnderscore)) {
-                    preparedStatement.setString(index++, String.valueOf(entry.getValue()));
-                }
+        return sort;
+    }
+    
+    private Page<JobStatusTraceEvent> getJobStatusTraceEvents(final Condition condition) {
+        Specification<JobStatusTraceLog> specification =
+                getSpecification(JobStatusTraceLog.class, condition, "creationTime");
+        Page<JobStatusTraceLog> page =
+                jobStatusTraceLogRepository.findAll(specification, getPageable(condition, JobStatusTraceLog.class));
+        return new PageImpl<>(
+                page.get().map(JobStatusTraceLog::toJobStatusTraceEvent).collect(Collectors.toList()),
+                page.getPageable(),
+                page.getTotalElements()
+        );
+    }
+    
+    private <T> Specification<T> getSpecification(final Class<T> clazz, final Condition condition, final String dateField) {
+        Example<T> example = getExample(condition.getFields(), clazz);
+        return getSpecWithExampleAndDate(
+                example, condition.getStartTime(), condition.getEndTime(), dateField
+        );
+    }
+    
+    private <T> Specification<T> getSpecWithExampleAndDate(
+            final Example<T> example, final Date from, final Date to, final String field
+    ) {
+        return (Specification<T>) (root, query, builder) -> {
+            final List<Predicate> predicates = new ArrayList<>();
+            if (from != null) {
+                predicates.add(builder.greaterThan(root.get(field), from));
             }
-        }
-        if (null != condition.getStartTime()) {
-            preparedStatement.setTimestamp(index++, new Timestamp(condition.getStartTime().getTime()));
-        }
-        if (null != condition.getEndTime()) {
-            preparedStatement.setTimestamp(index, new Timestamp(condition.getEndTime().getTime()));
-        }
-    }
-    
-    private String getTableTimeField(final String tableName) {
-        String result = "";
-        if (TABLE_JOB_EXECUTION_LOG.equals(tableName)) {
-            result = "start_time";
-        } else if (TABLE_JOB_STATUS_TRACE_LOG.equals(tableName)) {
-            result = "creation_time";
-        }
-        return result;
-    }
-    
-    private String buildOrder(final Collection<String> tableFields, final String sortName, final String sortOrder) {
-        if (Strings.isNullOrEmpty(sortName)) {
-            return "";
-        }
-        String lowerUnderscore = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, sortName);
-        if (!tableFields.contains(lowerUnderscore)) {
-            return "";
-        }
-        StringBuilder sqlBuilder = new StringBuilder();
-        sqlBuilder.append(" ORDER BY ").append(lowerUnderscore);
-        switch (sortOrder.toUpperCase()) {
-            case "ASC":
-                sqlBuilder.append(" ASC");
-                break;
-            case "DESC":
-                sqlBuilder.append(" DESC");
-                break;
-            default :
-                sqlBuilder.append(" ASC");
-        }
-        return sqlBuilder.toString();
+            if (to != null) {
+                predicates.add(builder.lessThan(root.get(field), to));
+            }
+            predicates.add(QueryByExamplePredicateBuilder.getPredicate(root, builder, example));
+            return builder.and(predicates.toArray(new Predicate[0]));
+        };
     }
     
-    private String buildLimit(final int page, final int perPage) {
-        StringBuilder sqlBuilder = new StringBuilder();
-        if (page > 0 && perPage > 0) {
-            sqlBuilder.append(" LIMIT ").append((page - 1) * perPage).append(",").append(perPage);
-        } else {
-            sqlBuilder.append(" LIMIT ").append(Condition.DEFAULT_PAGE_SIZE);
+    private <T> Example<T> getExample(final Map<String, Object> fields, final Class<T> clazz) {
+        T bean = BeanUtils.toBean(fields, clazz);
+        if (Objects.isNull(bean)) {
+            bean = BeanUtils.newInstance(clazz);
         }
-        return sqlBuilder.toString();
+        return Example.of(bean);
     }
     
     /**
@@ -293,7 +208,7 @@ public final class RDBJobEventSearch {
     @Getter
     public static class Result<T> {
         
-        private final Integer total;
+        private final Long total;
         
         private final List<T> rows;
     }
diff --git a/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/util/BeanUtils.java b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/util/BeanUtils.java
new file mode 100644
index 0000000..f8953fe
--- /dev/null
+++ b/elastic-job-lite-console/src/main/java/org/apache/shardingsphere/elasticjob/lite/console/util/BeanUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.lite.console.util;
+
+import org.springframework.cglib.beans.BeanMap;
+
+import java.util.Map;
+import java.util.Objects;
+
+public class BeanUtils extends org.springframework.beans.BeanUtils {
+    
+    /**
+     * return a new instance by specified java type.
+     *
+     * @param clazz java type class
+     * @param <T>   java type
+     * @return new instance
+     */
+    public static <T> T newInstance(final Class<T> clazz) {
+        return instantiateClass(clazz);
+    }
+    
+    /**
+     * map to java object.
+     *
+     * @param map  source map
+     * @param type class
+     * @param <T>  target java type
+     * @return java object
+     */
+    public static <T> T toBean(final Map<String, Object> map, final Class<T> type) {
+        if (Objects.isNull(map)) {
+            return null;
+        }
+        T bean = newInstance(type);
+        BeanMap beanMap = BeanMap.create(bean);
+        beanMap.putAll(map);
+        return bean;
+    }
+    
+}
diff --git a/elastic-job-lite-console/src/test/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearchTest.java b/elastic-job-lite-console/src/test/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearchTest.java
index 1e64510..113508f 100644
--- a/elastic-job-lite-console/src/test/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearchTest.java
+++ b/elastic-job-lite-console/src/test/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearchTest.java
@@ -17,18 +17,18 @@
 
 package org.apache.shardingsphere.elasticjob.lite.console.dao.search;
 
-import org.apache.commons.dbcp.BasicDataSource;
 import org.apache.shardingsphere.elasticjob.lite.console.dao.search.RDBJobEventSearch.Condition;
 import org.apache.shardingsphere.elasticjob.lite.console.dao.search.RDBJobEventSearch.Result;
 import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobExecutionEvent;
 import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobStatusTraceEvent;
-import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobStatusTraceEvent.Source;
-import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobStatusTraceEvent.State;
-import org.apache.shardingsphere.elasticjob.lite.tracing.rdb.storage.RDBJobEventStorage;
-import org.junit.BeforeClass;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringRunner;
 
-import java.sql.SQLException;
+import javax.sql.DataSource;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -36,68 +36,48 @@ import java.util.Map;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@Import(RDBJobEventSearchTestConfiguration.class)
 public final class RDBJobEventSearchTest {
     
-    private static RDBJobEventStorage storage;
+    @Autowired
+    private RDBJobEventSearch repository;
     
-    private static RDBJobEventSearch repository;
-    
-    @BeforeClass
-    public static void setUpClass() throws SQLException {
-        BasicDataSource dataSource = new BasicDataSource();
-        dataSource.setDriverClassName(org.h2.Driver.class.getName());
-        dataSource.setUrl("jdbc:h2:mem:");
-        dataSource.setUsername("sa");
-        dataSource.setPassword("");
-        storage = new RDBJobEventStorage(dataSource);
-        repository = new RDBJobEventSearch(dataSource);
-        initStorage();
-    }
-    
-    private static void initStorage() {
-        for (int i = 1; i <= 500; i++) {
-            JobExecutionEvent startEvent = new JobExecutionEvent("localhost", "127.0.0.1", "fake_task_id", "test_job_" + i, JobExecutionEvent.ExecutionSource.NORMAL_TRIGGER, 0);
-            storage.addJobExecutionEvent(startEvent);
-            if (i % 2 == 0) {
-                JobExecutionEvent successEvent = startEvent.executionSuccess();
-                storage.addJobExecutionEvent(successEvent);
-            }
-            storage.addJobStatusTraceEvent(new JobStatusTraceEvent(
-                    "test_job_" + i, "fake_failed_failover_task_id", "fake_slave_id", Source.LITE_EXECUTOR, "FAILOVER", "0", State.TASK_FAILED, "message is empty."));
-        }
-    }
+    @Autowired
+    private DataSource dataSource;
     
     @Test
     public void assertFindJobExecutionEventsWithPageSizeAndNumber() {
         Result<JobExecutionEvent> result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         result = repository.findJobExecutionEvents(new Condition(50, 1, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(50));
         result = repository.findJobExecutionEvents(new Condition(100, 5, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(100));
         result = repository.findJobExecutionEvents(new Condition(100, 6, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(0));
     }
     
     @Test
     public void assertFindJobExecutionEventsWithErrorPageSizeAndNumber() {
         Result<JobExecutionEvent> result = repository.findJobExecutionEvents(new Condition(-1, -1, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
     }
     
     @Test
     public void assertFindJobExecutionEventsWithSort() {
         Result<JobExecutionEvent> result = repository.findJobExecutionEvents(new Condition(10, 1, "jobName", "ASC", null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         assertThat(result.getRows().get(0).getJobName(), is("test_job_1"));
         result = repository.findJobExecutionEvents(new Condition(10, 1, "jobName", "DESC", null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         assertThat(result.getRows().get(0).getJobName(), is("test_job_99"));
     }
@@ -105,11 +85,11 @@ public final class RDBJobEventSearchTest {
     @Test
     public void assertFindJobExecutionEventsWithErrorSort() {
         Result<JobExecutionEvent> result = repository.findJobExecutionEvents(new Condition(10, 1, "jobName", "ERROR_SORT", null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         assertThat(result.getRows().get(0).getJobName(), is("test_job_1"));
         result = repository.findJobExecutionEvents(new Condition(10, 1, "notExistField", "ASC", null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
     }
     
@@ -118,19 +98,19 @@ public final class RDBJobEventSearchTest {
         Date now = new Date();
         Date tenMinutesBefore = new Date(now.getTime() - 10 * 60 * 1000);
         Result<JobExecutionEvent> result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, tenMinutesBefore, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, now, null, null));
-        assertThat(result.getTotal(), is(0));
+        assertThat(result.getTotal(), is(0L));
         assertThat(result.getRows().size(), is(0));
         result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, null, tenMinutesBefore, null));
-        assertThat(result.getTotal(), is(0));
+        assertThat(result.getTotal(), is(0L));
         assertThat(result.getRows().size(), is(0));
         result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, null, now, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, tenMinutesBefore, now, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
     }
     
@@ -139,12 +119,12 @@ public final class RDBJobEventSearchTest {
         Map<String, Object> fields = new HashMap<>();
         fields.put("isSuccess", "1");
         Result<JobExecutionEvent> result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, null, null, fields));
-        assertThat(result.getTotal(), is(250));
+        assertThat(result.getTotal(), is(250L));
         assertThat(result.getRows().size(), is(10));
         fields.put("isSuccess", null);
         fields.put("jobName", "test_job_1");
         result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, null, null, fields));
-        assertThat(result.getTotal(), is(1));
+        assertThat(result.getTotal(), is(1L));
         assertThat(result.getRows().size(), is(1));
     }
     
@@ -153,41 +133,41 @@ public final class RDBJobEventSearchTest {
         Map<String, Object> fields = new HashMap<>();
         fields.put("notExistField", "some value");
         Result<JobExecutionEvent> result = repository.findJobExecutionEvents(new Condition(10, 1, null, null, null, null, fields));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
     }
     
     @Test
     public void assertFindJobStatusTraceEventsWithPageSizeAndNumber() {
         Result<JobStatusTraceEvent> result = repository.findJobStatusTraceEvents(new Condition(10, 1, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         result = repository.findJobStatusTraceEvents(new Condition(50, 1, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(50));
         result = repository.findJobStatusTraceEvents(new Condition(100, 5, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(100));
         result = repository.findJobStatusTraceEvents(new Condition(100, 6, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(0));
     }
     
     @Test
     public void assertFindJobStatusTraceEventsWithErrorPageSizeAndNumber() {
         Result<JobStatusTraceEvent> result = repository.findJobStatusTraceEvents(new Condition(-1, -1, null, null, null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
     }
     
     @Test
     public void assertFindJobStatusTraceEventsWithSort() {
         Result<JobStatusTraceEvent> result = repository.findJobStatusTraceEvents(new Condition(10, 1, "jobName", "ASC", null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         assertThat(result.getRows().get(0).getJobName(), is("test_job_1"));
         result = repository.findJobStatusTraceEvents(new Condition(10, 1, "jobName", "DESC", null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         assertThat(result.getRows().get(0).getJobName(), is("test_job_99"));
     }
@@ -195,11 +175,11 @@ public final class RDBJobEventSearchTest {
     @Test
     public void assertFindJobStatusTraceEventsWithErrorSort() {
         Result<JobStatusTraceEvent> result = repository.findJobStatusTraceEvents(new Condition(10, 1, "jobName", "ERROR_SORT", null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         assertThat(result.getRows().get(0).getJobName(), is("test_job_1"));
         result = repository.findJobStatusTraceEvents(new Condition(10, 1, "notExistField", "ASC", null, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
     }
     
@@ -208,19 +188,19 @@ public final class RDBJobEventSearchTest {
         Date now = new Date();
         Date tenMinutesBefore = new Date(now.getTime() - 10 * 60 * 1000);
         Result<JobStatusTraceEvent> result = repository.findJobStatusTraceEvents(new Condition(10, 1, null, null, tenMinutesBefore, null, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         result = repository.findJobStatusTraceEvents(new Condition(10, 1, null, null, now, null, null));
-        assertThat(result.getTotal(), is(0));
+        assertThat(result.getTotal(), is(0L));
         assertThat(result.getRows().size(), is(0));
         result = repository.findJobStatusTraceEvents(new Condition(10, 1, null, null, null, tenMinutesBefore, null));
-        assertThat(result.getTotal(), is(0));
+        assertThat(result.getTotal(), is(0L));
         assertThat(result.getRows().size(), is(0));
         result = repository.findJobStatusTraceEvents(new Condition(10, 1, null, null, null, now, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
         result = repository.findJobStatusTraceEvents(new Condition(10, 1, null, null, tenMinutesBefore, now, null));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
     }
     
@@ -229,7 +209,7 @@ public final class RDBJobEventSearchTest {
         Map<String, Object> fields = new HashMap<>();
         fields.put("jobName", "test_job_1");
         Result<JobStatusTraceEvent> result = repository.findJobStatusTraceEvents(new Condition(10, 1, null, null, null, null, fields));
-        assertThat(result.getTotal(), is(1));
+        assertThat(result.getTotal(), is(1L));
         assertThat(result.getRows().size(), is(1));
     }
     
@@ -238,7 +218,8 @@ public final class RDBJobEventSearchTest {
         Map<String, Object> fields = new HashMap<>();
         fields.put("notExistField", "some value");
         Result<JobStatusTraceEvent> result = repository.findJobStatusTraceEvents(new Condition(10, 1, null, null, null, null, fields));
-        assertThat(result.getTotal(), is(500));
+        assertThat(result.getTotal(), is(500L));
         assertThat(result.getRows().size(), is(10));
     }
+
 }
diff --git a/elastic-job-lite-console/src/test/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearchTestConfiguration.java b/elastic-job-lite-console/src/test/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearchTestConfiguration.java
new file mode 100644
index 0000000..6b21c7f
--- /dev/null
+++ b/elastic-job-lite-console/src/test/java/org/apache/shardingsphere/elasticjob/lite/console/dao/search/RDBJobEventSearchTestConfiguration.java
@@ -0,0 +1,51 @@
+package org.apache.shardingsphere.elasticjob.lite.console.dao.search;
+
+import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobExecutionEvent;
+import org.apache.shardingsphere.elasticjob.lite.tracing.event.JobStatusTraceEvent;
+import org.apache.shardingsphere.elasticjob.lite.tracing.rdb.storage.RDBJobEventStorage;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.TestConfiguration;
+
+import javax.sql.DataSource;
+import java.sql.SQLException;
+
+@TestConfiguration
+public class RDBJobEventSearchTestConfiguration implements InitializingBean {
+    
+    @Autowired
+    private RDBJobEventSearch repository;
+    
+    @Autowired
+    private DataSource dataSource;
+    
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        initStorage();
+    }
+    
+    private void initStorage() throws SQLException {
+        repository.findJobExecutionEvents(new RDBJobEventSearch.Condition(10, 1, null, null, null, null, null));
+        repository.findJobStatusTraceEvents(new RDBJobEventSearch.Condition(10, 1, null, null, null, null, null));
+        RDBJobEventStorage storage = new RDBJobEventStorage(dataSource);
+        for (int i = 1; i <= 500L; i++) {
+            JobExecutionEvent startEvent = new JobExecutionEvent("localhost", "127.0.0.1", "fake_task_id", "test_job_" + i, JobExecutionEvent.ExecutionSource.NORMAL_TRIGGER, 0);
+            storage.addJobExecutionEvent(startEvent);
+            if (i % 2 == 0) {
+                JobExecutionEvent successEvent = startEvent.executionSuccess();
+                storage.addJobExecutionEvent(successEvent);
+            }
+            storage.addJobStatusTraceEvent(new JobStatusTraceEvent(
+                    "test_job_" + i,
+                    "fake_failed_failover_task_id",
+                    "fake_slave_id",
+                    JobStatusTraceEvent.Source.LITE_EXECUTOR,
+                    "FAILOVER",
+                    "0",
+                    JobStatusTraceEvent.State.TASK_FAILED,
+                    "message is empty."
+            ));
+        }
+    }
+    
+}