我有一个自制的框架/ API,它会进行一些可能需要很长时间的数据库搜索。长时间意味着几秒到几分钟,具体取决于输入。搜索将结果放入java.util.concurrent.BlockingQueue中。因此,第一个搜索结果是immediatley可用。
我现在想要创建一个搜索页面,在提交搜索后,结果页面立即加载这些首次点击,然后定期添加后续点击直到搜索完成。我怎么能这样做?
答案 0 :(得分:0)
行。我想出了一个解决方案。
大警告: 未经测试用于生产用途!
使用的javascript依赖项:JQuery,jquery datatables插件(http://datatables.net)和用于数据表的fnStandingRedraw插件。
Java依赖:googles Gson库
我有一个提交给servlet的搜索页面。
servlet启动搜索并将搜索线程和BlockingQueue放入会话中,然后重定向到结果页面:
final LinkedBlockingQueue<Integer> hits =
new LinkedBlockingQueue<Integer>();
Thread searcher = new Thread() {
@Override
public void run() {
query.search(hits);
}
};
searcher.start();
session.setAttribute("searchQueue", hits);
session.setAttribute("searchThread", searcher);
response.sendRedirect("SearchResult.jsp");
请注意,Queue中填充了数据库中的主键,因此为整数。
结果页面需要以下参考:
<link href="css/datatables/demo_table.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="js/jquery-1.6.2.js"></script>
<script type="text/javascript" src="js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="js/datatables.fnStandingRedraw.js"></script>
并按照javascript代码初始化表并添加行:
<script type="text/javascript">
var resultsTable;
var myTimer;
$(document).ready(function() {
resultsTable = $('#result').dataTable( {
"bDeferRender": true,
"bProcessing": true,
"sPaginationType": "full_numbers",
"iDisplayLength": 4,
"aLengthMenu": [[4, -1], [4, "All"]],
"sScrollY": "580",
"aaSorting": [],
"aoColumns": [
/* column1 */ {"bSearchable": true,
"bSortable": true,
"sWidth": "50px"},
/* column2*/{"bSearchable": false,
"bSortable": false,
"sWidth": "510px"}
]
} );
getAndAddRows();
startTimer();
} );
function startTimer() {
myTimer = window.setInterval( function() {
getAndAddRows();
}, 500);
};
function stopTimer(){
window.clearInterval(myTimer);
}
function getAndAddRows(){
$.get(
'getSearchHits',
function(data){
if(data[0] == 'searchCompleted'){
stopTimer();
$('#hitsFound').text('Hits Found: '
+ resultsTable.fnSettings().fnRecordsTotal()
+ ' Search Completed');
return;
}
$('#result').dataTable().fnAddData(data, false);
resultsTable.fnStandingRedraw();
$('#hitsFound').text('Hits Found: '
+ resultsTable.fnSettings().fnRecordsTotal()
+ ' Searching...');
},
"json"
);
}
</script>
<h1 id="hitsFound">Hits Found:</h1>
<table id="result">
<thead>
<tr>
<th>column1</th>
<th>column2</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
请参阅数据表主页,了解如何设置此类表格。 基于计时器定期调用JQuery get-function。它调用另一个从队列中读取的servlet并返回格式为JSON的新命中:
int limit = 50; //amount of records ro return
int counter = 0;
BlockingQueue<Integer> hits = (BlockingQueue<Integer>) session.getAttribute("searchQueue");
Thread searcher = (Thread) session.getAttribute("searchThread");
Gson gson = new Gson();
ArrayList<ArrayList> rows = new ArrayList<ArrayList>();
while (hits.size() > 0 && limit > counter) {
ArrayList row = new ArrayList();
Integer key = hits.take();
// get desired data and add it to current row example:
String myData = dataccessObject.getMyData(key);
row.add(key);
row.add(myData);
rows.add(row);
counter++;
}
if (rows.size() < 1 && !searcher.isAlive()) {
out.print("[\"searchCompleted\"]");
} else {
String json = gson.toJson(rows); //==> json is [[1,"myData1"],[2,"myData2"]]
out.print(json);
}
可能需要根据您的目的调整限制和计时器。有限制,以便实际定期添加新结果。 (如果获取数据的速度低于找到新命中的速度,则在搜索完成之前不会返回任何内容并显示。)
请注意,字符串myData可以是HTML,并以HTML格式呈现。在我的情况下,我返回图像标签。这些图像标签有另一个servlet作为动态创建图像的源。请注意,datatables选项“bDeferRender”:true仅在第一次可见时才会呈现相应的行(在我的情况下为图像)。例如。如果用户只查看前3页,则4-100页实际上从未实现过渲染。如果用户点击最后一页,则只有最后一页被渲染,而不是全部。
如有任何问题或改进/可能的错误,请发表评论。