Java EE 7批处理和魔兽世界–第2部分
今天,我將把第二部分帶到我以前關(guān)于Java EE 7批處理和《魔獸世界–第1部分》的帖子中。 在本文中,我們將了解如何從第1部分中獲得的數(shù)據(jù)中匯總和提取指標(biāo)。
概括
批處理目的是下載魔獸世界拍賣(mài)行的數(shù)據(jù),處理拍賣(mài)并提取指標(biāo)。 這些指標(biāo)將建立拍賣(mài)項(xiàng)目?jī)r(jià)格隨時(shí)間變化的歷史記錄。 在第1部分中 ,我們已經(jīng)下載了數(shù)據(jù)并將其插入數(shù)據(jù)庫(kù)。
應(yīng)用程序
處理作業(yè)
在將原始數(shù)據(jù)添加到數(shù)據(jù)庫(kù)之后,我們將添加一個(gè)帶有Chunk樣式處理的步驟。 在塊中,我們將讀取聚合的數(shù)據(jù),然后將其插入數(shù)據(jù)庫(kù)中的另一個(gè)表中以便于訪問(wèn)。 這是在process-job.xml :
process-job.xml
<step id="importStatistics"><chunk item-count="100"><reader ref="processedAuctionsReader"/><processor ref="processedAuctionsProcessor"/><writer ref="processedAuctionsWriter"/></chunk> </step>塊一次讀取一個(gè)數(shù)據(jù),并在事務(wù)內(nèi)創(chuàng)建要寫(xiě)出的塊。 從ItemReader讀入一項(xiàng),交給ItemProcessor并進(jìn)行聚合。 一旦讀取的項(xiàng)目數(shù)等于提交間隔,就通過(guò)ItemWriter寫(xiě)入整個(gè)塊,然后提交事務(wù)。
ProcessedAuctionsReader
在讀者中,我們將使用數(shù)據(jù)庫(kù)功能選擇和匯總指標(biāo)。
ProcessedAuctionsReader.java
@Named public class ProcessedAuctionsReader extends AbstractAuctionFileProcess implements ItemReader {@Resource(name = "java:comp/DefaultDataSource")protected DataSource dataSource;private PreparedStatement preparedStatement;private ResultSet resultSet;@Overridepublic void open(Serializable checkpoint) throws Exception {Connection connection = dataSource.getConnection();preparedStatement = connection.prepareStatement("SELECT" +" itemid as itemId," +" sum(quantity)," +" sum(bid)," +" sum(buyout)," +" min(bid / quantity)," +" min(buyout / quantity)," +" max(bid / quantity)," +" max(buyout / quantity)" +" FROM auction" +" WHERE auctionfile_id = " +getContext().getFileToProcess().getId() +" GROUP BY itemid" +" ORDER BY 1",ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY,ResultSet.HOLD_CURSORS_OVER_COMMIT);// Weird bug here. Check https://java.net/bugzilla/show_bug.cgi?id=5315//preparedStatement.setLong(1, getContext().getFileToProcess().getId());resultSet = preparedStatement.executeQuery();}@Overridepublic void close() throws Exception {DbUtils.closeQuietly(resultSet);DbUtils.closeQuietly(preparedStatement);}@Overridepublic Object readItem() throws Exception {return resultSet.next() ? resultSet : null;}@Overridepublic Serializable checkpointInfo() throws Exception {return null;}在此示例中,我們通過(guò)使用具有簡(jiǎn)單可滾動(dòng)結(jié)果集的純JDBC獲得最佳性能結(jié)果。 這樣,僅執(zhí)行一個(gè)查詢,并根據(jù)需要在readItem中提取結(jié)果。 您可能想探索其他替代方法。
Plain JPA在標(biāo)準(zhǔn)中沒(méi)有可滾動(dòng)的結(jié)果集,因此您需要對(duì)結(jié)果進(jìn)行分頁(yè)。 這將導(dǎo)致多個(gè)查詢,這將減慢閱讀速度。 另一個(gè)選擇是使用新的Java 8 Streams API來(lái)執(zhí)行聚合操作。 這些操作很快,但是您需要從數(shù)據(jù)庫(kù)中選擇整個(gè)數(shù)據(jù)集到流中。 最終,這會(huì)削弱您的性能。
我確實(shí)嘗試了這兩種方法,并通過(guò)使用數(shù)據(jù)庫(kù)聚合功能獲得了最佳結(jié)果。 我并不是說(shuō)這始終是最好的選擇,但是在這種情況下,這是最好的選擇。
在實(shí)施過(guò)程中,我還發(fā)現(xiàn)了Batch中的錯(cuò)誤。 您可以在這里檢查。 在PreparedStatement中設(shè)置參數(shù)時(shí)會(huì)引發(fā)異常。 解決方法是將參數(shù)直接注入查詢SQL中。 丑陋,我知道...
ProcessedAuctionsProcessor
在處理器中,讓我們將所有聚合值存儲(chǔ)在一個(gè)holder對(duì)象中,以存儲(chǔ)在數(shù)據(jù)庫(kù)中。
ProcessedAuctionsProcessor.java
@Named public class ProcessedAuctionsProcessor extends AbstractAuctionFileProcess implements ItemProcessor {@Override@SuppressWarnings("unchecked")public Object processItem(Object item) throws Exception {ResultSet resultSet = (ResultSet) item;AuctionItemStatistics auctionItemStatistics = new AuctionItemStatistics();auctionItemStatistics.setItemId(resultSet.getInt(1));auctionItemStatistics.setQuantity(resultSet.getLong(2));auctionItemStatistics.setBid(resultSet.getLong(3));auctionItemStatistics.setBuyout(resultSet.getLong(4));auctionItemStatistics.setMinBid(resultSet.getLong(5));auctionItemStatistics.setMinBuyout(resultSet.getLong(6));auctionItemStatistics.setMaxBid(resultSet.getLong(7));auctionItemStatistics.setMaxBuyout(resultSet.getLong(8));auctionItemStatistics.setTimestamp(getContext().getFileToProcess().getLastModified());auctionItemStatistics.setAvgBid((double) (auctionItemStatistics.getBid() / auctionItemStatistics.getQuantity()));auctionItemStatistics.setAvgBuyout((double) (auctionItemStatistics.getBuyout() / auctionItemStatistics.getQuantity()));auctionItemStatistics.setRealm(getContext().getRealm());return auctionItemStatistics;} }由于指標(biāo)會(huì)及時(shí)記錄數(shù)據(jù)的準(zhǔn)確快照,因此計(jì)算僅需執(zhí)行一次。 這就是為什么我們要保存匯總指標(biāo)。 它們永遠(yuǎn)不會(huì)改變,我們可以輕松地檢查歷史。
如果您知道源數(shù)據(jù)是不可變的,并且需要對(duì)其進(jìn)行操作,那么建議您將結(jié)果保留在某處。 這樣可以節(jié)省您的時(shí)間。 當(dāng)然,如果將來(lái)要多次訪問(wèn)此數(shù)據(jù),則需要平衡。 如果不是這樣,也許您就不需要經(jīng)歷持久化數(shù)據(jù)的麻煩了。
ProcessedAuctionsWriter
最后,我們只需要將數(shù)據(jù)寫(xiě)到數(shù)據(jù)庫(kù)中即可:
ProcessedAuctionsWriter.java
@Named public class ProcessedAuctionsWriter extends AbstractItemWriter {@PersistenceContextprotected EntityManager em;@Override@SuppressWarnings("unchecked")public void writeItems(List items) throws Exception {List<AuctionItemStatistics> statistis = (List<AuctionItemStatistics>) items;statistis.forEach(em::persist);} }指標(biāo)
現(xiàn)在,為了對(duì)數(shù)據(jù)做一些有用的事情,我們將公開(kāi)一個(gè)REST端點(diǎn),以對(duì)所計(jì)算的指標(biāo)執(zhí)行查詢。 方法如下:
WowBusinessBean.java
@Override @GET@Path("items")public List<AuctionItemStatistics> findAuctionItemStatisticsByRealmAndItem(@QueryParam("realmId") Long realmId,@QueryParam("itemId") Integer itemId) {Realm realm = (Realm) em.createNamedQuery("Realm.findRealmsWithConnectionsById").setParameter("id", realmId).getSingleResult();// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=433075 if using EclipseLinkList<Realm> connectedRealms = new ArrayList<>();connectedRealms.addAll(realm.getConnectedRealms());List<Long> ids = connectedRealms.stream().map(Realm::getId).collect(Collectors.toList());ids.add(realmId);return em.createNamedQuery("AuctionItemStatistics.findByRealmsAndItem").setParameter("realmIds", ids).setParameter("itemId", itemId).getResultList();}如果您還記得第1部分中的一些細(xì)節(jié),那么魔獸世界服務(wù)器稱為Realms 。 這些領(lǐng)域可以相互鏈接并共享同一拍賣(mài)行 。 為此,我們還擁有有關(guān)領(lǐng)域之間如何相互聯(lián)系的信息。 這很重要,因?yàn)槲覀兛梢栽谒羞B接的領(lǐng)域中搜索拍賣(mài)品 。 其余的邏輯只是簡(jiǎn)單的查詢以獲取數(shù)據(jù)。
在開(kāi)發(fā)過(guò)程中,我還發(fā)現(xiàn)了Eclipse Link (如果您在Glassfish中運(yùn)行)和Java 8的錯(cuò)誤。顯然, Eclipse Link返回的基礎(chǔ)Collection的元素計(jì)數(shù)設(shè)置為0。嘗試內(nèi)聯(lián)查詢調(diào)用以及Stream操作。 流將認(rèn)為它為空,并且不會(huì)返回任何結(jié)果。 您可以在這里有關(guān)此的內(nèi)容。
接口
我還使用Angular和Google Charts開(kāi)發(fā)了一個(gè)小界面來(lái)顯示指標(biāo)。 看一看:
在這里,我在尋找一個(gè)名為“Aggra(葡萄牙語(yǔ))”的境界與拍賣(mài)項(xiàng)目編號(hào)72092對(duì)應(yīng)于鬼鐵礦石 。 如您所見(jiàn),我們可以檢查待售數(shù)量,出價(jià)和買(mǎi)斷值以及價(jià)格隨時(shí)間的波動(dòng)。 整齊? 我可能會(huì)寫(xiě)另一篇關(guān)于將來(lái)構(gòu)建Web Interface的文章。
資源資源
您可以從我的github存儲(chǔ)庫(kù)中克隆完整的工作副本,然后將其部署到Wildfly或Glassfish中 。 您可以在那里找到部署說(shuō)明: 魔獸世界拍賣(mài)
也請(qǐng)檢查Java EE示例項(xiàng)目,其中包含大量完整的批處理示例。
翻譯自: https://www.javacodegeeks.com/2015/01/java-ee-7-batch-processing-and-world-of-warcraft-part-2.html
總結(jié)
以上是生活随笔為你收集整理的Java EE 7批处理和魔兽世界–第2部分的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 杭州的市树是什么树 杭州的市树是哪种树
- 下一篇: 太糟糕了,Java 8没有Iterabl